Compare commits

..

42 Commits

Author SHA1 Message Date
Sijie.Sun
25ed41caf5 use correct config server url (#519)
Some checks are pending
EasyTier Core / pre_job (push) Waiting to run
EasyTier Core / build (freebsd-13.2-x86_64, 13.2, ubuntu-22.04, x86_64-unknown-freebsd) (push) Blocked by required conditions
EasyTier Core / build (linux-aarch64, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-arm, ubuntu-22.04, arm-unknown-linux-musleabi) (push) Blocked by required conditions
EasyTier Core / build (linux-armhf, ubuntu-22.04, arm-unknown-linux-musleabihf) (push) Blocked by required conditions
EasyTier Core / build (linux-armv7, ubuntu-22.04, armv7-unknown-linux-musleabi) (push) Blocked by required conditions
EasyTier Core / build (linux-armv7hf, ubuntu-22.04, armv7-unknown-linux-musleabihf) (push) Blocked by required conditions
EasyTier Core / build (linux-mips, ubuntu-22.04, mips-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-mipsel, ubuntu-22.04, mipsel-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-x86_64, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (macos-aarch64, macos-latest, aarch64-apple-darwin) (push) Blocked by required conditions
EasyTier Core / build (macos-x86_64, macos-latest, x86_64-apple-darwin) (push) Blocked by required conditions
EasyTier Core / build (windows-arm64, windows-latest, aarch64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier Core / build (windows-x86_64, windows-latest, x86_64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier Core / core-result (push) Blocked by required conditions
EasyTier GUI / pre_job (push) Waiting to run
EasyTier GUI / build-gui (linux-aarch64, aarch64-unknown-linux-gnu, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier GUI / build-gui (linux-x86_64, x86_64-unknown-linux-gnu, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier GUI / build-gui (macos-aarch64, aarch64-apple-darwin, macos-latest, aarch64-apple-darwin) (push) Blocked by required conditions
EasyTier GUI / build-gui (macos-x86_64, x86_64-apple-darwin, macos-latest, x86_64-apple-darwin) (push) Blocked by required conditions
EasyTier GUI / build-gui (windows-arm64, aarch64-pc-windows-msvc, windows-latest, aarch64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier GUI / build-gui (windows-x86_64, x86_64-pc-windows-msvc, windows-latest, x86_64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier GUI / gui-result (push) Blocked by required conditions
EasyTier Mobile / pre_job (push) Waiting to run
EasyTier Mobile / build-mobile (android, ubuntu-22.04, android) (push) Blocked by required conditions
EasyTier Mobile / mobile-result (push) Blocked by required conditions
EasyTier Test / pre_job (push) Waiting to run
EasyTier Test / test (push) Blocked by required conditions
2024-12-20 00:21:22 +08:00
Sijie.Sun
4bb72b5606 fix rpc packet route before first route info exchange (#516)
Some checks are pending
EasyTier Core / pre_job (push) Waiting to run
EasyTier Core / build (freebsd-13.2-x86_64, 13.2, ubuntu-22.04, x86_64-unknown-freebsd) (push) Blocked by required conditions
EasyTier Core / build (linux-aarch64, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-arm, ubuntu-22.04, arm-unknown-linux-musleabi) (push) Blocked by required conditions
EasyTier Core / build (linux-armhf, ubuntu-22.04, arm-unknown-linux-musleabihf) (push) Blocked by required conditions
EasyTier Core / build (linux-armv7, ubuntu-22.04, armv7-unknown-linux-musleabi) (push) Blocked by required conditions
EasyTier Core / build (linux-armv7hf, ubuntu-22.04, armv7-unknown-linux-musleabihf) (push) Blocked by required conditions
EasyTier Core / build (linux-mips, ubuntu-22.04, mips-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-mipsel, ubuntu-22.04, mipsel-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-x86_64, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (macos-aarch64, macos-latest, aarch64-apple-darwin) (push) Blocked by required conditions
EasyTier Core / build (macos-x86_64, macos-latest, x86_64-apple-darwin) (push) Blocked by required conditions
EasyTier Core / build (windows-arm64, windows-latest, aarch64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier Core / build (windows-x86_64, windows-latest, x86_64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier Core / core-result (push) Blocked by required conditions
EasyTier GUI / pre_job (push) Waiting to run
EasyTier GUI / build-gui (linux-aarch64, aarch64-unknown-linux-gnu, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier GUI / build-gui (linux-x86_64, x86_64-unknown-linux-gnu, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier GUI / build-gui (macos-aarch64, aarch64-apple-darwin, macos-latest, aarch64-apple-darwin) (push) Blocked by required conditions
EasyTier GUI / build-gui (macos-x86_64, x86_64-apple-darwin, macos-latest, x86_64-apple-darwin) (push) Blocked by required conditions
EasyTier GUI / build-gui (windows-arm64, aarch64-pc-windows-msvc, windows-latest, aarch64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier GUI / build-gui (windows-x86_64, x86_64-pc-windows-msvc, windows-latest, x86_64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier GUI / gui-result (push) Blocked by required conditions
EasyTier Mobile / pre_job (push) Waiting to run
EasyTier Mobile / build-mobile (android, ubuntu-22.04, android) (push) Blocked by required conditions
EasyTier Mobile / mobile-result (push) Blocked by required conditions
EasyTier Test / pre_job (push) Waiting to run
EasyTier Test / test (push) Blocked by required conditions
* fix rpc packet route before first route info exchange
* fix install script
* update config server help
2024-12-19 09:25:25 +08:00
Sijie.Sun
c4d8ea4fec refactor sym to cone punch (#515)
Some checks are pending
EasyTier Core / pre_job (push) Waiting to run
EasyTier Core / build (freebsd-13.2-x86_64, 13.2, ubuntu-22.04, x86_64-unknown-freebsd) (push) Blocked by required conditions
EasyTier Core / build (linux-aarch64, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-arm, ubuntu-22.04, arm-unknown-linux-musleabi) (push) Blocked by required conditions
EasyTier Core / build (linux-armhf, ubuntu-22.04, arm-unknown-linux-musleabihf) (push) Blocked by required conditions
EasyTier Core / build (linux-armv7, ubuntu-22.04, armv7-unknown-linux-musleabi) (push) Blocked by required conditions
EasyTier Core / build (linux-armv7hf, ubuntu-22.04, armv7-unknown-linux-musleabihf) (push) Blocked by required conditions
EasyTier Core / build (linux-mips, ubuntu-22.04, mips-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-mipsel, ubuntu-22.04, mipsel-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-x86_64, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (macos-aarch64, macos-latest, aarch64-apple-darwin) (push) Blocked by required conditions
EasyTier Core / build (macos-x86_64, macos-latest, x86_64-apple-darwin) (push) Blocked by required conditions
EasyTier Core / build (windows-arm64, windows-latest, aarch64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier Core / build (windows-x86_64, windows-latest, x86_64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier Core / core-result (push) Blocked by required conditions
EasyTier GUI / pre_job (push) Waiting to run
EasyTier GUI / build-gui (linux-aarch64, aarch64-unknown-linux-gnu, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier GUI / build-gui (linux-x86_64, x86_64-unknown-linux-gnu, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier GUI / build-gui (macos-aarch64, aarch64-apple-darwin, macos-latest, aarch64-apple-darwin) (push) Blocked by required conditions
EasyTier GUI / build-gui (macos-x86_64, x86_64-apple-darwin, macos-latest, x86_64-apple-darwin) (push) Blocked by required conditions
EasyTier GUI / build-gui (windows-arm64, aarch64-pc-windows-msvc, windows-latest, aarch64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier GUI / build-gui (windows-x86_64, x86_64-pc-windows-msvc, windows-latest, x86_64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier GUI / gui-result (push) Blocked by required conditions
EasyTier Mobile / pre_job (push) Waiting to run
EasyTier Mobile / build-mobile (android, ubuntu-22.04, android) (push) Blocked by required conditions
EasyTier Mobile / mobile-result (push) Blocked by required conditions
EasyTier Test / pre_job (push) Waiting to run
EasyTier Test / test (push) Blocked by required conditions
2024-12-18 23:56:47 +08:00
Sophon
8588c9201a Add windows arm64 target to CI GUI (#487)
Some checks failed
EasyTier Core / pre_job (push) Has been cancelled
EasyTier GUI / pre_job (push) Has been cancelled
EasyTier Mobile / pre_job (push) Has been cancelled
EasyTier Test / pre_job (push) Has been cancelled
EasyTier Core / build (freebsd-13.2-x86_64, 13.2, ubuntu-22.04, x86_64-unknown-freebsd) (push) Has been cancelled
EasyTier Core / build (linux-aarch64, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Has been cancelled
EasyTier Core / build (linux-arm, ubuntu-22.04, arm-unknown-linux-musleabi) (push) Has been cancelled
EasyTier Core / build (linux-armhf, ubuntu-22.04, arm-unknown-linux-musleabihf) (push) Has been cancelled
EasyTier Core / build (linux-armv7, ubuntu-22.04, armv7-unknown-linux-musleabi) (push) Has been cancelled
EasyTier Core / build (linux-armv7hf, ubuntu-22.04, armv7-unknown-linux-musleabihf) (push) Has been cancelled
EasyTier Core / build (linux-mips, ubuntu-22.04, mips-unknown-linux-musl) (push) Has been cancelled
EasyTier Core / build (linux-mipsel, ubuntu-22.04, mipsel-unknown-linux-musl) (push) Has been cancelled
EasyTier Core / build (linux-x86_64, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Has been cancelled
EasyTier Core / build (macos-aarch64, macos-latest, aarch64-apple-darwin) (push) Has been cancelled
EasyTier Core / build (macos-x86_64, macos-latest, x86_64-apple-darwin) (push) Has been cancelled
EasyTier Core / build (windows-arm64, windows-latest, aarch64-pc-windows-msvc) (push) Has been cancelled
EasyTier Core / build (windows-x86_64, windows-latest, x86_64-pc-windows-msvc) (push) Has been cancelled
EasyTier Core / core-result (push) Has been cancelled
EasyTier GUI / build-gui (linux-aarch64, aarch64-unknown-linux-gnu, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Has been cancelled
EasyTier GUI / build-gui (linux-x86_64, x86_64-unknown-linux-gnu, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Has been cancelled
EasyTier GUI / build-gui (macos-aarch64, aarch64-apple-darwin, macos-latest, aarch64-apple-darwin) (push) Has been cancelled
EasyTier GUI / build-gui (macos-x86_64, x86_64-apple-darwin, macos-latest, x86_64-apple-darwin) (push) Has been cancelled
EasyTier GUI / build-gui (windows-arm64, aarch64-pc-windows-msvc, windows-latest, aarch64-pc-windows-msvc) (push) Has been cancelled
EasyTier GUI / build-gui (windows-x86_64, x86_64-pc-windows-msvc, windows-latest, x86_64-pc-windows-msvc) (push) Has been cancelled
EasyTier GUI / gui-result (push) Has been cancelled
EasyTier Mobile / build-mobile (android, ubuntu-22.04, android) (push) Has been cancelled
EasyTier Mobile / mobile-result (push) Has been cancelled
EasyTier Test / test (push) Has been cancelled
2024-11-21 23:14:09 +08:00
Sophon
dd2236c697 Add windows arm64 target to CI core (#486)
Some checks are pending
EasyTier Core / pre_job (push) Waiting to run
EasyTier Core / build (freebsd-13.2-x86_64, 13.2, ubuntu-22.04, x86_64-unknown-freebsd) (push) Blocked by required conditions
EasyTier Core / build (linux-aarch64, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-arm, ubuntu-22.04, arm-unknown-linux-musleabi) (push) Blocked by required conditions
EasyTier Core / build (linux-armhf, ubuntu-22.04, arm-unknown-linux-musleabihf) (push) Blocked by required conditions
EasyTier Core / build (linux-armv7, ubuntu-22.04, armv7-unknown-linux-musleabi) (push) Blocked by required conditions
EasyTier Core / build (linux-armv7hf, ubuntu-22.04, armv7-unknown-linux-musleabihf) (push) Blocked by required conditions
EasyTier Core / build (linux-mips, ubuntu-22.04, mips-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-mipsel, ubuntu-22.04, mipsel-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-x86_64, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (macos-aarch64, macos-latest, aarch64-apple-darwin) (push) Blocked by required conditions
EasyTier Core / build (macos-x86_64, macos-latest, x86_64-apple-darwin) (push) Blocked by required conditions
EasyTier Core / build (windows-arm64, windows-latest, aarch64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier Core / build (windows-x86_64, windows-latest, x86_64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier Core / core-result (push) Blocked by required conditions
EasyTier GUI / pre_job (push) Waiting to run
EasyTier GUI / build-gui (linux-aarch64, aarch64-unknown-linux-gnu, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier GUI / build-gui (linux-x86_64, x86_64-unknown-linux-gnu, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier GUI / build-gui (macos-aarch64, aarch64-apple-darwin, macos-latest, aarch64-apple-darwin) (push) Blocked by required conditions
EasyTier GUI / build-gui (macos-x86_64, x86_64-apple-darwin, macos-latest, x86_64-apple-darwin) (push) Blocked by required conditions
EasyTier GUI / build-gui (windows-x86_64, x86_64-pc-windows-msvc, windows-latest, x86_64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier GUI / gui-result (push) Blocked by required conditions
EasyTier Mobile / pre_job (push) Waiting to run
EasyTier Mobile / build-mobile (android, ubuntu-22.04, android) (push) Blocked by required conditions
EasyTier Mobile / mobile-result (push) Blocked by required conditions
EasyTier Test / pre_job (push) Waiting to run
EasyTier Test / test (push) Blocked by required conditions
2024-11-21 15:27:24 +08:00
Sophon
bc7c4d8cd0 Fix building on Windows ARM64 (#485)
Tested building natively on Windows ARM64, with Rust 1.82 and VS2022 toolchains.
Basic functionality of easytier-cli, easytier-core and easytier-web has been verified.
2024-11-21 10:32:03 +08:00
Sijie.Sun
aed54f7318 support respond stun request in udp tunnel (#484)
Some checks are pending
EasyTier Core / pre_job (push) Waiting to run
EasyTier Core / build (freebsd-13.2-x86_64, 13.2, ubuntu-22.04, x86_64-unknown-freebsd) (push) Blocked by required conditions
EasyTier Core / build (linux-aarch64, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-arm, ubuntu-22.04, arm-unknown-linux-musleabi) (push) Blocked by required conditions
EasyTier Core / build (linux-armhf, ubuntu-22.04, arm-unknown-linux-musleabihf) (push) Blocked by required conditions
EasyTier Core / build (linux-armv7, ubuntu-22.04, armv7-unknown-linux-musleabi) (push) Blocked by required conditions
EasyTier Core / build (linux-armv7hf, ubuntu-22.04, armv7-unknown-linux-musleabihf) (push) Blocked by required conditions
EasyTier Core / build (linux-mips, ubuntu-22.04, mips-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-mipsel, ubuntu-22.04, mipsel-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-x86_64, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (macos-aarch64, macos-latest, aarch64-apple-darwin) (push) Blocked by required conditions
EasyTier Core / build (macos-x86_64, macos-latest, x86_64-apple-darwin) (push) Blocked by required conditions
EasyTier Core / build (windows-x86_64, windows-latest, x86_64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier Core / core-result (push) Blocked by required conditions
EasyTier GUI / pre_job (push) Waiting to run
EasyTier GUI / build-gui (linux-aarch64, aarch64-unknown-linux-gnu, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier GUI / build-gui (linux-x86_64, x86_64-unknown-linux-gnu, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier GUI / build-gui (macos-aarch64, aarch64-apple-darwin, macos-latest, aarch64-apple-darwin) (push) Blocked by required conditions
EasyTier GUI / build-gui (macos-x86_64, x86_64-apple-darwin, macos-latest, x86_64-apple-darwin) (push) Blocked by required conditions
EasyTier GUI / build-gui (windows-x86_64, x86_64-pc-windows-msvc, windows-latest, x86_64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier GUI / gui-result (push) Blocked by required conditions
EasyTier Mobile / pre_job (push) Waiting to run
EasyTier Mobile / build-mobile (android, ubuntu-22.04, android) (push) Blocked by required conditions
EasyTier Mobile / mobile-result (push) Blocked by required conditions
EasyTier Test / pre_job (push) Waiting to run
EasyTier Test / test (push) Blocked by required conditions
we can use this to help the hole punching. (getting public mapped address stablely)
2024-11-20 23:45:06 +08:00
Sijie.Sun
86600c6315 version 2.1.0 (#482)
Some checks are pending
EasyTier Core / pre_job (push) Waiting to run
EasyTier Core / build (freebsd-13.2-x86_64, 13.2, ubuntu-22.04, x86_64-unknown-freebsd) (push) Blocked by required conditions
EasyTier Core / build (linux-aarch64, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-arm, ubuntu-22.04, arm-unknown-linux-musleabi) (push) Blocked by required conditions
EasyTier Core / build (linux-armhf, ubuntu-22.04, arm-unknown-linux-musleabihf) (push) Blocked by required conditions
EasyTier Core / build (linux-armv7, ubuntu-22.04, armv7-unknown-linux-musleabi) (push) Blocked by required conditions
EasyTier Core / build (linux-armv7hf, ubuntu-22.04, armv7-unknown-linux-musleabihf) (push) Blocked by required conditions
EasyTier Core / build (linux-mips, ubuntu-22.04, mips-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-mipsel, ubuntu-22.04, mipsel-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-x86_64, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (macos-aarch64, macos-latest, aarch64-apple-darwin) (push) Blocked by required conditions
EasyTier Core / build (macos-x86_64, macos-latest, x86_64-apple-darwin) (push) Blocked by required conditions
EasyTier Core / build (windows-x86_64, windows-latest, x86_64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier Core / core-result (push) Blocked by required conditions
EasyTier GUI / pre_job (push) Waiting to run
EasyTier GUI / build-gui (linux-aarch64, aarch64-unknown-linux-gnu, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier GUI / build-gui (linux-x86_64, x86_64-unknown-linux-gnu, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier GUI / build-gui (macos-aarch64, aarch64-apple-darwin, macos-latest, aarch64-apple-darwin) (push) Blocked by required conditions
EasyTier GUI / build-gui (macos-x86_64, x86_64-apple-darwin, macos-latest, x86_64-apple-darwin) (push) Blocked by required conditions
EasyTier GUI / build-gui (windows-x86_64, x86_64-pc-windows-msvc, windows-latest, x86_64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier GUI / gui-result (push) Blocked by required conditions
EasyTier Mobile / pre_job (push) Waiting to run
EasyTier Mobile / build-mobile (android, ubuntu-22.04, android) (push) Blocked by required conditions
EasyTier Mobile / mobile-result (push) Blocked by required conditions
EasyTier Test / pre_job (push) Waiting to run
EasyTier Test / test (push) Blocked by required conditions
2024-11-19 23:46:02 +08:00
Sijie.Sun
3f47f37470 fix high cpu usage when client proto mismatch (#481)
before this patch, invalid packat received by tunnel reader may cause a dead loop in handshake.
2024-11-19 21:36:09 +08:00
Sijie.Sun
1324e6163e move toast to bottom right (#478)
Some checks failed
EasyTier Core / pre_job (push) Has been cancelled
EasyTier GUI / pre_job (push) Has been cancelled
EasyTier Mobile / pre_job (push) Has been cancelled
EasyTier Test / pre_job (push) Has been cancelled
EasyTier Core / build (freebsd-13.2-x86_64, 13.2, ubuntu-22.04, x86_64-unknown-freebsd) (push) Has been cancelled
EasyTier Core / build (linux-aarch64, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Has been cancelled
EasyTier Core / build (linux-arm, ubuntu-22.04, arm-unknown-linux-musleabi) (push) Has been cancelled
EasyTier Core / build (linux-armhf, ubuntu-22.04, arm-unknown-linux-musleabihf) (push) Has been cancelled
EasyTier Core / build (linux-armv7, ubuntu-22.04, armv7-unknown-linux-musleabi) (push) Has been cancelled
EasyTier Core / build (linux-armv7hf, ubuntu-22.04, armv7-unknown-linux-musleabihf) (push) Has been cancelled
EasyTier Core / build (linux-mips, ubuntu-22.04, mips-unknown-linux-musl) (push) Has been cancelled
EasyTier Core / build (linux-mipsel, ubuntu-22.04, mipsel-unknown-linux-musl) (push) Has been cancelled
EasyTier Core / build (linux-x86_64, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Has been cancelled
EasyTier Core / build (macos-aarch64, macos-latest, aarch64-apple-darwin) (push) Has been cancelled
EasyTier Core / build (macos-x86_64, macos-latest, x86_64-apple-darwin) (push) Has been cancelled
EasyTier Core / build (windows-x86_64, windows-latest, x86_64-pc-windows-msvc) (push) Has been cancelled
EasyTier Core / core-result (push) Has been cancelled
EasyTier GUI / build-gui (linux-aarch64, aarch64-unknown-linux-gnu, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Has been cancelled
EasyTier GUI / build-gui (linux-x86_64, x86_64-unknown-linux-gnu, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Has been cancelled
EasyTier GUI / build-gui (macos-aarch64, aarch64-apple-darwin, macos-latest, aarch64-apple-darwin) (push) Has been cancelled
EasyTier GUI / build-gui (macos-x86_64, x86_64-apple-darwin, macos-latest, x86_64-apple-darwin) (push) Has been cancelled
EasyTier GUI / build-gui (windows-x86_64, x86_64-pc-windows-msvc, windows-latest, x86_64-pc-windows-msvc) (push) Has been cancelled
EasyTier GUI / gui-result (push) Has been cancelled
EasyTier Mobile / build-mobile (android, ubuntu-22.04, android) (push) Has been cancelled
EasyTier Mobile / mobile-result (push) Has been cancelled
EasyTier Test / test (push) Has been cancelled
2024-11-16 21:36:16 +08:00
Sijie.Sun
89093167c6 fix autocomplete not show dropdown (#477) 2024-11-16 21:31:19 +08:00
Sijie.Sun
15ad92aef2 fix no relay not work in local network (#476)
Some checks are pending
EasyTier Core / pre_job (push) Waiting to run
EasyTier Core / build (freebsd-13.2-x86_64, 13.2, ubuntu-22.04, x86_64-unknown-freebsd) (push) Blocked by required conditions
EasyTier Core / build (linux-aarch64, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-arm, ubuntu-22.04, arm-unknown-linux-musleabi) (push) Blocked by required conditions
EasyTier Core / build (linux-armhf, ubuntu-22.04, arm-unknown-linux-musleabihf) (push) Blocked by required conditions
EasyTier Core / build (linux-armv7, ubuntu-22.04, armv7-unknown-linux-musleabi) (push) Blocked by required conditions
EasyTier Core / build (linux-armv7hf, ubuntu-22.04, armv7-unknown-linux-musleabihf) (push) Blocked by required conditions
EasyTier Core / build (linux-mips, ubuntu-22.04, mips-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-mipsel, ubuntu-22.04, mipsel-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-x86_64, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (macos-aarch64, macos-latest, aarch64-apple-darwin) (push) Blocked by required conditions
EasyTier Core / build (macos-x86_64, macos-latest, x86_64-apple-darwin) (push) Blocked by required conditions
EasyTier Core / build (windows-x86_64, windows-latest, x86_64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier Core / core-result (push) Blocked by required conditions
EasyTier GUI / pre_job (push) Waiting to run
EasyTier GUI / build-gui (linux-aarch64, aarch64-unknown-linux-gnu, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier GUI / build-gui (linux-x86_64, x86_64-unknown-linux-gnu, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier GUI / build-gui (macos-aarch64, aarch64-apple-darwin, macos-latest, aarch64-apple-darwin) (push) Blocked by required conditions
EasyTier GUI / build-gui (macos-x86_64, x86_64-apple-darwin, macos-latest, x86_64-apple-darwin) (push) Blocked by required conditions
EasyTier GUI / build-gui (windows-x86_64, x86_64-pc-windows-msvc, windows-latest, x86_64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier GUI / gui-result (push) Blocked by required conditions
EasyTier Mobile / pre_job (push) Waiting to run
EasyTier Mobile / build-mobile (android, ubuntu-22.04, android) (push) Blocked by required conditions
EasyTier Mobile / mobile-result (push) Blocked by required conditions
EasyTier Test / pre_job (push) Waiting to run
EasyTier Test / test (push) Blocked by required conditions
2024-11-16 14:36:17 +08:00
Sijie.Sun
6cdea38284 support compress for rpc and tun data (#473)
* support compress for rpc and tun data
* add compression layer to easytier-web
2024-11-16 11:23:18 +08:00
咸鱼而已
9d455e22fa 支持服务管理 (#456)
* Support running as a Windows service.

* Optimize startup logic contro

* When running in a Windows service environment, delegate the termination of the program to the win_service_event_loop function.

* Remove the use of std::ffi::OsString at the top.

* Support  service manager

* Move the service-related features to be implemented in easytier-cli.

* Add a command line option work-dir to specify the working directory
Adjust the error handling logic
2024-11-16 09:01:58 +08:00
Sijie.Sun
4fc3ff8ce8 gui use frontend-lib, fix memory leak (#467)
Some checks failed
EasyTier Core / pre_job (push) Has been cancelled
EasyTier GUI / pre_job (push) Has been cancelled
EasyTier Mobile / pre_job (push) Has been cancelled
EasyTier Test / pre_job (push) Has been cancelled
EasyTier Core / build (freebsd-13.2-x86_64, 13.2, ubuntu-22.04, x86_64-unknown-freebsd) (push) Has been cancelled
EasyTier Core / build (linux-aarch64, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Has been cancelled
EasyTier Core / build (linux-arm, ubuntu-22.04, arm-unknown-linux-musleabi) (push) Has been cancelled
EasyTier Core / build (linux-armhf, ubuntu-22.04, arm-unknown-linux-musleabihf) (push) Has been cancelled
EasyTier Core / build (linux-armv7, ubuntu-22.04, armv7-unknown-linux-musleabi) (push) Has been cancelled
EasyTier Core / build (linux-armv7hf, ubuntu-22.04, armv7-unknown-linux-musleabihf) (push) Has been cancelled
EasyTier Core / build (linux-mips, ubuntu-22.04, mips-unknown-linux-musl) (push) Has been cancelled
EasyTier Core / build (linux-mipsel, ubuntu-22.04, mipsel-unknown-linux-musl) (push) Has been cancelled
EasyTier Core / build (linux-x86_64, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Has been cancelled
EasyTier Core / build (macos-aarch64, macos-latest, aarch64-apple-darwin) (push) Has been cancelled
EasyTier Core / build (macos-x86_64, macos-latest, x86_64-apple-darwin) (push) Has been cancelled
EasyTier Core / build (windows-x86_64, windows-latest, x86_64-pc-windows-msvc) (push) Has been cancelled
EasyTier Core / core-result (push) Has been cancelled
EasyTier GUI / build-gui (linux-aarch64, aarch64-unknown-linux-gnu, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Has been cancelled
EasyTier GUI / build-gui (linux-x86_64, x86_64-unknown-linux-gnu, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Has been cancelled
EasyTier GUI / build-gui (macos-aarch64, aarch64-apple-darwin, macos-latest, aarch64-apple-darwin) (push) Has been cancelled
EasyTier GUI / build-gui (macos-x86_64, x86_64-apple-darwin, macos-latest, x86_64-apple-darwin) (push) Has been cancelled
EasyTier GUI / build-gui (windows-x86_64, x86_64-pc-windows-msvc, windows-latest, x86_64-pc-windows-msvc) (push) Has been cancelled
EasyTier GUI / gui-result (push) Has been cancelled
EasyTier Mobile / build-mobile (android, ubuntu-22.04, android) (push) Has been cancelled
EasyTier Mobile / mobile-result (push) Has been cancelled
EasyTier Test / test (push) Has been cancelled
2024-11-10 23:03:40 +08:00
Sijie.Sun
88e6de9d7e make all frontend functions works (#466)
Some checks are pending
EasyTier Core / pre_job (push) Waiting to run
EasyTier Core / build (freebsd-13.2-x86_64, 13.2, ubuntu-22.04, x86_64-unknown-freebsd) (push) Blocked by required conditions
EasyTier Core / build (linux-aarch64, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-arm, ubuntu-22.04, arm-unknown-linux-musleabi) (push) Blocked by required conditions
EasyTier Core / build (linux-armhf, ubuntu-22.04, arm-unknown-linux-musleabihf) (push) Blocked by required conditions
EasyTier Core / build (linux-armv7, ubuntu-22.04, armv7-unknown-linux-musleabi) (push) Blocked by required conditions
EasyTier Core / build (linux-armv7hf, ubuntu-22.04, armv7-unknown-linux-musleabihf) (push) Blocked by required conditions
EasyTier Core / build (linux-mips, ubuntu-22.04, mips-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-mipsel, ubuntu-22.04, mipsel-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-x86_64, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (macos-aarch64, macos-latest, aarch64-apple-darwin) (push) Blocked by required conditions
EasyTier Core / build (macos-x86_64, macos-latest, x86_64-apple-darwin) (push) Blocked by required conditions
EasyTier Core / build (windows-x86_64, windows-latest, x86_64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier Core / core-result (push) Blocked by required conditions
EasyTier GUI / pre_job (push) Waiting to run
EasyTier GUI / build-gui (linux-aarch64, aarch64-unknown-linux-gnu, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier GUI / build-gui (linux-x86_64, x86_64-unknown-linux-gnu, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier GUI / build-gui (macos-aarch64, aarch64-apple-darwin, macos-latest, aarch64-apple-darwin) (push) Blocked by required conditions
EasyTier GUI / build-gui (macos-x86_64, x86_64-apple-darwin, macos-latest, x86_64-apple-darwin) (push) Blocked by required conditions
EasyTier GUI / build-gui (windows-x86_64, x86_64-pc-windows-msvc, windows-latest, x86_64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier GUI / gui-result (push) Blocked by required conditions
EasyTier Mobile / pre_job (push) Waiting to run
EasyTier Mobile / build-mobile (android, ubuntu-22.04, android) (push) Blocked by required conditions
EasyTier Mobile / mobile-result (push) Blocked by required conditions
EasyTier Test / pre_job (push) Waiting to run
EasyTier Test / test (push) Blocked by required conditions
2024-11-10 11:06:58 +08:00
Sijie.Sun
e948dbfcc1 Feat/web (Patchset 4) (#460)
Some checks failed
EasyTier Core / pre_job (push) Has been cancelled
EasyTier GUI / pre_job (push) Has been cancelled
EasyTier Mobile / pre_job (push) Has been cancelled
EasyTier Test / pre_job (push) Has been cancelled
EasyTier Core / build (freebsd-13.2-x86_64, 13.2, ubuntu-22.04, x86_64-unknown-freebsd) (push) Has been cancelled
EasyTier Core / build (linux-aarch64, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Has been cancelled
EasyTier Core / build (linux-arm, ubuntu-22.04, arm-unknown-linux-musleabi) (push) Has been cancelled
EasyTier Core / build (linux-armhf, ubuntu-22.04, arm-unknown-linux-musleabihf) (push) Has been cancelled
EasyTier Core / build (linux-armv7, ubuntu-22.04, armv7-unknown-linux-musleabi) (push) Has been cancelled
EasyTier Core / build (linux-armv7hf, ubuntu-22.04, armv7-unknown-linux-musleabihf) (push) Has been cancelled
EasyTier Core / build (linux-mips, ubuntu-22.04, mips-unknown-linux-musl) (push) Has been cancelled
EasyTier Core / build (linux-mipsel, ubuntu-22.04, mipsel-unknown-linux-musl) (push) Has been cancelled
EasyTier Core / build (linux-x86_64, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Has been cancelled
EasyTier Core / build (macos-aarch64, macos-latest, aarch64-apple-darwin) (push) Has been cancelled
EasyTier Core / build (macos-x86_64, macos-latest, x86_64-apple-darwin) (push) Has been cancelled
EasyTier Core / build (windows-x86_64, windows-latest, x86_64-pc-windows-msvc) (push) Has been cancelled
EasyTier Core / core-result (push) Has been cancelled
EasyTier GUI / build-gui (linux-aarch64, aarch64-unknown-linux-gnu, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Has been cancelled
EasyTier GUI / build-gui (linux-x86_64, x86_64-unknown-linux-gnu, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Has been cancelled
EasyTier GUI / build-gui (macos-aarch64, aarch64-apple-darwin, macos-latest, aarch64-apple-darwin) (push) Has been cancelled
EasyTier GUI / build-gui (macos-x86_64, x86_64-apple-darwin, macos-latest, x86_64-apple-darwin) (push) Has been cancelled
EasyTier GUI / build-gui (windows-x86_64, x86_64-pc-windows-msvc, windows-latest, x86_64-pc-windows-msvc) (push) Has been cancelled
EasyTier GUI / gui-result (push) Has been cancelled
EasyTier Mobile / build-mobile (android, ubuntu-22.04, android) (push) Has been cancelled
EasyTier Mobile / mobile-result (push) Has been cancelled
EasyTier Test / test (push) Has been cancelled
support basic functions in frontend
1. create/del network
2. inspect network running status
2024-11-08 23:33:17 +08:00
Sijie.Sun
8aca5851f2 feat/web: Patchset 3 (#455)
Some checks failed
EasyTier Core / pre_job (push) Has been cancelled
EasyTier GUI / pre_job (push) Has been cancelled
EasyTier Mobile / pre_job (push) Has been cancelled
EasyTier Test / pre_job (push) Has been cancelled
EasyTier Core / build (freebsd-13.2-x86_64, 13.2, ubuntu-22.04, x86_64-unknown-freebsd) (push) Has been cancelled
EasyTier Core / build (linux-aarch64, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Has been cancelled
EasyTier Core / build (linux-arm, ubuntu-22.04, arm-unknown-linux-musleabi) (push) Has been cancelled
EasyTier Core / build (linux-armhf, ubuntu-22.04, arm-unknown-linux-musleabihf) (push) Has been cancelled
EasyTier Core / build (linux-armv7, ubuntu-22.04, armv7-unknown-linux-musleabi) (push) Has been cancelled
EasyTier Core / build (linux-armv7hf, ubuntu-22.04, armv7-unknown-linux-musleabihf) (push) Has been cancelled
EasyTier Core / build (linux-mips, ubuntu-22.04, mips-unknown-linux-musl) (push) Has been cancelled
EasyTier Core / build (linux-mipsel, ubuntu-22.04, mipsel-unknown-linux-musl) (push) Has been cancelled
EasyTier Core / build (linux-x86_64, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Has been cancelled
EasyTier Core / build (macos-aarch64, macos-latest, aarch64-apple-darwin) (push) Has been cancelled
EasyTier Core / build (macos-x86_64, macos-latest, x86_64-apple-darwin) (push) Has been cancelled
EasyTier Core / build (windows-x86_64, windows-latest, x86_64-pc-windows-msvc) (push) Has been cancelled
EasyTier Core / core-result (push) Has been cancelled
EasyTier GUI / build-gui (linux-aarch64, aarch64-unknown-linux-gnu, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Has been cancelled
EasyTier GUI / build-gui (linux-x86_64, x86_64-unknown-linux-gnu, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Has been cancelled
EasyTier GUI / build-gui (macos-aarch64, aarch64-apple-darwin, macos-latest, aarch64-apple-darwin) (push) Has been cancelled
EasyTier GUI / build-gui (macos-x86_64, x86_64-apple-darwin, macos-latest, x86_64-apple-darwin) (push) Has been cancelled
EasyTier GUI / build-gui (windows-x86_64, x86_64-pc-windows-msvc, windows-latest, x86_64-pc-windows-msvc) (push) Has been cancelled
EasyTier GUI / gui-result (push) Has been cancelled
EasyTier Mobile / build-mobile (android, ubuntu-22.04, android) (push) Has been cancelled
EasyTier Mobile / mobile-result (push) Has been cancelled
EasyTier Test / test (push) Has been cancelled
https://apifox.com/apidoc/shared-ceda7a60-e817-4ea8-827b-de4e874dc45e

implement all backend API
2024-11-02 15:13:19 +08:00
Jiangqiu Shen
18da94bf33 make the panic message more useful (#437)
Some checks failed
EasyTier Core / pre_job (push) Has been cancelled
EasyTier GUI / pre_job (push) Has been cancelled
EasyTier Mobile / pre_job (push) Has been cancelled
EasyTier Test / pre_job (push) Has been cancelled
EasyTier Core / build (freebsd-13.2-x86_64, 13.2, ubuntu-22.04, x86_64-unknown-freebsd) (push) Has been cancelled
EasyTier Core / build (linux-aarch64, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Has been cancelled
EasyTier Core / build (linux-arm, ubuntu-22.04, arm-unknown-linux-musleabi) (push) Has been cancelled
EasyTier Core / build (linux-armhf, ubuntu-22.04, arm-unknown-linux-musleabihf) (push) Has been cancelled
EasyTier Core / build (linux-armv7, ubuntu-22.04, armv7-unknown-linux-musleabi) (push) Has been cancelled
EasyTier Core / build (linux-armv7hf, ubuntu-22.04, armv7-unknown-linux-musleabihf) (push) Has been cancelled
EasyTier Core / build (linux-mips, ubuntu-22.04, mips-unknown-linux-musl) (push) Has been cancelled
EasyTier Core / build (linux-mipsel, ubuntu-22.04, mipsel-unknown-linux-musl) (push) Has been cancelled
EasyTier Core / build (linux-x86_64, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Has been cancelled
EasyTier Core / build (macos-aarch64, macos-latest, aarch64-apple-darwin) (push) Has been cancelled
EasyTier Core / build (macos-x86_64, macos-latest, x86_64-apple-darwin) (push) Has been cancelled
EasyTier Core / build (windows-x86_64, windows-latest, x86_64-pc-windows-msvc) (push) Has been cancelled
EasyTier Core / core-result (push) Has been cancelled
EasyTier GUI / build-gui (linux-aarch64, aarch64-unknown-linux-gnu, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Has been cancelled
EasyTier GUI / build-gui (linux-x86_64, x86_64-unknown-linux-gnu, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Has been cancelled
EasyTier GUI / build-gui (macos-aarch64, aarch64-apple-darwin, macos-latest, aarch64-apple-darwin) (push) Has been cancelled
EasyTier GUI / build-gui (macos-x86_64, x86_64-apple-darwin, macos-latest, x86_64-apple-darwin) (push) Has been cancelled
EasyTier GUI / build-gui (windows-x86_64, x86_64-pc-windows-msvc, windows-latest, x86_64-pc-windows-msvc) (push) Has been cancelled
EasyTier GUI / gui-result (push) Has been cancelled
EasyTier Mobile / build-mobile (android, ubuntu-22.04, android) (push) Has been cancelled
EasyTier Mobile / mobile-result (push) Has been cancelled
EasyTier Test / test (push) Has been cancelled
when panic happend, previouse panic info:
panic occurred: PanicHookInfo { payload: Any { .. }, location: Location { file: "easytier/src/easytier-core.rs", line: 680, col: 9 }, can_unwind: true, force_no_backtrace: false }

the new panic info:

panic occurred: payload:launcher error: "anyhow error: failed to add listener tcp://0.0.0.0:11010", location: Some(Location { file: "easytier/src/easytier-core.rs", line: 680, col: 9 })
backtrace saved to easytier-panic.log
2024-10-26 01:33:04 +08:00
咸鱼而已
1ac2e1c8e3 Support running as a Windows service. (#445)
When the program is started by the Service Control Manager (SCM), it needs to call StartServiceCtrlDispatcher (which corresponds to service_dispatcher::start in the windows-service crate) to inform the SCM of the service's entry function. The SCM then calls the service entry function passed to StartServiceCtrlDispatcher. The process calling StartServiceCtrlDispatcher will block until the service's status is set to Stopped. If the current program is not run through the SCM, StartServiceCtrlDispatcher will return the error ERROR_FAILED_SERVICE_CONTROLLER_CONNECT, and the program will run according to its original mechanism. For more details about SCM, please refer to Microsoft's documentation.
2024-10-26 01:32:25 +08:00
Sijie.Sun
a78b759741 feat/web (Patchset 2) (#444)
This patch implement a restful server without any auth.

usage:

```bash
# run easytier-web, which acts as an gateway and registry for all easytier-core
$> easytier-web

# run easytier-core and connect to easytier-web with a token
$> easytier-core --config-server udp://127.0.0.1:22020/fdsafdsa

# use restful api to list session
$> curl -H "Content-Type: application/json" -X GET 127.0.0.1:11211/api/v1/sessions
[{"token":"fdsafdsa","client_url":"udp://127.0.0.1:48915","machine_id":"de3f5b8f-0f2f-d9d0-fb30-a2ac8951d92f"}]%

# use restful api to run a network instance
$> curl -H "Content-Type: application/json" -X POST 127.0.0.1:11211/api/v1/network/de3f5b8f-0f2f-d9d0-fb30-a2ac8951d92f -d '{"config": "listeners = [\"udp://0.0.0.0:12344\"]"}'

# use restful api to get network instance info
$> curl -H "Content-Type: application/json" -X GET 127.0.0.1:11211/api/v1/network/de3f5b8f-0f2f-d9d0-fb30-a2ac8951d92f/65437e50-b286-4098-a624-74429f2cb839 
```
2024-10-26 00:04:22 +08:00
fanyang
b5c3726e67 Optimize building speed (#442)
Some checks failed
EasyTier Core / pre_job (push) Has been cancelled
EasyTier GUI / pre_job (push) Has been cancelled
EasyTier Mobile / pre_job (push) Has been cancelled
EasyTier Test / pre_job (push) Has been cancelled
EasyTier Core / build (freebsd-13.2-x86_64, 13.2, ubuntu-22.04, x86_64-unknown-freebsd) (push) Has been cancelled
EasyTier Core / build (linux-aarch64, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Has been cancelled
EasyTier Core / build (linux-arm, ubuntu-22.04, arm-unknown-linux-musleabi) (push) Has been cancelled
EasyTier Core / build (linux-armhf, ubuntu-22.04, arm-unknown-linux-musleabihf) (push) Has been cancelled
EasyTier Core / build (linux-armv7, ubuntu-22.04, armv7-unknown-linux-musleabi) (push) Has been cancelled
EasyTier Core / build (linux-armv7hf, ubuntu-22.04, armv7-unknown-linux-musleabihf) (push) Has been cancelled
EasyTier Core / build (linux-mips, ubuntu-22.04, mips-unknown-linux-musl) (push) Has been cancelled
EasyTier Core / build (linux-mipsel, ubuntu-22.04, mipsel-unknown-linux-musl) (push) Has been cancelled
EasyTier Core / build (linux-x86_64, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Has been cancelled
EasyTier Core / build (macos-aarch64, macos-latest, aarch64-apple-darwin) (push) Has been cancelled
EasyTier Core / build (macos-x86_64, macos-latest, x86_64-apple-darwin) (push) Has been cancelled
EasyTier Core / build (windows-x86_64, windows-latest, x86_64-pc-windows-msvc) (push) Has been cancelled
EasyTier Core / core-result (push) Has been cancelled
EasyTier GUI / build-gui (linux-aarch64, aarch64-unknown-linux-gnu, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Has been cancelled
EasyTier GUI / build-gui (linux-x86_64, x86_64-unknown-linux-gnu, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Has been cancelled
EasyTier GUI / build-gui (macos-aarch64, aarch64-apple-darwin, macos-latest, aarch64-apple-darwin) (push) Has been cancelled
EasyTier GUI / build-gui (macos-x86_64, x86_64-apple-darwin, macos-latest, x86_64-apple-darwin) (push) Has been cancelled
EasyTier GUI / build-gui (windows-x86_64, x86_64-pc-windows-msvc, windows-latest, x86_64-pc-windows-msvc) (push) Has been cancelled
EasyTier GUI / gui-result (push) Has been cancelled
EasyTier Mobile / build-mobile (android, ubuntu-22.04, android) (push) Has been cancelled
EasyTier Mobile / mobile-result (push) Has been cancelled
EasyTier Test / test (push) Has been cancelled
Make easytier-cli and easytier-core link to the easytier library to
avoid duplicate linking of mods.
2024-10-24 16:21:35 +08:00
m1m1sha
efee3707da 🐳 chore: use vue 3.4.38 (#438)
Some checks failed
EasyTier Core / pre_job (push) Has been cancelled
EasyTier GUI / pre_job (push) Has been cancelled
EasyTier Mobile / pre_job (push) Has been cancelled
EasyTier Test / pre_job (push) Has been cancelled
EasyTier Core / build (freebsd-13.2-x86_64, 13.2, ubuntu-22.04, x86_64-unknown-freebsd) (push) Has been cancelled
EasyTier Core / build (linux-aarch64, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Has been cancelled
EasyTier Core / build (linux-arm, ubuntu-22.04, arm-unknown-linux-musleabi) (push) Has been cancelled
EasyTier Core / build (linux-armhf, ubuntu-22.04, arm-unknown-linux-musleabihf) (push) Has been cancelled
EasyTier Core / build (linux-armv7, ubuntu-22.04, armv7-unknown-linux-musleabi) (push) Has been cancelled
EasyTier Core / build (linux-armv7hf, ubuntu-22.04, armv7-unknown-linux-musleabihf) (push) Has been cancelled
EasyTier Core / build (linux-mips, ubuntu-22.04, mips-unknown-linux-musl) (push) Has been cancelled
EasyTier Core / build (linux-mipsel, ubuntu-22.04, mipsel-unknown-linux-musl) (push) Has been cancelled
EasyTier Core / build (linux-x86_64, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Has been cancelled
EasyTier Core / build (macos-aarch64, macos-latest, aarch64-apple-darwin) (push) Has been cancelled
EasyTier Core / build (macos-x86_64, macos-latest, x86_64-apple-darwin) (push) Has been cancelled
EasyTier Core / build (windows-x86_64, windows-latest, x86_64-pc-windows-msvc) (push) Has been cancelled
EasyTier Core / core-result (push) Has been cancelled
EasyTier GUI / build-gui (linux-aarch64, aarch64-unknown-linux-gnu, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Has been cancelled
EasyTier GUI / build-gui (linux-x86_64, x86_64-unknown-linux-gnu, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Has been cancelled
EasyTier GUI / build-gui (macos-aarch64, aarch64-apple-darwin, macos-latest, aarch64-apple-darwin) (push) Has been cancelled
EasyTier GUI / build-gui (macos-x86_64, x86_64-apple-darwin, macos-latest, x86_64-apple-darwin) (push) Has been cancelled
EasyTier GUI / build-gui (windows-x86_64, x86_64-pc-windows-msvc, windows-latest, x86_64-pc-windows-msvc) (push) Has been cancelled
EasyTier GUI / gui-result (push) Has been cancelled
EasyTier Mobile / build-mobile (android, ubuntu-22.04, android) (push) Has been cancelled
EasyTier Mobile / mobile-result (push) Has been cancelled
EasyTier Test / test (push) Has been cancelled
* 🐳 chore: use vue 3.4.38
2024-10-22 14:08:25 +08:00
Serenity
bbd3453f36 ref: log files are suffix for easier viewing (#433)
Some checks failed
EasyTier Core / pre_job (push) Has been cancelled
EasyTier GUI / pre_job (push) Has been cancelled
EasyTier Mobile / pre_job (push) Has been cancelled
EasyTier Test / pre_job (push) Has been cancelled
EasyTier Core / build (freebsd-13.2-x86_64, 13.2, ubuntu-22.04, x86_64-unknown-freebsd) (push) Has been cancelled
EasyTier Core / build (linux-aarch64, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Has been cancelled
EasyTier Core / build (linux-arm, ubuntu-22.04, arm-unknown-linux-musleabi) (push) Has been cancelled
EasyTier Core / build (linux-armhf, ubuntu-22.04, arm-unknown-linux-musleabihf) (push) Has been cancelled
EasyTier Core / build (linux-armv7, ubuntu-22.04, armv7-unknown-linux-musleabi) (push) Has been cancelled
EasyTier Core / build (linux-armv7hf, ubuntu-22.04, armv7-unknown-linux-musleabihf) (push) Has been cancelled
EasyTier Core / build (linux-mips, ubuntu-22.04, mips-unknown-linux-musl) (push) Has been cancelled
EasyTier Core / build (linux-mipsel, ubuntu-22.04, mipsel-unknown-linux-musl) (push) Has been cancelled
EasyTier Core / build (linux-x86_64, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Has been cancelled
EasyTier Core / build (macos-aarch64, macos-latest, aarch64-apple-darwin) (push) Has been cancelled
EasyTier Core / build (macos-x86_64, macos-latest, x86_64-apple-darwin) (push) Has been cancelled
EasyTier Core / build (windows-x86_64, windows-latest, x86_64-pc-windows-msvc) (push) Has been cancelled
EasyTier Core / core-result (push) Has been cancelled
EasyTier GUI / build-gui (linux-aarch64, aarch64-unknown-linux-gnu, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Has been cancelled
EasyTier GUI / build-gui (linux-x86_64, x86_64-unknown-linux-gnu, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Has been cancelled
EasyTier GUI / build-gui (macos-aarch64, aarch64-apple-darwin, macos-latest, aarch64-apple-darwin) (push) Has been cancelled
EasyTier GUI / build-gui (macos-x86_64, x86_64-apple-darwin, macos-latest, x86_64-apple-darwin) (push) Has been cancelled
EasyTier GUI / build-gui (windows-x86_64, x86_64-pc-windows-msvc, windows-latest, x86_64-pc-windows-msvc) (push) Has been cancelled
EasyTier GUI / gui-result (push) Has been cancelled
EasyTier Mobile / build-mobile (android, ubuntu-22.04, android) (push) Has been cancelled
EasyTier Mobile / mobile-result (push) Has been cancelled
EasyTier Test / test (push) Has been cancelled
2024-10-20 17:34:19 +08:00
Sijie.Sun
0bf42c53cc Feat/web (PatchSet 1) (#436)
Some checks are pending
EasyTier Core / pre_job (push) Waiting to run
EasyTier Core / build (freebsd-13.2-x86_64, 13.2, ubuntu-22.04, x86_64-unknown-freebsd) (push) Blocked by required conditions
EasyTier Core / build (linux-aarch64, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-arm, ubuntu-22.04, arm-unknown-linux-musleabi) (push) Blocked by required conditions
EasyTier Core / build (linux-armhf, ubuntu-22.04, arm-unknown-linux-musleabihf) (push) Blocked by required conditions
EasyTier Core / build (linux-armv7, ubuntu-22.04, armv7-unknown-linux-musleabi) (push) Blocked by required conditions
EasyTier Core / build (linux-armv7hf, ubuntu-22.04, armv7-unknown-linux-musleabihf) (push) Blocked by required conditions
EasyTier Core / build (linux-mips, ubuntu-22.04, mips-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-mipsel, ubuntu-22.04, mipsel-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-x86_64, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (macos-aarch64, macos-latest, aarch64-apple-darwin) (push) Blocked by required conditions
EasyTier Core / build (macos-x86_64, macos-latest, x86_64-apple-darwin) (push) Blocked by required conditions
EasyTier Core / build (windows-x86_64, windows-latest, x86_64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier Core / core-result (push) Blocked by required conditions
EasyTier GUI / pre_job (push) Waiting to run
EasyTier GUI / build-gui (linux-aarch64, aarch64-unknown-linux-gnu, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier GUI / build-gui (linux-x86_64, x86_64-unknown-linux-gnu, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier GUI / build-gui (macos-aarch64, aarch64-apple-darwin, macos-latest, aarch64-apple-darwin) (push) Blocked by required conditions
EasyTier GUI / build-gui (macos-x86_64, x86_64-apple-darwin, macos-latest, x86_64-apple-darwin) (push) Blocked by required conditions
EasyTier GUI / build-gui (windows-x86_64, x86_64-pc-windows-msvc, windows-latest, x86_64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier GUI / gui-result (push) Blocked by required conditions
EasyTier Mobile / pre_job (push) Waiting to run
EasyTier Mobile / build-mobile (android, ubuntu-22.04, android) (push) Blocked by required conditions
EasyTier Mobile / mobile-result (push) Blocked by required conditions
EasyTier Test / pre_job (push) Waiting to run
EasyTier Test / test (push) Blocked by required conditions
* move rpc-build out of easytier dir and make it a independant project
* easytier core use launcher
* fix flags not print on launch
* allow launcher not fetch node info
* abstract out peer rpc impl
* fix arm gui ci. see https://github.com/actions/runner-images/pull/10807
* add easytier-web crate
* fix manual_connector test case
2024-10-19 18:10:02 +08:00
Sijie.Sun
2134bc9139 fix icmp/udp subnet proxy not work with public server relay (#431)
Some checks failed
EasyTier Core / pre_job (push) Has been cancelled
EasyTier GUI / pre_job (push) Has been cancelled
EasyTier Mobile / pre_job (push) Has been cancelled
EasyTier Test / pre_job (push) Has been cancelled
EasyTier Core / build (freebsd-13.2-x86_64, 13.2, ubuntu-latest, x86_64-unknown-freebsd) (push) Has been cancelled
EasyTier Core / build (linux-aarch64, ubuntu-latest, aarch64-unknown-linux-musl) (push) Has been cancelled
EasyTier Core / build (linux-arm, ubuntu-latest, arm-unknown-linux-musleabi) (push) Has been cancelled
EasyTier Core / build (linux-armhf, ubuntu-latest, arm-unknown-linux-musleabihf) (push) Has been cancelled
EasyTier Core / build (linux-armv7, ubuntu-latest, armv7-unknown-linux-musleabi) (push) Has been cancelled
EasyTier Core / build (linux-armv7hf, ubuntu-latest, armv7-unknown-linux-musleabihf) (push) Has been cancelled
EasyTier Core / build (linux-mips, ubuntu-latest, mips-unknown-linux-musl) (push) Has been cancelled
EasyTier Core / build (linux-mipsel, ubuntu-latest, mipsel-unknown-linux-musl) (push) Has been cancelled
EasyTier Core / build (linux-x86_64, ubuntu-latest, x86_64-unknown-linux-musl) (push) Has been cancelled
EasyTier Core / build (macos-aarch64, macos-latest, aarch64-apple-darwin) (push) Has been cancelled
EasyTier Core / build (macos-x86_64, macos-latest, x86_64-apple-darwin) (push) Has been cancelled
EasyTier Core / build (windows-x86_64, windows-latest, x86_64-pc-windows-msvc) (push) Has been cancelled
EasyTier Core / core-result (push) Has been cancelled
EasyTier GUI / build-gui (linux-aarch64, aarch64-unknown-linux-gnu, ubuntu-latest, aarch64-unknown-linux-musl) (push) Has been cancelled
EasyTier GUI / build-gui (linux-x86_64, x86_64-unknown-linux-gnu, ubuntu-latest, x86_64-unknown-linux-musl) (push) Has been cancelled
EasyTier GUI / build-gui (macos-aarch64, aarch64-apple-darwin, macos-latest, aarch64-apple-darwin) (push) Has been cancelled
EasyTier GUI / build-gui (macos-x86_64, x86_64-apple-darwin, macos-latest, x86_64-apple-darwin) (push) Has been cancelled
EasyTier GUI / build-gui (windows-x86_64, x86_64-pc-windows-msvc, windows-latest, x86_64-pc-windows-msvc) (push) Has been cancelled
EasyTier GUI / gui-result (push) Has been cancelled
EasyTier Mobile / build-mobile (android, ubuntu-latest, android) (push) Has been cancelled
EasyTier Mobile / mobile-result (push) Has been cancelled
EasyTier Test / test (push) Has been cancelled
2024-10-17 00:22:42 +08:00
Xiao Tan
4df8d7e976 fix mtu parameter prompt (#429)
Co-authored-by: BH1XAQ <tanxiao@16iot.cn>
2024-10-16 23:16:09 +08:00
fanyang
70708b34cc Fix app not displayed when click on the dock icon under macOS (#424)
Some checks failed
EasyTier Core / pre_job (push) Has been cancelled
EasyTier GUI / pre_job (push) Has been cancelled
EasyTier Mobile / pre_job (push) Has been cancelled
EasyTier Test / pre_job (push) Has been cancelled
EasyTier Core / build (freebsd-13.2-x86_64, 13.2, ubuntu-latest, x86_64-unknown-freebsd) (push) Has been cancelled
EasyTier Core / build (linux-aarch64, ubuntu-latest, aarch64-unknown-linux-musl) (push) Has been cancelled
EasyTier Core / build (linux-arm, ubuntu-latest, arm-unknown-linux-musleabi) (push) Has been cancelled
EasyTier Core / build (linux-armhf, ubuntu-latest, arm-unknown-linux-musleabihf) (push) Has been cancelled
EasyTier Core / build (linux-armv7, ubuntu-latest, armv7-unknown-linux-musleabi) (push) Has been cancelled
EasyTier Core / build (linux-armv7hf, ubuntu-latest, armv7-unknown-linux-musleabihf) (push) Has been cancelled
EasyTier Core / build (linux-mips, ubuntu-latest, mips-unknown-linux-musl) (push) Has been cancelled
EasyTier Core / build (linux-mipsel, ubuntu-latest, mipsel-unknown-linux-musl) (push) Has been cancelled
EasyTier Core / build (linux-x86_64, ubuntu-latest, x86_64-unknown-linux-musl) (push) Has been cancelled
EasyTier Core / build (macos-aarch64, macos-latest, aarch64-apple-darwin) (push) Has been cancelled
EasyTier Core / build (macos-x86_64, macos-latest, x86_64-apple-darwin) (push) Has been cancelled
EasyTier Core / build (windows-x86_64, windows-latest, x86_64-pc-windows-msvc) (push) Has been cancelled
EasyTier Core / core-result (push) Has been cancelled
EasyTier GUI / build-gui (linux-aarch64, aarch64-unknown-linux-gnu, ubuntu-latest, aarch64-unknown-linux-musl) (push) Has been cancelled
EasyTier GUI / build-gui (linux-x86_64, x86_64-unknown-linux-gnu, ubuntu-latest, x86_64-unknown-linux-musl) (push) Has been cancelled
EasyTier GUI / build-gui (macos-aarch64, aarch64-apple-darwin, macos-latest, aarch64-apple-darwin) (push) Has been cancelled
EasyTier GUI / build-gui (macos-x86_64, x86_64-apple-darwin, macos-latest, x86_64-apple-darwin) (push) Has been cancelled
EasyTier GUI / build-gui (windows-x86_64, x86_64-pc-windows-msvc, windows-latest, x86_64-pc-windows-msvc) (push) Has been cancelled
EasyTier GUI / gui-result (push) Has been cancelled
EasyTier Mobile / build-mobile (android, ubuntu-latest, android) (push) Has been cancelled
EasyTier Mobile / mobile-result (push) Has been cancelled
EasyTier Test / test (push) Has been cancelled
2024-10-14 21:33:48 +08:00
m1m1sha
949003ee1b Merge pull request #422 from EasyTier/chore/proxy
Some checks are pending
EasyTier Core / pre_job (push) Waiting to run
EasyTier Core / build (freebsd-13.2-x86_64, 13.2, ubuntu-latest, x86_64-unknown-freebsd) (push) Blocked by required conditions
EasyTier Core / build (linux-aarch64, ubuntu-latest, aarch64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-arm, ubuntu-latest, arm-unknown-linux-musleabi) (push) Blocked by required conditions
EasyTier Core / build (linux-armhf, ubuntu-latest, arm-unknown-linux-musleabihf) (push) Blocked by required conditions
EasyTier Core / build (linux-armv7, ubuntu-latest, armv7-unknown-linux-musleabi) (push) Blocked by required conditions
EasyTier Core / build (linux-armv7hf, ubuntu-latest, armv7-unknown-linux-musleabihf) (push) Blocked by required conditions
EasyTier Core / build (linux-mips, ubuntu-latest, mips-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-mipsel, ubuntu-latest, mipsel-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-x86_64, ubuntu-latest, x86_64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (macos-aarch64, macos-latest, aarch64-apple-darwin) (push) Blocked by required conditions
EasyTier Core / build (macos-x86_64, macos-latest, x86_64-apple-darwin) (push) Blocked by required conditions
EasyTier Core / build (windows-x86_64, windows-latest, x86_64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier Core / core-result (push) Blocked by required conditions
EasyTier GUI / pre_job (push) Waiting to run
EasyTier GUI / build-gui (linux-aarch64, aarch64-unknown-linux-gnu, ubuntu-latest, aarch64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier GUI / build-gui (linux-x86_64, x86_64-unknown-linux-gnu, ubuntu-latest, x86_64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier GUI / build-gui (macos-aarch64, aarch64-apple-darwin, macos-latest, aarch64-apple-darwin) (push) Blocked by required conditions
EasyTier GUI / build-gui (macos-x86_64, x86_64-apple-darwin, macos-latest, x86_64-apple-darwin) (push) Blocked by required conditions
EasyTier GUI / build-gui (windows-x86_64, x86_64-pc-windows-msvc, windows-latest, x86_64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier GUI / gui-result (push) Blocked by required conditions
EasyTier Mobile / pre_job (push) Waiting to run
EasyTier Mobile / build-mobile (android, ubuntu-latest, android) (push) Blocked by required conditions
EasyTier Mobile / mobile-result (push) Blocked by required conditions
EasyTier Test / pre_job (push) Waiting to run
EasyTier Test / test (push) Blocked by required conditions
🐳 chore: proxy
2024-10-14 08:36:20 +08:00
m1m1sha
db9df1df94 🐳 chore: proxy 2024-10-14 08:33:53 +08:00
sijie.sun
4dca25db86 bump version to 2.0.3
Some checks are pending
EasyTier Core / pre_job (push) Waiting to run
EasyTier Core / build (freebsd-13.2-x86_64, 13.2, ubuntu-latest, x86_64-unknown-freebsd) (push) Blocked by required conditions
EasyTier Core / build (linux-aarch64, ubuntu-latest, aarch64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-arm, ubuntu-latest, arm-unknown-linux-musleabi) (push) Blocked by required conditions
EasyTier Core / build (linux-armhf, ubuntu-latest, arm-unknown-linux-musleabihf) (push) Blocked by required conditions
EasyTier Core / build (linux-armv7, ubuntu-latest, armv7-unknown-linux-musleabi) (push) Blocked by required conditions
EasyTier Core / build (linux-armv7hf, ubuntu-latest, armv7-unknown-linux-musleabihf) (push) Blocked by required conditions
EasyTier Core / build (linux-mips, ubuntu-latest, mips-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-mipsel, ubuntu-latest, mipsel-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-x86_64, ubuntu-latest, x86_64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (macos-aarch64, macos-latest, aarch64-apple-darwin) (push) Blocked by required conditions
EasyTier Core / build (macos-x86_64, macos-latest, x86_64-apple-darwin) (push) Blocked by required conditions
EasyTier Core / build (windows-x86_64, windows-latest, x86_64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier Core / core-result (push) Blocked by required conditions
EasyTier GUI / pre_job (push) Waiting to run
EasyTier GUI / build-gui (linux-aarch64, aarch64-unknown-linux-gnu, ubuntu-latest, aarch64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier GUI / build-gui (linux-x86_64, x86_64-unknown-linux-gnu, ubuntu-latest, x86_64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier GUI / build-gui (macos-aarch64, aarch64-apple-darwin, macos-latest, aarch64-apple-darwin) (push) Blocked by required conditions
EasyTier GUI / build-gui (macos-x86_64, x86_64-apple-darwin, macos-latest, x86_64-apple-darwin) (push) Blocked by required conditions
EasyTier GUI / build-gui (windows-x86_64, x86_64-pc-windows-msvc, windows-latest, x86_64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier GUI / gui-result (push) Blocked by required conditions
EasyTier Mobile / pre_job (push) Waiting to run
EasyTier Mobile / build-mobile (android, ubuntu-latest, android) (push) Blocked by required conditions
EasyTier Mobile / mobile-result (push) Blocked by required conditions
EasyTier Test / pre_job (push) Waiting to run
EasyTier Test / test (push) Blocked by required conditions
2024-10-13 12:49:01 +08:00
Sijie.Sun
d87a440c04 fix 202 bugs (#418)
* fix peer rpc stop working because of mpsc tunnel close unexpectedly

* fix gui:

1. allow set network prefix for virtual ipv4
2. fix android crash
3. fix subnet proxy cannot be set on android
2024-10-13 11:59:16 +08:00
m1m1sha
55efd62798 Merge pull request #417 from EasyTier/perf/detail
Some checks are pending
EasyTier Core / pre_job (push) Waiting to run
EasyTier Core / build (freebsd-13.2-x86_64, 13.2, ubuntu-latest, x86_64-unknown-freebsd) (push) Blocked by required conditions
EasyTier Core / build (linux-aarch64, ubuntu-latest, aarch64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-arm, ubuntu-latest, arm-unknown-linux-musleabi) (push) Blocked by required conditions
EasyTier Core / build (linux-armhf, ubuntu-latest, arm-unknown-linux-musleabihf) (push) Blocked by required conditions
EasyTier Core / build (linux-armv7, ubuntu-latest, armv7-unknown-linux-musleabi) (push) Blocked by required conditions
EasyTier Core / build (linux-armv7hf, ubuntu-latest, armv7-unknown-linux-musleabihf) (push) Blocked by required conditions
EasyTier Core / build (linux-mips, ubuntu-latest, mips-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-mipsel, ubuntu-latest, mipsel-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-x86_64, ubuntu-latest, x86_64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (macos-aarch64, macos-latest, aarch64-apple-darwin) (push) Blocked by required conditions
EasyTier Core / build (macos-x86_64, macos-latest, x86_64-apple-darwin) (push) Blocked by required conditions
EasyTier Core / build (windows-x86_64, windows-latest, x86_64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier Core / core-result (push) Blocked by required conditions
EasyTier GUI / pre_job (push) Waiting to run
EasyTier GUI / build-gui (linux-aarch64, aarch64-unknown-linux-gnu, ubuntu-latest, aarch64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier GUI / build-gui (linux-x86_64, x86_64-unknown-linux-gnu, ubuntu-latest, x86_64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier GUI / build-gui (macos-aarch64, aarch64-apple-darwin, macos-latest, aarch64-apple-darwin) (push) Blocked by required conditions
EasyTier GUI / build-gui (macos-x86_64, x86_64-apple-darwin, macos-latest, x86_64-apple-darwin) (push) Blocked by required conditions
EasyTier GUI / build-gui (windows-x86_64, x86_64-pc-windows-msvc, windows-latest, x86_64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier GUI / gui-result (push) Blocked by required conditions
EasyTier Mobile / pre_job (push) Waiting to run
EasyTier Mobile / build-mobile (android, ubuntu-latest, android) (push) Blocked by required conditions
EasyTier Mobile / mobile-result (push) Blocked by required conditions
EasyTier Test / pre_job (push) Waiting to run
EasyTier Test / test (push) Blocked by required conditions
🎈 perf: event log
2024-10-12 20:47:42 +08:00
m1m1sha
70a41275c1 feat: display
Display server tag and whether server supports relay
2024-10-12 20:17:45 +08:00
m1m1sha
dd941681ce 🎈 perf: event log 2024-10-12 19:57:36 +08:00
m1m1sha
9824d0adaa Fix/UI detail (#414)
Some checks are pending
EasyTier Core / pre_job (push) Waiting to run
EasyTier Core / build (freebsd-13.2-x86_64, 13.2, ubuntu-latest, x86_64-unknown-freebsd) (push) Blocked by required conditions
EasyTier Core / build (linux-aarch64, ubuntu-latest, aarch64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-arm, ubuntu-latest, arm-unknown-linux-musleabi) (push) Blocked by required conditions
EasyTier Core / build (linux-armhf, ubuntu-latest, arm-unknown-linux-musleabihf) (push) Blocked by required conditions
EasyTier Core / build (linux-armv7, ubuntu-latest, armv7-unknown-linux-musleabi) (push) Blocked by required conditions
EasyTier Core / build (linux-armv7hf, ubuntu-latest, armv7-unknown-linux-musleabihf) (push) Blocked by required conditions
EasyTier Core / build (linux-mips, ubuntu-latest, mips-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-mipsel, ubuntu-latest, mipsel-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-x86_64, ubuntu-latest, x86_64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (macos-aarch64, macos-latest, aarch64-apple-darwin) (push) Blocked by required conditions
EasyTier Core / build (macos-x86_64, macos-latest, x86_64-apple-darwin) (push) Blocked by required conditions
EasyTier Core / build (windows-x86_64, windows-latest, x86_64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier Core / core-result (push) Blocked by required conditions
EasyTier GUI / pre_job (push) Waiting to run
EasyTier GUI / build-gui (linux-aarch64, aarch64-unknown-linux-gnu, ubuntu-latest, aarch64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier GUI / build-gui (linux-x86_64, x86_64-unknown-linux-gnu, ubuntu-latest, x86_64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier GUI / build-gui (macos-aarch64, aarch64-apple-darwin, macos-latest, aarch64-apple-darwin) (push) Blocked by required conditions
EasyTier GUI / build-gui (macos-x86_64, x86_64-apple-darwin, macos-latest, x86_64-apple-darwin) (push) Blocked by required conditions
EasyTier GUI / build-gui (windows-x86_64, x86_64-pc-windows-msvc, windows-latest, x86_64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier GUI / gui-result (push) Blocked by required conditions
EasyTier Mobile / pre_job (push) Waiting to run
EasyTier Mobile / build-mobile (android, ubuntu-latest, android) (push) Blocked by required conditions
EasyTier Mobile / mobile-result (push) Blocked by required conditions
EasyTier Test / pre_job (push) Waiting to run
EasyTier Test / test (push) Blocked by required conditions
2024-10-12 00:36:57 +08:00
Sijie.Sun
d2291628e0 mpsc tunnel may be stuck by slow tcp stream, should not panic for this (#406)
Some checks are pending
EasyTier Core / pre_job (push) Waiting to run
EasyTier Core / build (freebsd-13.2-x86_64, 13.2, ubuntu-latest, x86_64-unknown-freebsd) (push) Blocked by required conditions
EasyTier Core / build (linux-aarch64, ubuntu-latest, aarch64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-arm, ubuntu-latest, arm-unknown-linux-musleabi) (push) Blocked by required conditions
EasyTier Core / build (linux-armhf, ubuntu-latest, arm-unknown-linux-musleabihf) (push) Blocked by required conditions
EasyTier Core / build (linux-armv7, ubuntu-latest, armv7-unknown-linux-musleabi) (push) Blocked by required conditions
EasyTier Core / build (linux-armv7hf, ubuntu-latest, armv7-unknown-linux-musleabihf) (push) Blocked by required conditions
EasyTier Core / build (linux-mips, ubuntu-latest, mips-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-mipsel, ubuntu-latest, mipsel-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-x86_64, ubuntu-latest, x86_64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (macos-aarch64, macos-latest, aarch64-apple-darwin) (push) Blocked by required conditions
EasyTier Core / build (macos-x86_64, macos-latest, x86_64-apple-darwin) (push) Blocked by required conditions
EasyTier Core / build (windows-x86_64, windows-latest, x86_64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier Core / core-result (push) Blocked by required conditions
EasyTier GUI / pre_job (push) Waiting to run
EasyTier GUI / build-gui (linux-aarch64, aarch64-unknown-linux-gnu, ubuntu-latest, aarch64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier GUI / build-gui (linux-x86_64, x86_64-unknown-linux-gnu, ubuntu-latest, x86_64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier GUI / build-gui (macos-aarch64, aarch64-apple-darwin, macos-latest, aarch64-apple-darwin) (push) Blocked by required conditions
EasyTier GUI / build-gui (macos-x86_64, x86_64-apple-darwin, macos-latest, x86_64-apple-darwin) (push) Blocked by required conditions
EasyTier GUI / build-gui (windows-x86_64, x86_64-pc-windows-msvc, windows-latest, x86_64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier GUI / gui-result (push) Blocked by required conditions
EasyTier Mobile / pre_job (push) Waiting to run
EasyTier Mobile / build-mobile (android, ubuntu-latest, android) (push) Blocked by required conditions
EasyTier Mobile / mobile-result (push) Blocked by required conditions
EasyTier Test / pre_job (push) Waiting to run
EasyTier Test / test (push) Blocked by required conditions
* mpsc tunnel may be stuck by slow tcp stream, should not panic for this
* disallow node connect to self
2024-10-11 00:12:14 +08:00
Sijie.Sun
7ab8cad1af allow use ipv4 address in any cidr (#404)
Some checks are pending
EasyTier Core / pre_job (push) Waiting to run
EasyTier Core / build (freebsd-13.2-x86_64, 13.2, ubuntu-latest, x86_64-unknown-freebsd) (push) Blocked by required conditions
EasyTier Core / build (linux-aarch64, ubuntu-latest, aarch64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-arm, ubuntu-latest, arm-unknown-linux-musleabi) (push) Blocked by required conditions
EasyTier Core / build (linux-armhf, ubuntu-latest, arm-unknown-linux-musleabihf) (push) Blocked by required conditions
EasyTier Core / build (linux-armv7, ubuntu-latest, armv7-unknown-linux-musleabi) (push) Blocked by required conditions
EasyTier Core / build (linux-armv7hf, ubuntu-latest, armv7-unknown-linux-musleabihf) (push) Blocked by required conditions
EasyTier Core / build (linux-mips, ubuntu-latest, mips-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-mipsel, ubuntu-latest, mipsel-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-x86_64, ubuntu-latest, x86_64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (macos-aarch64, macos-latest, aarch64-apple-darwin) (push) Blocked by required conditions
EasyTier Core / build (macos-x86_64, macos-latest, x86_64-apple-darwin) (push) Blocked by required conditions
EasyTier Core / build (windows-x86_64, windows-latest, x86_64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier Core / core-result (push) Blocked by required conditions
EasyTier GUI / pre_job (push) Waiting to run
EasyTier GUI / build-gui (linux-aarch64, aarch64-unknown-linux-gnu, ubuntu-latest, aarch64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier GUI / build-gui (linux-x86_64, x86_64-unknown-linux-gnu, ubuntu-latest, x86_64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier GUI / build-gui (macos-aarch64, aarch64-apple-darwin, macos-latest, aarch64-apple-darwin) (push) Blocked by required conditions
EasyTier GUI / build-gui (macos-x86_64, x86_64-apple-darwin, macos-latest, x86_64-apple-darwin) (push) Blocked by required conditions
EasyTier GUI / build-gui (windows-x86_64, x86_64-pc-windows-msvc, windows-latest, x86_64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier GUI / gui-result (push) Blocked by required conditions
EasyTier Mobile / pre_job (push) Waiting to run
EasyTier Mobile / build-mobile (android, ubuntu-latest, android) (push) Blocked by required conditions
EasyTier Mobile / mobile-result (push) Blocked by required conditions
EasyTier Test / pre_job (push) Waiting to run
EasyTier Test / test (push) Blocked by required conditions
2024-10-10 10:28:48 +08:00
Sijie.Sun
2c017e0fc5 improve hole punch (#403)
Some checks are pending
EasyTier Core / pre_job (push) Waiting to run
EasyTier Core / build (freebsd-13.2-x86_64, 13.2, ubuntu-latest, x86_64-unknown-freebsd) (push) Blocked by required conditions
EasyTier Core / build (linux-aarch64, ubuntu-latest, aarch64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-arm, ubuntu-latest, arm-unknown-linux-musleabi) (push) Blocked by required conditions
EasyTier Core / build (linux-armhf, ubuntu-latest, arm-unknown-linux-musleabihf) (push) Blocked by required conditions
EasyTier Core / build (linux-armv7, ubuntu-latest, armv7-unknown-linux-musleabi) (push) Blocked by required conditions
EasyTier Core / build (linux-armv7hf, ubuntu-latest, armv7-unknown-linux-musleabihf) (push) Blocked by required conditions
EasyTier Core / build (linux-mips, ubuntu-latest, mips-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-mipsel, ubuntu-latest, mipsel-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-x86_64, ubuntu-latest, x86_64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (macos-aarch64, macos-latest, aarch64-apple-darwin) (push) Blocked by required conditions
EasyTier Core / build (macos-x86_64, macos-latest, x86_64-apple-darwin) (push) Blocked by required conditions
EasyTier Core / build (windows-x86_64, windows-latest, x86_64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier Core / core-result (push) Blocked by required conditions
EasyTier GUI / pre_job (push) Waiting to run
EasyTier GUI / build-gui (linux-aarch64, aarch64-unknown-linux-gnu, ubuntu-latest, aarch64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier GUI / build-gui (linux-x86_64, x86_64-unknown-linux-gnu, ubuntu-latest, x86_64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier GUI / build-gui (macos-aarch64, aarch64-apple-darwin, macos-latest, aarch64-apple-darwin) (push) Blocked by required conditions
EasyTier GUI / build-gui (macos-x86_64, x86_64-apple-darwin, macos-latest, x86_64-apple-darwin) (push) Blocked by required conditions
EasyTier GUI / build-gui (windows-x86_64, x86_64-pc-windows-msvc, windows-latest, x86_64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier GUI / gui-result (push) Blocked by required conditions
EasyTier Mobile / pre_job (push) Waiting to run
EasyTier Mobile / build-mobile (android, ubuntu-latest, android) (push) Blocked by required conditions
EasyTier Mobile / mobile-result (push) Blocked by required conditions
EasyTier Test / pre_job (push) Waiting to run
EasyTier Test / test (push) Blocked by required conditions
* fix duplicated peer id (again)

* improve udp hole punch

1. always try cone punch for any nat type, tolerate fault stun type.
2. serializing all sym punch request, including server side.
2024-10-10 00:07:42 +08:00
Hs_Yeah
d9453589ac Fix panic when DNS resolution for STUN server returns only IPv6 addrs. (#402) 2024-10-09 22:40:01 +08:00
Sijie.Sun
e344372616 fix cone-to-cone punch (#401) 2024-10-09 22:39:06 +08:00
Sijie.Sun
63821e56bc fix udp buffer size, avoid packet loss (#399)
Some checks are pending
EasyTier Core / pre_job (push) Waiting to run
EasyTier Core / build (freebsd-13.2-x86_64, 13.2, ubuntu-latest, x86_64-unknown-freebsd) (push) Blocked by required conditions
EasyTier Core / build (linux-aarch64, ubuntu-latest, aarch64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-arm, ubuntu-latest, arm-unknown-linux-musleabi) (push) Blocked by required conditions
EasyTier Core / build (linux-armhf, ubuntu-latest, arm-unknown-linux-musleabihf) (push) Blocked by required conditions
EasyTier Core / build (linux-armv7, ubuntu-latest, armv7-unknown-linux-musleabi) (push) Blocked by required conditions
EasyTier Core / build (linux-armv7hf, ubuntu-latest, armv7-unknown-linux-musleabihf) (push) Blocked by required conditions
EasyTier Core / build (linux-mips, ubuntu-latest, mips-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-mipsel, ubuntu-latest, mipsel-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-x86_64, ubuntu-latest, x86_64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (macos-aarch64, macos-latest, aarch64-apple-darwin) (push) Blocked by required conditions
EasyTier Core / build (macos-x86_64, macos-latest, x86_64-apple-darwin) (push) Blocked by required conditions
EasyTier Core / build (windows-x86_64, windows-latest, x86_64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier Core / core-result (push) Blocked by required conditions
EasyTier GUI / pre_job (push) Waiting to run
EasyTier GUI / build-gui (linux-aarch64, aarch64-unknown-linux-gnu, ubuntu-latest, aarch64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier GUI / build-gui (linux-x86_64, x86_64-unknown-linux-gnu, ubuntu-latest, x86_64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier GUI / build-gui (macos-aarch64, aarch64-apple-darwin, macos-latest, aarch64-apple-darwin) (push) Blocked by required conditions
EasyTier GUI / build-gui (macos-x86_64, x86_64-apple-darwin, macos-latest, x86_64-apple-darwin) (push) Blocked by required conditions
EasyTier GUI / build-gui (windows-x86_64, x86_64-pc-windows-msvc, windows-latest, x86_64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier GUI / gui-result (push) Blocked by required conditions
EasyTier Mobile / pre_job (push) Waiting to run
EasyTier Mobile / build-mobile (android, ubuntu-latest, android) (push) Blocked by required conditions
EasyTier Mobile / mobile-result (push) Blocked by required conditions
EasyTier Test / pre_job (push) Waiting to run
EasyTier Test / test (push) Blocked by required conditions
also bump version to 2.0.2
2024-10-08 22:01:15 +08:00
194 changed files with 21980 additions and 3126 deletions

View File

@@ -37,28 +37,28 @@ jobs:
matrix: matrix:
include: include:
- TARGET: aarch64-unknown-linux-musl - TARGET: aarch64-unknown-linux-musl
OS: ubuntu-latest OS: ubuntu-22.04
ARTIFACT_NAME: linux-aarch64 ARTIFACT_NAME: linux-aarch64
- TARGET: x86_64-unknown-linux-musl - TARGET: x86_64-unknown-linux-musl
OS: ubuntu-latest OS: ubuntu-22.04
ARTIFACT_NAME: linux-x86_64 ARTIFACT_NAME: linux-x86_64
- TARGET: mips-unknown-linux-musl - TARGET: mips-unknown-linux-musl
OS: ubuntu-latest OS: ubuntu-22.04
ARTIFACT_NAME: linux-mips ARTIFACT_NAME: linux-mips
- TARGET: mipsel-unknown-linux-musl - TARGET: mipsel-unknown-linux-musl
OS: ubuntu-latest OS: ubuntu-22.04
ARTIFACT_NAME: linux-mipsel ARTIFACT_NAME: linux-mipsel
- TARGET: armv7-unknown-linux-musleabihf # raspberry pi 2-3-4, not tested - TARGET: armv7-unknown-linux-musleabihf # raspberry pi 2-3-4, not tested
OS: ubuntu-latest OS: ubuntu-22.04
ARTIFACT_NAME: linux-armv7hf ARTIFACT_NAME: linux-armv7hf
- TARGET: armv7-unknown-linux-musleabi # raspberry pi 2-3-4, not tested - TARGET: armv7-unknown-linux-musleabi # raspberry pi 2-3-4, not tested
OS: ubuntu-latest OS: ubuntu-22.04
ARTIFACT_NAME: linux-armv7 ARTIFACT_NAME: linux-armv7
- TARGET: arm-unknown-linux-musleabihf # raspberry pi 0-1, not tested - TARGET: arm-unknown-linux-musleabihf # raspberry pi 0-1, not tested
OS: ubuntu-latest OS: ubuntu-22.04
ARTIFACT_NAME: linux-armhf ARTIFACT_NAME: linux-armhf
- TARGET: arm-unknown-linux-musleabi # raspberry pi 0-1, not tested - TARGET: arm-unknown-linux-musleabi # raspberry pi 0-1, not tested
OS: ubuntu-latest OS: ubuntu-22.04
ARTIFACT_NAME: linux-arm ARTIFACT_NAME: linux-arm
- TARGET: x86_64-apple-darwin - TARGET: x86_64-apple-darwin
@@ -72,8 +72,12 @@ jobs:
OS: windows-latest OS: windows-latest
ARTIFACT_NAME: windows-x86_64 ARTIFACT_NAME: windows-x86_64
- TARGET: aarch64-pc-windows-msvc
OS: windows-latest
ARTIFACT_NAME: windows-arm64
- TARGET: x86_64-unknown-freebsd - TARGET: x86_64-unknown-freebsd
OS: ubuntu-latest OS: ubuntu-22.04
ARTIFACT_NAME: freebsd-13.2-x86_64 ARTIFACT_NAME: freebsd-13.2-x86_64
BSD_VERSION: 13.2 BSD_VERSION: 13.2
@@ -111,7 +115,7 @@ jobs:
run: | run: |
bash ./.github/workflows/install_rust.sh bash ./.github/workflows/install_rust.sh
if [[ $OS =~ ^ubuntu.*$ && $TARGET =~ ^mips.*$ ]]; then 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 --verbose --target $TARGET -Z build-std=std,panic_abort --no-default-features --features mips --package=easytier
else else
cargo build --release --verbose --target $TARGET cargo build --release --verbose --target $TARGET
fi fi
@@ -164,10 +168,14 @@ jobs:
run: | run: |
mkdir -p ./artifacts/objects/ mkdir -p ./artifacts/objects/
# windows is the only OS using a different convention for executable file name # 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 SUFFIX=.exe
cp easytier/third_party/Packet.dll ./artifacts/objects/ cp easytier/third_party/Packet.dll ./artifacts/objects/
cp easytier/third_party/wintun.dll ./artifacts/objects/ cp easytier/third_party/wintun.dll ./artifacts/objects/
elif [[ $OS =~ ^windows.*$ && $TARGET =~ ^aarch64.*$ ]]; then
SUFFIX=.exe
cp easytier/third_party/arm64/Packet.dll ./artifacts/objects/
cp easytier/third_party/arm64/wintun.dll ./artifacts/objects/
fi fi
if [[ $GITHUB_REF_TYPE =~ ^tag$ ]]; then if [[ $GITHUB_REF_TYPE =~ ^tag$ ]]; then
TAG=$GITHUB_REF_NAME TAG=$GITHUB_REF_NAME
@@ -182,6 +190,9 @@ jobs:
mv ./target/$TARGET/release/easytier-core"$SUFFIX" ./artifacts/objects/ mv ./target/$TARGET/release/easytier-core"$SUFFIX" ./artifacts/objects/
mv ./target/$TARGET/release/easytier-cli"$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/
fi
mv ./artifacts/objects/* ./artifacts/ mv ./artifacts/objects/* ./artifacts/
rm -rf ./artifacts/objects/ rm -rf ./artifacts/objects/

View File

@@ -36,11 +36,11 @@ jobs:
matrix: matrix:
include: include:
- TARGET: aarch64-unknown-linux-musl - TARGET: aarch64-unknown-linux-musl
OS: ubuntu-latest OS: ubuntu-22.04
GUI_TARGET: aarch64-unknown-linux-gnu GUI_TARGET: aarch64-unknown-linux-gnu
ARTIFACT_NAME: linux-aarch64 ARTIFACT_NAME: linux-aarch64
- TARGET: x86_64-unknown-linux-musl - TARGET: x86_64-unknown-linux-musl
OS: ubuntu-latest OS: ubuntu-22.04
GUI_TARGET: x86_64-unknown-linux-gnu GUI_TARGET: x86_64-unknown-linux-gnu
ARTIFACT_NAME: linux-x86_64 ARTIFACT_NAME: linux-x86_64
@@ -58,6 +58,11 @@ jobs:
GUI_TARGET: x86_64-pc-windows-msvc GUI_TARGET: x86_64-pc-windows-msvc
ARTIFACT_NAME: windows-x86_64 ARTIFACT_NAME: windows-x86_64
- TARGET: aarch64-pc-windows-msvc
OS: windows-latest
GUI_TARGET: aarch64-pc-windows-msvc
ARTIFACT_NAME: windows-arm64
runs-on: ${{ matrix.OS }} runs-on: ${{ matrix.OS }}
env: env:
NAME: easytier NAME: easytier
@@ -99,8 +104,8 @@ jobs:
- name: Install frontend dependencies - name: Install frontend dependencies
run: | run: |
(cd easytier-gui; pnpm install) pnpm -r install
(cd tauri-plugin-vpnservice; pnpm install; pnpm build) pnpm -r build
- name: Cargo cache - name: Cargo cache
uses: actions/cache@v4 uses: actions/cache@v4
@@ -123,41 +128,52 @@ jobs:
if: ${{ matrix.TARGET == 'aarch64-unknown-linux-musl' }} if: ${{ matrix.TARGET == 'aarch64-unknown-linux-musl' }}
run: | run: |
# see https://tauri.app/v1/guides/building/linux/ # 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/ jammy 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/ jammy-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/ jammy 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/ jammy-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/ jammy 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/ jammy-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://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/ noble-security main restricted" | 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/ noble-security universe" | 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/ noble-security multiverse" | 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 noble main restricted" | 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 noble-updates 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 noble universe" | 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 noble-updates 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 noble multiverse" | 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 noble-updates 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 noble-backports main restricted universe 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 noble-security main restricted" | 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 noble-security universe" | 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 noble-security multiverse" | 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 dpkg --add-architecture arm64
sudo apt-get update && sudo apt-get upgrade -y sudo apt-get update
sudo apt-get install -y libgstreamer1.0-0:arm64 gstreamer1.0-plugins-base:arm64 gstreamer1.0-plugins-good:arm64
sudo apt-get install -y libgstreamer-gl1.0-0:arm64 libgstreamer-plugins-base1.0-0:arm64 libgstreamer-plugins-good1.0-0:arm64 libwebkit2gtk-4.1-0:arm64
sudo apt install -f -o Dpkg::Options::="--force-overwrite" libwebkit2gtk-4.1-dev:arm64 libssl-dev:arm64 gcc-aarch64-linux-gnu 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_SYSROOT_DIR=/usr/aarch64-linux-gnu/" >> "$GITHUB_ENV"
echo "PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig/" >> "$GITHUB_ENV" echo "PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig/" >> "$GITHUB_ENV"
- name: copy correct DLLs
if: ${{ matrix.OS == 'windows-latest' }}
run: |
if [[ $GUI_TARGET =~ ^aarch64.*$ ]]; then
cp ./easytier/third_party/arm64/*.dll ./easytier-gui/src-tauri/
else
cp ./easytier/third_party/*.dll ./easytier-gui/src-tauri/
fi
- name: Build GUI - name: Build GUI
if: ${{ matrix.GUI_TARGET != '' }} if: ${{ matrix.GUI_TARGET != '' }}
uses: tauri-apps/tauri-action@v0 uses: tauri-apps/tauri-action@v0
with: with:
projectPath: ./easytier-gui projectPath: ./easytier-gui
# https://tauri.app/v1/guides/building/linux/#cross-compiling-tauri-applications-for-arm-based-devices # 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 - name: Compress
run: | run: |

View File

@@ -36,7 +36,7 @@ jobs:
matrix: matrix:
include: include:
- TARGET: android - TARGET: android
OS: ubuntu-latest OS: ubuntu-22.04
ARTIFACT_NAME: android ARTIFACT_NAME: android
runs-on: ${{ matrix.OS }} runs-on: ${{ matrix.OS }}
env: env:
@@ -95,8 +95,8 @@ jobs:
- name: Install frontend dependencies - name: Install frontend dependencies
run: | run: |
(cd easytier-gui; pnpm install) pnpm -r install
(cd tauri-plugin-vpnservice; pnpm install; pnpm build) pnpm -r build
- name: Cargo cache - name: Cargo cache
uses: actions/cache@v4 uses: actions/cache@v4

View File

@@ -21,7 +21,7 @@ on:
version: version:
description: 'Version for this release' description: 'Version for this release'
type: string type: string
default: 'v2.0.1' default: 'v2.1.0'
required: true required: true
make_latest: make_latest:
description: 'Mark this release as latest' description: 'Mark this release as latest'

View File

@@ -30,7 +30,7 @@ jobs:
skip_after_successful_duplicate: 'true' skip_after_successful_duplicate: 'true'
paths: '["Cargo.toml", "Cargo.lock", "easytier/**", ".github/workflows/test.yml"]' paths: '["Cargo.toml", "Cargo.lock", "easytier/**", ".github/workflows/test.yml"]'
test: test:
runs-on: ubuntu-latest runs-on: ubuntu-22.04
needs: pre_job needs: pre_job
if: needs.pre_job.outputs.should_skip != 'true' if: needs.pre_job.outputs.should_skip != 'true'
steps: steps:

8
.gitignore vendored
View File

@@ -11,6 +11,7 @@ target-*/
*.pdb *.pdb
.vscode .vscode
/.idea
# perf & flamegraph # perf & flamegraph
perf.data perf.data
@@ -29,3 +30,10 @@ musl_gcc
# log # log
easytier-panic.log easytier-panic.log
# web
node_modules
.vite
easytier-gui/src-tauri/*.dll

2470
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
[workspace] [workspace]
resolver = "2" resolver = "2"
members = ["easytier", "easytier-gui/src-tauri"] members = ["easytier", "easytier-gui/src-tauri", "easytier-rpc-build", "easytier-web"]
default-members = ["easytier"] default-members = ["easytier", "easytier-web"]
[profile.dev] [profile.dev]
panic = "unwind" panic = "unwind"

View File

@@ -4,84 +4,27 @@
"path": "." "path": "."
}, },
{ {
"name": "gui",
"path": "easytier-gui" "path": "easytier-gui"
}, },
{ {
"name": "core",
"path": "easytier" "path": "easytier"
},
{
"name": "vpnservice",
"path": "tauri-plugin-vpnservice"
},
{
"name": "rpc-build",
"path": "easytier-rpc-build"
} }
], ],
"settings": { "settings": {
"eslint.useFlatConfig": true, "i18n-ally.sourceLanguage": "cn",
"i18n-ally.keystyle": "nested",
"i18n-ally.sortKeys": true,
// Disable the default formatter
"prettier.enable": false, "prettier.enable": false,
"editor.formatOnSave": false,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit",
"source.organizeImports": "never"
},
"eslint.rules.customizations": [
{
"rule": "style/*",
"severity": "off"
},
{
"rule": "style/eol-last",
"severity": "error"
},
{
"rule": "format/*",
"severity": "off"
},
{
"rule": "*-indent",
"severity": "off"
},
{
"rule": "*-spacing",
"severity": "off"
},
{
"rule": "*-spaces",
"severity": "off"
},
{
"rule": "*-order",
"severity": "off"
},
{
"rule": "*-dangle",
"severity": "off"
},
{
"rule": "*-newline",
"severity": "off"
},
{
"rule": "*quotes",
"severity": "off"
},
{
"rule": "*semi",
"severity": "off"
}
],
"eslint.validate": [
"code-workspace",
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"vue",
"html",
"markdown",
"json",
"jsonc",
"yaml",
"toml",
"gql",
"graphql"
],
"i18n-ally.localesPaths": [
"easytier-gui/locales"
]
} }
} }

View File

@@ -1,2 +0,0 @@
shamefully-hoist=true
strict-peer-dependencies=false

View File

@@ -1,5 +1,81 @@
{ {
"i18n-ally.localesPaths": [ "cSpell.words": [
"locales" "easytier",
"Vite",
"vueuse",
"pinia",
"demi",
"antfu",
"iconify",
"intlify",
"vitejs",
"unplugin",
"pnpm"
],
"i18n-ally.localesPaths": "locales",
"editor.formatOnSave": false,
// Auto fix
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit",
"source.organizeImports": "never"
},
// Silent the stylistic rules in you IDE, but still auto fix them
"eslint.rules.customizations": [
{
"rule": "style/*",
"severity": "off"
},
{
"rule": "format/*",
"severity": "off"
},
{
"rule": "*-indent",
"severity": "off"
},
{
"rule": "*-spacing",
"severity": "off"
},
{
"rule": "*-spaces",
"severity": "off"
},
{
"rule": "*-order",
"severity": "off"
},
{
"rule": "*-dangle",
"severity": "off"
},
{
"rule": "*-newline",
"severity": "off"
},
{
"rule": "*quotes",
"severity": "off"
},
{
"rule": "*semi",
"severity": "off"
}
],
// The following is optional.
// It's better to put under project setting `.vscode/settings.json`
// to avoid conflicts with working with different eslint configs
// that does not support all formats.
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"vue",
"html",
"markdown",
"json",
"jsonc",
"yaml"
] ]
} }

View File

@@ -72,6 +72,8 @@ loss_rate: 丢包率
status: status:
version: 内核版本 version: 内核版本
local: 本机 local: 本机
server: 服务器
relay: 中继
run_network: 运行网络 run_network: 运行网络
stop_network: 停止网络 stop_network: 停止网络
@@ -91,3 +93,23 @@ about:
license: 许可证 license: 许可证
description: 一个简单、安全、去中心化的内网穿透 VPN 组网方案,使用 Rust 语言和 Tokio 框架实现。 description: 一个简单、安全、去中心化的内网穿透 VPN 组网方案,使用 Rust 语言和 Tokio 框架实现。
check_update: 检查更新 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地址冲突

View File

@@ -71,6 +71,8 @@ loss_rate: Loss Rate
status: status:
version: Version version: Version
local: Local local: Local
server: Server
relay: Relay
run_network: Run Network run_network: Run Network
stop_network: Stop Network stop_network: Stop Network
@@ -90,3 +92,23 @@ about:
license: License license: License
description: 'EasyTier is a simple, safe and decentralized VPN networking solution implemented with the Rust language and Tokio framework.' description: 'EasyTier is a simple, safe and decentralized VPN networking solution implemented with the Rust language and Tokio framework.'
check_update: Check Update 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

View File

@@ -1,8 +1,9 @@
{ {
"name": "easytier-gui", "name": "easytier-gui",
"type": "module", "type": "module",
"version": "2.0.1", "version": "2.1.0",
"private": true, "private": true,
"packageManager": "pnpm@9.12.1+sha512.e5a7e52a4183a02d5931057f7a0dbff9d5e9ce3161e33fa68ae392125b79282a8a8a470a51dfc8a0ed86221442eb2fb57019b0990ed24fab519bf0e1bc5ccfc4",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build": "vue-tsc --noEmit && vite build", "build": "vue-tsc --noEmit && vite build",
@@ -12,33 +13,32 @@
"lint:fix": "eslint . --ignore-pattern src-tauri --fix" "lint:fix": "eslint . --ignore-pattern src-tauri --fix"
}, },
"dependencies": { "dependencies": {
"@primevue/themes": "^4.1.0", "@primevue/themes": "^4.2.1",
"@tauri-apps/plugin-autostart": "2.0.0-rc.1", "@tauri-apps/plugin-autostart": "2.0.0",
"@tauri-apps/plugin-clipboard-manager": "2.0.0-rc.1", "@tauri-apps/plugin-clipboard-manager": "2.0.0",
"@tauri-apps/plugin-os": "2.0.0-rc.1", "@tauri-apps/plugin-os": "2.0.0",
"@tauri-apps/plugin-process": "2.0.0-rc.1", "@tauri-apps/plugin-process": "2.0.0",
"@tauri-apps/plugin-shell": "2.0.0-rc.1", "@tauri-apps/plugin-shell": "2.0.1",
"@vueuse/core": "^11.2.0",
"aura": "link:@primevue\\themes\\aura", "aura": "link:@primevue\\themes\\aura",
"easytier-frontend-lib": "workspace:*",
"ip-num": "1.5.1", "ip-num": "1.5.1",
"pinia": "^2.2.4", "pinia": "^2.2.4",
"primeflex": "^3.3.1", "primevue": "^4.2.1",
"primeicons": "^7.0.0", "tauri-plugin-vpnservice-api": "workspace:*",
"primevue": "^4.1.0", "vue": "^3.5.12",
"tauri-plugin-vpnservice-api": "link:..\\tauri-plugin-vpnservice",
"vue": "^3.5.11",
"vue-i18n": "^10.0.4",
"vue-router": "^4.4.5" "vue-router": "^4.4.5"
}, },
"devDependencies": { "devDependencies": {
"@antfu/eslint-config": "^3.7.3", "@antfu/eslint-config": "^3.7.3",
"@intlify/unplugin-vue-i18n": "^5.2.0", "@intlify/unplugin-vue-i18n": "^5.2.0",
"@primevue/auto-import-resolver": "^4.1.0", "@primevue/auto-import-resolver": "^4.1.0",
"@tauri-apps/api": "2.0.0-rc.0", "@tauri-apps/api": "2.1.0",
"@tauri-apps/cli": "2.0.0-rc.3", "@tauri-apps/cli": "2.1.0",
"@types/node": "^22.7.4", "@types/node": "^22.7.4",
"@types/uuid": "^10.0.0", "@types/uuid": "^10.0.0",
"@vitejs/plugin-vue": "^5.1.4", "@vitejs/plugin-vue": "^5.1.4",
"@vue-macros/volar": "^0.29.1", "@vue-macros/volar": "0.30.5",
"autoprefixer": "^10.4.20", "autoprefixer": "^10.4.20",
"eslint": "^9.12.0", "eslint": "^9.12.0",
"eslint-plugin-format": "^0.1.2", "eslint-plugin-format": "^0.1.2",
@@ -48,7 +48,7 @@
"typescript": "^5.6.2", "typescript": "^5.6.2",
"unplugin-auto-import": "^0.18.3", "unplugin-auto-import": "^0.18.3",
"unplugin-vue-components": "^0.27.4", "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-markdown": "^0.26.2",
"unplugin-vue-router": "^0.10.8", "unplugin-vue-router": "^0.10.8",
"uuid": "^10.0.0", "uuid": "^10.0.0",
@@ -56,6 +56,6 @@
"vite-plugin-vue-devtools": "^7.4.6", "vite-plugin-vue-devtools": "^7.4.6",
"vite-plugin-vue-layouts": "^0.11.0", "vite-plugin-vue-layouts": "^0.11.0",
"vue-i18n": "^10.0.0", "vue-i18n": "^10.0.0",
"vue-tsc": "^2.1.6" "vue-tsc": "^2.1.10"
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "easytier-gui" name = "easytier-gui"
version = "2.0.1" version = "2.1.0"
description = "EasyTier GUI" description = "EasyTier GUI"
authors = ["you"] authors = ["you"]
edition = "2021" edition = "2021"
@@ -15,10 +15,11 @@ crate-type = ["staticlib", "cdylib", "rlib"]
tauri-build = { version = "2.0.0-rc", features = [] } tauri-build = { version = "2.0.0-rc", features = [] }
[dependencies] [dependencies]
tauri = { version = "2.0.0-rc", features = [ tauri = { version = "2.1", features = [
"tray-icon", "tray-icon",
"image-png", "image-png",
"image-ico", "image-ico",
"devtools",
] } ] }
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
@@ -37,13 +38,13 @@ gethostname = "0.5"
dunce = "1.0.4" dunce = "1.0.4"
tauri-plugin-shell = "2.0.0-rc" tauri-plugin-shell = "2.0"
tauri-plugin-process = "2.0.0-rc" tauri-plugin-process = "2.0"
tauri-plugin-clipboard-manager = "2.0.0-rc" tauri-plugin-clipboard-manager = "2.0"
tauri-plugin-positioner = { version = "2.0.0-rc", features = ["tray-icon"] } tauri-plugin-positioner = { version = "2.0", features = ["tray-icon"] }
tauri-plugin-vpnservice = { path = "../../tauri-plugin-vpnservice" } tauri-plugin-vpnservice = { path = "../../tauri-plugin-vpnservice" }
tauri-plugin-os = "2.0.0-rc" tauri-plugin-os = "2.0"
tauri-plugin-autostart = "2.0.0-rc" tauri-plugin-autostart = "2.0"
[features] [features]

Binary file not shown.

View File

@@ -1,4 +1,5 @@
{ {
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "migrated", "identifier": "migrated",
"description": "permissions that were migrated from v1", "description": "permissions that were migrated from v1",
"local": true, "local": true,
@@ -13,6 +14,7 @@
"core:window:allow-show", "core:window:allow-show",
"core:window:allow-hide", "core:window:allow-hide",
"core:window:allow-set-focus", "core:window:allow-set-focus",
"core:window:allow-set-title",
"core:app:default", "core:app:default",
"core:resources:default", "core:resources:default",
"core:menu:default", "core:menu:default",
@@ -24,7 +26,6 @@
"shell:default", "shell:default",
"process:default", "process:default",
"clipboard-manager:default", "clipboard-manager:default",
"core:tray:default",
"core:tray:allow-new", "core:tray:allow-new",
"core:tray:allow-set-menu", "core:tray:allow-set-menu",
"core:tray:allow-set-title", "core:tray:allow-set-title",

View File

@@ -3,174 +3,20 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use anyhow::Context;
use dashmap::DashMap; use dashmap::DashMap;
use easytier::{ use easytier::{
common::config::{ common::config::{ConfigLoader, FileLoggerConfig, TomlConfigLoader},
ConfigLoader, FileLoggerConfig, Flags, NetworkIdentity, PeerConfig, TomlConfigLoader, launcher::{NetworkConfig, NetworkInstance, NetworkInstanceRunningInfo},
VpnPortalConfig,
},
launcher::{NetworkInstance, NetworkInstanceRunningInfo},
utils::{self, NewFilterSender}, utils::{self, NewFilterSender},
}; };
use serde::{Deserialize, Serialize};
use tauri::Manager as _; use tauri::Manager as _;
pub const AUTOSTART_ARG: &str = "--autostart"; 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"))] #[cfg(not(target_os = "android"))]
use tauri::tray::{MouseButton, MouseButtonState, TrayIconBuilder, TrayIconEvent}; use tauri::tray::{MouseButton, MouseButtonState, TrayIconBuilder, TrayIconEvent};
#[derive(Deserialize, Serialize, Debug, Default)]
struct NetworkConfig {
instance_id: String,
dhcp: bool,
virtual_ipv4: String,
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 {
cfg.set_ipv4(Some(self.virtual_ipv4.parse().with_context(|| {
format!("failed to parse ipv4 address: {}", self.virtual_ipv4)
})?))
}
}
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>> = static INSTANCE_MAP: once_cell::sync::Lazy<DashMap<String, NetworkInstance>> =
once_cell::sync::Lazy::new(DashMap::new); once_cell::sync::Lazy::new(DashMap::new);
@@ -198,10 +44,10 @@ fn parse_network_config(cfg: NetworkConfig) -> Result<String, String> {
#[tauri::command] #[tauri::command]
fn run_network_instance(cfg: NetworkConfig) -> Result<(), String> { fn run_network_instance(cfg: NetworkConfig) -> Result<(), String> {
if INSTANCE_MAP.contains_key(&cfg.instance_id) { if INSTANCE_MAP.contains_key(cfg.instance_id()) {
return Err("instance already exists".to_string()); 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 cfg = cfg.gen_config().map_err(|e| e.to_string())?;
let mut instance = NetworkInstance::new(cfg); let mut instance = NetworkInstance::new(cfg);
@@ -328,7 +174,7 @@ pub fn run() {
.plugin(tauri_plugin_shell::init()) .plugin(tauri_plugin_shell::init())
.plugin(tauri_plugin_vpnservice::init()); .plugin(tauri_plugin_vpnservice::init());
builder let app = builder
.setup(|app| { .setup(|app| {
// for logging config // for logging config
let Ok(log_dir) = app.path().app_log_dir() else { let Ok(log_dir) = app.path().app_log_dir() else {
@@ -387,6 +233,20 @@ pub fn run() {
} }
_ => {} _ => {}
}) })
.run(tauri::generate_context!()) .build(tauri::generate_context!())
.expect("error while running tauri application"); .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);
}
_ => {}
});
}
} }

View File

@@ -17,7 +17,7 @@
"createUpdaterArtifacts": false "createUpdaterArtifacts": false
}, },
"productName": "easytier-gui", "productName": "easytier-gui",
"version": "2.0.1", "version": "2.1.0",
"identifier": "com.kkrainbow.easytier", "identifier": "com.kkrainbow.easytier",
"plugins": {}, "plugins": {},
"app": { "app": {

Binary file not shown.

View File

@@ -1,3 +1,12 @@
<script setup lang="ts">
import { getCurrentWindow } from '@tauri-apps/api/window'
import pkg from '~/../package.json'
onBeforeMount(async () => {
await getCurrentWindow().setTitle(`Easytier GUI: v${pkg.version}`)
})
</script>
<template> <template>
<RouterView /> <RouterView />
</template> </template>

View File

@@ -21,6 +21,7 @@ declare global {
const definePage: typeof import('unplugin-vue-router/runtime')['definePage'] const definePage: typeof import('unplugin-vue-router/runtime')['definePage']
const defineStore: typeof import('pinia')['defineStore'] const defineStore: typeof import('pinia')['defineStore']
const effectScope: typeof import('vue')['effectScope'] const effectScope: typeof import('vue')['effectScope']
const event2human: typeof import('./composables/utils')['event2human']
const generateMenuItem: typeof import('./composables/tray')['generateMenuItem'] const generateMenuItem: typeof import('./composables/tray')['generateMenuItem']
const getActivePinia: typeof import('pinia')['getActivePinia'] const getActivePinia: typeof import('pinia')['getActivePinia']
const getCurrentInstance: typeof import('vue')['getCurrentInstance'] const getCurrentInstance: typeof import('vue')['getCurrentInstance']
@@ -44,6 +45,8 @@ declare global {
const mapWritableState: typeof import('pinia')['mapWritableState'] const mapWritableState: typeof import('pinia')['mapWritableState']
const markRaw: typeof import('vue')['markRaw'] const markRaw: typeof import('vue')['markRaw']
const nextTick: typeof import('vue')['nextTick'] const nextTick: typeof import('vue')['nextTick']
const num2ipv4: typeof import('./composables/utils')['num2ipv4']
const num2ipv6: typeof import('./composables/utils')['num2ipv6']
const onActivated: typeof import('vue')['onActivated'] const onActivated: typeof import('vue')['onActivated']
const onBeforeMount: typeof import('vue')['onBeforeMount'] const onBeforeMount: typeof import('vue')['onBeforeMount']
const onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave'] const onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave']
@@ -81,6 +84,7 @@ declare global {
const shallowReadonly: typeof import('vue')['shallowReadonly'] const shallowReadonly: typeof import('vue')['shallowReadonly']
const shallowRef: typeof import('vue')['shallowRef'] const shallowRef: typeof import('vue')['shallowRef']
const storeToRefs: typeof import('pinia')['storeToRefs'] const storeToRefs: typeof import('pinia')['storeToRefs']
const timeAgoCn: typeof import('./composables/utils')['timeAgoCn']
const toRaw: typeof import('vue')['toRaw'] const toRaw: typeof import('vue')['toRaw']
const toRef: typeof import('vue')['toRef'] const toRef: typeof import('vue')['toRef']
const toRefs: typeof import('vue')['toRefs'] const toRefs: typeof import('vue')['toRefs']

View File

@@ -1,308 +0,0 @@
<script setup lang="ts">
import InputGroup from 'primevue/inputgroup'
import InputGroupAddon from 'primevue/inputgroupaddon'
import { ping } from 'tauri-plugin-vpnservice-api'
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 {
new URL(query)
ret.push(query)
}
catch (e) {}
}
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 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()
osHostname.value = await ping('ffdklsajflkdsjl') || ''
})
</script>
<template>
<div class="flex flex-column h-full">
<div class="flex flex-column">
<div class="w-10/12 self-center mb-3">
<Message severity="warn">
{{ t('dhcp_experimental_warning') }}
</Message>
</div>
<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>/24</span>
</InputGroupAddon>
</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>
<Chips
id="chips" v-model="curNetwork.proxy_cidrs"
:placeholder="t('chips_placeholder', ['10.0.0.0/24'])" separator=" " class="w-full"
/>
</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>

View File

@@ -1,6 +1,9 @@
import type { NetworkTypes } from 'easytier-frontend-lib'
import { addPluginListener } from '@tauri-apps/api/core' 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 { prepare_vpn, start_vpn, stop_vpn } from 'tauri-plugin-vpnservice-api'
import type { Route } from '~/types/network'
type Route = NetworkTypes.Route
const networkStore = useNetworkStore() const networkStore = useNetworkStore()
@@ -48,7 +51,7 @@ async function doStartVpn(ipv4Addr: string, cidr: number, routes: string[]) {
console.log('start vpn') console.log('start vpn')
const start_ret = await start_vpn({ const start_ret = await start_vpn({
ipv4Addr: `${ipv4Addr}/${cidr}`, ipv4Addr: `${ipv4Addr}`,
routes, routes,
disallowedApplications: ['com.kkrainbow.easytier'], disallowedApplications: ['com.kkrainbow.easytier'],
mtu: 1300, mtu: 1300,
@@ -122,12 +125,17 @@ async function onNetworkInstanceChange() {
return 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) { if (!virtual_ip || !virtual_ip.length) {
await doStopVpn() await doStopVpn()
return return
} }
let network_length = curNetworkInfo?.my_node_info?.virtual_ipv4.network_length
if (!network_length) {
network_length = 24
}
const routes = getRoutesForVpn(curNetworkInfo?.routes) const routes = getRoutesForVpn(curNetworkInfo?.routes)
const ipChanged = virtual_ip !== curVpnStatus.ipv4Addr const ipChanged = virtual_ip !== curVpnStatus.ipv4Addr

View File

@@ -1,6 +1,8 @@
import type { NetworkTypes } from 'easytier-frontend-lib'
import { invoke } from '@tauri-apps/api/core' 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) { export async function parseNetworkConfig(cfg: NetworkConfig) {
return invoke<string>('parse_network_config', { cfg }) return invoke<string>('parse_network_config', { cfg })

View File

@@ -5,12 +5,11 @@ import ToastService from 'primevue/toastservice'
import { createRouter, createWebHistory } from 'vue-router/auto' import { createRouter, createWebHistory } from 'vue-router/auto'
import { routes } from 'vue-router/auto-routes' import { routes } from 'vue-router/auto-routes'
import App from '~/App.vue' 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 { getAutoLaunchStatusAsync, loadAutoLaunchStatusAsync } from './modules/auto_launch'
import '~/styles.css' import '~/styles.css'
import 'primeicons/primeicons.css' import 'easytier-frontend-lib/style.css'
import 'primeflex/primeflex.css'
if (import.meta.env.PROD) { if (import.meta.env.PROD) {
document.addEventListener('keydown', (event) => { document.addEventListener('keydown', (event) => {
@@ -29,7 +28,7 @@ if (import.meta.env.PROD) {
} }
async function main() { async function main() {
await loadLanguageAsync(localStorage.getItem('lang') || 'en') await I18nUtils.loadLanguageAsync(localStorage.getItem('lang') || 'en')
await loadAutoLaunchStatusAsync(getAutoLaunchStatusAsync()) await loadAutoLaunchStatusAsync(getAutoLaunchStatusAsync())
const app = createApp(App) const app = createApp(App)
@@ -41,18 +40,22 @@ async function main() {
app.use(router) app.use(router)
app.use(createPinia()) app.use(createPinia())
app.use(i18n, { useScope: 'global' }) app.use(EasyTierFrontendLib)
// app.use(i18n, { useScope: 'global' })
app.use(PrimeVue, { app.use(PrimeVue, {
theme: { theme: {
preset: Aura, preset: Aura,
options: { options: {
prefix: 'p', prefix: 'p',
darkModeSelector: 'system', darkModeSelector: 'system',
cssLayer: false, cssLayer: {
name: 'primevue',
order: 'tailwind-base, primevue, tailwind-utilities'
}
}, },
}, },
}) })
app.use(ToastService) app.use(ToastService as any)
app.mount('#app') app.mount('#app')
} }

View File

@@ -2,7 +2,16 @@ import { disable, enable, isEnabled } from '@tauri-apps/plugin-autostart'
export async function loadAutoLaunchStatusAsync(target_enable: boolean): Promise<boolean> { export async function loadAutoLaunchStatusAsync(target_enable: boolean): Promise<boolean> {
try { try {
target_enable ? await enable() : await disable() if (target_enable) {
await enable()
}
else {
// 消除没有配置自启动时进行关闭操作报错
try {
await disable()
}
catch { }
}
localStorage.setItem('auto_launch', JSON.stringify(await isEnabled())) localStorage.setItem('auto_launch', JSON.stringify(await isEnabled()))
return isEnabled() return isEnabled()
} }

View File

@@ -8,14 +8,11 @@ import { exit } from '@tauri-apps/plugin-process'
import { open } from '@tauri-apps/plugin-shell' import { open } from '@tauri-apps/plugin-shell'
import TieredMenu from 'primevue/tieredmenu' import TieredMenu from 'primevue/tieredmenu'
import { useToast } from 'primevue/usetoast' import { useToast } from 'primevue/usetoast'
import Config from '~/components/Config.vue' import { NetworkTypes, Config, Status, Utils, I18nUtils } from 'easytier-frontend-lib'
import Status from '~/components/Status.vue'
import { isAutostart, setLoggingLevel } from '~/composables/network' import { isAutostart, setLoggingLevel } from '~/composables/network'
import { useTray } from '~/composables/tray' import { useTray } from '~/composables/tray'
import { getAutoLaunchStatusAsync as getAutoLaunchStatus, loadAutoLaunchStatusAsync } from '~/modules/auto_launch' 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 { t, locale } = useI18n()
const visible = ref(false) const visible = ref(false)
@@ -65,6 +62,27 @@ const toast = useToast()
const networkStore = useNetworkStore() 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() { function addNewNetwork() {
networkStore.addNewNetwork() networkStore.addNewNetwork()
networkStore.curNetwork = networkStore.lastNetwork 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') { if (type() === 'android') {
await prepareVpnService() await prepareVpnService()
networkStore.clearNetworkInstances() networkStore.clearNetworkInstances()
@@ -106,7 +124,7 @@ async function runNetworkCb(cfg: NetworkConfig, cb: () => void) {
cb() cb()
} }
async function stopNetworkCb(cfg: NetworkConfig, cb: () => void) { async function stopNetworkCb(cfg: NetworkTypes.NetworkConfig, cb: () => void) {
// console.log('stopNetworkCb', cfg, cb) // console.log('stopNetworkCb', cfg, cb)
cb() cb()
networkStore.removeNetworkInstance(cfg.instance_id) networkStore.removeNetworkInstance(cfg.instance_id)
@@ -145,7 +163,7 @@ const setting_menu_items = ref([
label: () => t('exchange_language'), label: () => t('exchange_language'),
icon: 'pi pi-language', icon: 'pi pi-language',
command: async () => { command: async () => {
await loadLanguageAsync((locale.value === 'en' ? 'cn' : 'en')) await I18nUtils.loadLanguageAsync((locale.value === 'en' ? 'cn' : 'en'))
await setTrayMenu([ await setTrayMenu([
await MenuItemExit(t('tray.exit')), await MenuItemExit(t('tray.exit')),
await MenuItemShow(t('tray.show')), await MenuItemShow(t('tray.show')),
@@ -181,7 +199,7 @@ const setting_menu_items = ref([
label: () => t('logging_open_dir'), label: () => t('logging_open_dir'),
icon: 'pi pi-folder-open', icon: 'pi pi-folder-open',
command: async () => { command: async () => {
console.log('open log dir', await appLogDir()) // console.log('open log dir', await appLogDir())
await open(await appLogDir()) await open(await appLogDir())
}, },
}) })
@@ -221,7 +239,7 @@ onBeforeMount(async () => {
getCurrentWindow().hide() getCurrentWindow().hide()
const autoStartIds = networkStore.autoStartInstIds const autoStartIds = networkStore.autoStartInstIds
for (const id of autoStartIds) { 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) { if (cfg) {
networkStore.addNetworkInstance(cfg.instance_id) networkStore.addNetworkInstance(cfg.instance_id)
await runNetworkInstance(cfg) await runNetworkInstance(cfg)
@@ -245,7 +263,7 @@ function isRunning(id: string) {
</script> </script>
<template> <template>
<div id="root" class="flex flex-column"> <div id="root" class="flex flex-col">
<Dialog v-model:visible="visible" modal header="Config File" :style="{ width: '70%' }"> <Dialog v-model:visible="visible" modal header="Config File" :style="{ width: '70%' }">
<Panel> <Panel>
<ScrollPanel style="width: 100%; height: 300px"> <ScrollPanel style="width: 100%; height: 300px">
@@ -253,7 +271,7 @@ function isRunning(id: string) {
</ScrollPanel> </ScrollPanel>
</Panel> </Panel>
<Divider /> <Divider />
<div class="flex gap-2 justify-content-end"> <div class="flex gap-2 justify-end">
<Button type="button" :label="t('close')" @click="visible = false" /> <Button type="button" :label="t('close')" @click="visible = false" />
</div> </div>
</Dialog> </Dialog>
@@ -265,65 +283,55 @@ function isRunning(id: string) {
<div> <div>
<Toolbar> <Toolbar>
<template #start> <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" /> <Button icon="pi pi-plus" severity="primary" :label="t('add_new_network')" @click="addNewNetwork" />
</div> </div>
</template> </template>
<template #center> <template #center>
<div class="min-w-40"> <div class="min-w-40">
<Dropdown <Select v-model="networkStore.curNetwork" :options="networkStore.networkList" :highlight-on-select="false"
v-model="networkStore.curNetwork" :options="networkStore.networkList" :highlight-on-select="false" :placeholder="t('select_network')" class="w-full">
:placeholder="t('select_network')" class="w-full"
>
<template #value="slotProps"> <template #value="slotProps">
<div class="flex items-start content-center"> <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> <span>{{ slotProps.value.network_name }}</span>
</div> </div>
<Tag <Tag class="my-auto leading-3" :severity="isRunning(slotProps.value.instance_id) ? 'success' : 'info'"
class="my-auto leading-3" :severity="isRunning(slotProps.value.instance_id) ? 'success' : 'info'" :value="t(isRunning(slotProps.value.instance_id) ? 'network_running' : 'network_stopped')" />
:value="t(isRunning(slotProps.value.instance_id) ? 'network_running' : 'network_stopped')"
/>
</div> </div>
</template> </template>
<template #option="slotProps"> <template #option="slotProps">
<div class="flex flex-col items-start content-center max-w-full"> <div class="flex flex-col items-start content-center max-w-full">
<div class="flex"> <div class="flex">
<div class="mr-3"> <div class="mr-4">
{{ t('network_name') }}: {{ slotProps.option.network_name }} {{ t('network_name') }}: {{ slotProps.option.network_name }}
</div> </div>
<Tag <Tag class="my-auto leading-3"
class="my-auto leading-3"
:severity="isRunning(slotProps.option.instance_id) ? 'success' : 'info'" :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>
<div <div v-if="slotProps.option.networking_method !== NetworkTypes.NetworkingMethod.Standalone"
v-if="slotProps.option.networking_method !== NetworkingMethod.Standalone" class="max-w-full overflow-hidden text-ellipsis">
class="max-w-full overflow-hidden text-ellipsis" {{ slotProps.option.networking_method === NetworkTypes.NetworkingMethod.Manual
>
{{ slotProps.option.networking_method === NetworkingMethod.Manual
? slotProps.option.peer_urls.join(', ') ? slotProps.option.peer_urls.join(', ')
: slotProps.option.public_server_url }} : slotProps.option.public_server_url }}
</div> </div>
<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 !== '')" 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 Utils.ipv4InetToString(networkStore.instances[slotProps.option.instance_id].detail?.my_node_info.virtual_ipv4)
? networkStore.instances[slotProps.option.instance_id].detail?.my_node_info.virtual_ipv4 : '' }} }}
</div> </div>
</div> </div>
</template> </template>
</Dropdown> </Select>
</div> </div>
</template> </template>
<template #end> <template #end>
<Button <Button icon="pi pi-cog" severity="secondary" aria-haspopup="true" :label="t('settings')"
icon="pi pi-cog" severity="secondary" aria-haspopup="true" :label="t('settings')" aria-controls="overlay_setting_menu" @click="toggle_setting_menu" />
aria-controls="overlay_setting_menu" @click="toggle_setting_menu"
/>
<TieredMenu id="overlay_setting_menu" ref="setting_menu" :model="setting_menu_items" :popup="true" /> <TieredMenu id="overlay_setting_menu" ref="setting_menu" :model="setting_menu_items" :popup="true" />
</template> </template>
</Toolbar> </Toolbar>
@@ -341,20 +349,16 @@ function isRunning(id: string) {
</StepList> </StepList>
<StepPanels value="1"> <StepPanels value="1">
<StepPanel v-slot="{ activateCallback = (s: string) => { } } = {}" value="1"> <StepPanel v-slot="{ activateCallback = (s: string) => { } } = {}" value="1">
<Config <Config :instance-id="networkStore.curNetworkId" :config-invalid="messageBarSeverity !== Severity.None"
:instance-id="networkStore.curNetworkId" :config-invalid="messageBarSeverity !== Severity.None" :cur-network="curNetworkConfig" @run-network="runNetworkCb($event, () => activateCallback('2'))" />
@run-network="runNetworkCb($event, () => activateCallback('2'))"
/>
</StepPanel> </StepPanel>
<StepPanel v-slot="{ activateCallback = (s: string) => { } } = {}" value="2"> <StepPanel v-slot="{ activateCallback = (s: string) => { } } = {}" value="2">
<div class="flex flex-column"> <div class="flex flex-col">
<Status :instance-id="networkStore.curNetworkId" /> <Status :cur-network-inst="curNetworkInst" />
</div> </div>
<div class="flex pt-4 justify-content-center"> <div class="flex pt-6 justify-center">
<Button <Button :label="t('stop_network')" severity="danger" icon="pi pi-arrow-left"
:label="t('stop_network')" severity="danger" icon="pi pi-arrow-left" @click="stopNetworkCb(networkStore.curNetwork, () => activateCallback('1'))" />
@click="stopNetworkCb(networkStore.curNetwork, () => activateCallback('1'))"
/>
</div> </div>
</StepPanel> </StepPanel>
</StepPanels> </StepPanels>

View File

@@ -1,26 +1,25 @@
import type { NetworkConfig, NetworkInstance, NetworkInstanceRunningInfo } from '~/types/network' import { NetworkTypes } from 'easytier-frontend-lib'
import { DEFAULT_NETWORK_CONFIG } from '~/types/network'
export const useNetworkStore = defineStore('networkStore', { export const useNetworkStore = defineStore('networkStore', {
state: () => { state: () => {
const networkList = [DEFAULT_NETWORK_CONFIG()] const networkList = [NetworkTypes.DEFAULT_NETWORK_CONFIG()]
return { return {
// for initially empty lists // for initially empty lists
networkList: networkList as NetworkConfig[], networkList: networkList as NetworkTypes.NetworkConfig[],
// for data that is not yet loaded // for data that is not yet loaded
curNetwork: networkList[0], curNetwork: networkList[0],
// uuid -> instance // 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[], autoStartInstIds: [] as string[],
} }
}, },
getters: { getters: {
lastNetwork(): NetworkConfig { lastNetwork(): NetworkTypes.NetworkConfig {
return this.networkList[this.networkList.length - 1] return this.networkList[this.networkList.length - 1]
}, },
@@ -28,7 +27,7 @@ export const useNetworkStore = defineStore('networkStore', {
return this.curNetwork.instance_id return this.curNetwork.instance_id
}, },
networkInstances(): Array<NetworkInstance> { networkInstances(): Array<NetworkTypes.NetworkInstance> {
return Object.values(this.instances) return Object.values(this.instances)
}, },
@@ -39,7 +38,7 @@ export const useNetworkStore = defineStore('networkStore', {
actions: { actions: {
addNewNetwork() { addNewNetwork() {
this.networkList.push(DEFAULT_NETWORK_CONFIG()) this.networkList.push(NetworkTypes.DEFAULT_NETWORK_CONFIG())
}, },
delCurNetwork() { delCurNetwork() {
@@ -66,7 +65,7 @@ export const useNetworkStore = defineStore('networkStore', {
this.instances = {} this.instances = {}
}, },
updateWithNetworkInfos(networkInfos: Record<string, NetworkInstanceRunningInfo>) { updateWithNetworkInfos(networkInfos: Record<string, NetworkTypes.NetworkInstanceRunningInfo>) {
this.networkInfos = networkInfos this.networkInfos = networkInfos
for (const [instanceId, info] of Object.entries(networkInfos)) { for (const [instanceId, info] of Object.entries(networkInfos)) {
if (this.instances[instanceId] === undefined) if (this.instances[instanceId] === undefined)
@@ -79,17 +78,17 @@ export const useNetworkStore = defineStore('networkStore', {
}, },
loadFromLocalStorage() { loadFromLocalStorage() {
let networkList: NetworkConfig[] let networkList: NetworkTypes.NetworkConfig[]
// if localStorage default is [{}], instanceId will be undefined // if localStorage default is [{}], instanceId will be undefined
networkList = JSON.parse(localStorage.getItem('networkList') || '[]') networkList = JSON.parse(localStorage.getItem('networkList') || '[]')
networkList = networkList.map((cfg) => { 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 // prevent a empty list from localStorage, should not happen
if (networkList.length === 0) if (networkList.length === 0)
networkList = [DEFAULT_NETWORK_CONFIG()] networkList = [NetworkTypes.DEFAULT_NETWORK_CONFIG()]
this.networkList = networkList this.networkList = networkList
this.curNetwork = this.networkList[0] this.curNetwork = this.networkList[0]

View 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.77.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
View File

@@ -0,0 +1 @@
../LICENSE

View File

@@ -0,0 +1,3 @@
# Introduction
This is a protobuf rpc service stub generator for [EasyTier](https://github.com/EasyTier/EasyTier) project.

View File

@@ -3,8 +3,12 @@ extern crate prost_build;
use std::fmt; use std::fmt;
#[cfg(feature = "internal-namespace")]
const NAMESPACE: &str = "crate::proto::rpc_types"; 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 /// The service generator to be used with `prost-build` to generate RPC implementations for
/// `prost-simple-rpc`. /// `prost-simple-rpc`.
/// ///

52
easytier-web/Cargo.toml Normal file
View File

@@ -0,0 +1,52 @@
[package]
name = "easytier-web"
version = "0.1.0"
edition = "2021"
[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"
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"
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",
] }

24
easytier-web/frontend-lib/.gitignore vendored Normal file
View 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?

View 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).

View 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>

View 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.2.1",
"@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.2.1",
"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.14",
"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
View 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

View File

@@ -0,0 +1,7 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
"postcss-nested": {},
},
}

View 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

View 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

View File

@@ -0,0 +1,275 @@
<script setup lang="ts">
import InputGroup from 'primevue/inputgroup'
import InputGroupAddon from 'primevue/inputgroupaddon'
import { SelectButton, Checkbox, InputText, InputNumber, AutoComplete, Panel, Divider, ToggleButton, Button } 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
}
</script>
<template>
<div class="frontend-lib">
<div class="flex flex-col h-full">
<div class="flex flex-col">
<div class="w-10/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>
<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-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">
<div class="flex 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-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="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/12" fluid />
</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">
<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>
</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>

View File

@@ -0,0 +1,35 @@
<script setup lang="ts">
import { useI18n } from 'vue-i18n';
import { EventType } from '../types/network'
import { computed } from 'vue';
import { Fieldset } from 'primevue';
const props = defineProps<{
event: {
[key: string]: any
}
}>()
const { t } = useI18n()
const eventKey = computed(() => {
const key = Object.keys(props.event)[0]
return Object.keys(EventType).includes(key) ? key : 'Unknown'
})
const eventValue = computed(() => {
const value = props.event[eventKey.value]
return typeof value === 'object' ? value : value
})
</script>
<template>
<Fieldset :legend="t(`event.${eventKey}`)">
<template v-if="eventKey !== 'Unknown'">
<div v-if="event.DhcpIpv4Changed">
{{ `${eventValue[0]} -> ${eventValue[1]}` }}
</div>
<pre v-else>{{ eventValue }}</pre>
</template>
<pre v-else>{{ eventValue }}</pre>
</Fieldset>
</template>

View File

@@ -1,40 +1,28 @@
<script setup lang="ts"> <script setup lang="ts">
import { IPv4, IPv6 } from 'ip-num/IPNumber' import { useTimeAgo } from '@vueuse/core'
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<{ const props = defineProps<{
instanceId?: string curNetworkInst: NetworkInstance | null,
}>() }>()
const { t } = useI18n() 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(() => { const peerRouteInfos = computed(() => {
if (curNetworkInst.value) { if (props.curNetworkInst) {
const my_node_info = curNetworkInst.value.detail?.my_node_info const my_node_info = props.curNetworkInst.detail?.my_node_info
return [{ return [{
route: { route: {
ipv4_addr: my_node_info?.virtual_ipv4, ipv4_addr: my_node_info?.virtual_ipv4,
hostname: my_node_info?.hostname, hostname: my_node_info?.hostname,
version: my_node_info?.version, version: my_node_info?.version,
}, },
}, ...(curNetworkInst.value.detail?.peer_route_pairs || [])] }, ...(props.curNetworkInst.detail?.peer_route_pairs || [])]
} }
return [] return []
@@ -111,11 +99,18 @@ function version(info: PeerRoutePair) {
return info.route.version === '' ? 'unknown' : info.route.version return info.route.version === '' ? 'unknown' : info.route.version
} }
function ipFormat(info: PeerRoutePair) {
const ip = info.route.ipv4_addr
if (typeof ip === 'string')
return ip
return ip ? `${IPv4.fromNumber(ip.address.addr)}/${ip.network_length}` : ''
}
const myNodeInfo = computed(() => { const myNodeInfo = computed(() => {
if (!curNetworkInst.value) if (!props.curNetworkInst)
return {} as NodeInfo return {} as NodeInfo
return curNetworkInst.value.detail?.my_node_info return props.curNetworkInst.detail?.my_node_info
}) })
interface Chip { interface Chip {
@@ -124,16 +119,16 @@ interface Chip {
} }
const myNodeInfoChips = computed(() => { const myNodeInfoChips = computed(() => {
if (!curNetworkInst.value) if (!props.curNetworkInst)
return [] return []
const chips: Array<Chip> = [] 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) if (!my_node_info)
return chips return chips
// TUN Device Name // TUN Device Name
const dev_name = curNetworkInst.value.detail?.dev_name const dev_name = props.curNetworkInst.detail?.dev_name
if (dev_name) { if (dev_name) {
chips.push({ chips.push({
label: `TUN Device Name: ${dev_name}`, label: `TUN Device Name: ${dev_name}`,
@@ -143,7 +138,7 @@ const myNodeInfoChips = computed(() => {
// virtual ipv4 // virtual ipv4
chips.push({ chips.push({
label: `Virtual IPv4: ${my_node_info.virtual_ipv4}`, label: `Virtual IPv4: ${ipv4InetToString(my_node_info.virtual_ipv4)}`,
icon: '', icon: '',
} as Chip) } as Chip)
@@ -151,7 +146,7 @@ const myNodeInfoChips = computed(() => {
const local_ipv4s = my_node_info.ips?.interface_ipv4s const local_ipv4s = my_node_info.ips?.interface_ipv4s
for (const [idx, ip] of local_ipv4s?.entries()) { for (const [idx, ip] of local_ipv4s?.entries()) {
chips.push({ chips.push({
label: `Local IPv4 ${idx}: ${IPv4.fromNumber(ip.addr)}`, label: `Local IPv4 ${idx}: ${ipv4ToString(ip)}`,
icon: '', icon: '',
} as Chip) } as Chip)
} }
@@ -160,11 +155,7 @@ const myNodeInfoChips = computed(() => {
const local_ipv6s = my_node_info.ips?.interface_ipv6s const local_ipv6s = my_node_info.ips?.interface_ipv6s
for (const [idx, ip] of local_ipv6s?.entries()) { for (const [idx, ip] of local_ipv6s?.entries()) {
chips.push({ chips.push({
label: `Local IPv6 ${idx}: ${IPv6.fromBigInt((BigInt(ip.part1) << BigInt(96)) label: `Local IPv6 ${idx}: ${ipv6ToString(ip)}`,
+ (BigInt(ip.part2) << BigInt(64))
+ (BigInt(ip.part3) << BigInt(32))
+ BigInt(ip.part4),
)}`,
icon: '', icon: '',
} as Chip) } as Chip)
} }
@@ -181,11 +172,7 @@ const myNodeInfoChips = computed(() => {
const public_ipv6 = my_node_info.ips?.public_ipv6 const public_ipv6 = my_node_info.ips?.public_ipv6
if (public_ipv6) { if (public_ipv6) {
chips.push({ chips.push({
label: `Public IPv6: ${IPv6.fromBigInt((BigInt(public_ipv6.part1) << BigInt(96)) label: `Public IPv6: ${ipv6ToString(public_ipv6)}`,
+ (BigInt(public_ipv6.part2) << BigInt(64))
+ (BigInt(public_ipv6.part3) << BigInt(32))
+ BigInt(public_ipv6.part4),
)}`,
icon: '', icon: '',
} as Chip) } as Chip)
} }
@@ -194,7 +181,7 @@ const myNodeInfoChips = computed(() => {
const listeners = my_node_info.listeners const listeners = my_node_info.listeners
for (const [idx, listener] of listeners?.entries()) { for (const [idx, listener] of listeners?.entries()) {
chips.push({ chips.push({
label: `Listener ${idx}: ${listener}`, label: `Listener ${idx}: ${listener.url}`,
icon: '', icon: '',
} as Chip) } as Chip)
} }
@@ -210,6 +197,8 @@ const myNodeInfoChips = computed(() => {
PortRestricted = 5, PortRestricted = 5,
Symmetric = 6, Symmetric = 6,
SymUdpFirewall = 7, SymUdpFirewall = 7,
SymmetricEasyInc = 8,
SymmetricEasyDec = 9,
}; };
const udpNatType: NatType = my_node_info.stun_info?.udp_nat_type const udpNatType: NatType = my_node_info.stun_info?.udp_nat_type
if (udpNatType !== undefined) { if (udpNatType !== undefined) {
@@ -222,6 +211,8 @@ const myNodeInfoChips = computed(() => {
[NatType.PortRestricted]: 'Port Restricted', [NatType.PortRestricted]: 'Port Restricted',
[NatType.Symmetric]: 'Symmetric', [NatType.Symmetric]: 'Symmetric',
[NatType.SymUdpFirewall]: 'Symmetric UDP Firewall', [NatType.SymUdpFirewall]: 'Symmetric UDP Firewall',
[NatType.SymmetricEasyInc]: 'Symmetric Easy Inc',
[NatType.SymmetricEasyDec]: 'Symmetric Easy Dec',
} }
chips.push({ chips.push({
@@ -300,28 +291,31 @@ function showVpnPortalConfig() {
} }
function showEventLogs() { function showEventLogs() {
const detail = curNetworkInst.value?.detail const detail = props.curNetworkInst?.detail
if (!detail) if (!detail)
return return
dialogContent.value = detail.events dialogContent.value = detail.events.map((event: string) => JSON.parse(event))
dialogHeader.value = 'event_log' dialogHeader.value = 'event_log'
dialogVisible.value = true dialogVisible.value = true
} }
</script> </script>
<template> <template>
<div> <div class="frontend-lib">
<Dialog v-model:visible="dialogVisible" modal :header="t(dialogHeader)" :style="{ width: '70%' }"> <Dialog v-model:visible="dialogVisible" modal :header="t(dialogHeader)" class="w-2/3 h-auto">
<Panel> <ScrollPanel v-if="dialogHeader === 'vpn_portal_config'">
<ScrollPanel style="width: 100%; height: 400px"> <pre>{{ dialogContent }}</pre>
<pre>{{ dialogContent }}</pre> </ScrollPanel>
</ScrollPanel> <Timeline v-else :value="dialogContent">
</Panel> <template #opposite="slotProps">
<Divider /> <small class="text-surface-500 dark:text-surface-400">{{ useTimeAgo(Date.parse(slotProps.item.time))
<div class="flex justify-content-end gap-2"> }}</small>
<Button type="button" :label="t('close')" @click="dialogVisible = false" /> </template>
</div> <template #content="slotProps">
<HumanEvent :event="slotProps.item.event" />
</template>
</Timeline>
</Dialog> </Dialog>
<Card v-if="curNetworkInst?.error_msg"> <Card v-if="curNetworkInst?.error_msg">
@@ -329,7 +323,7 @@ function showEventLogs() {
Run Network Error Run Network Error
</template> </template>
<template #content> <template #content>
<div class="flex flex-column gap-y-5"> <div class="flex flex-col gap-y-5">
<div class="text-red-500"> <div class="text-red-500">
{{ curNetworkInst.error_msg }} {{ curNetworkInst.error_msg }}
</div> </div>
@@ -343,12 +337,9 @@ function showEventLogs() {
{{ t('my_node_info') }} {{ t('my_node_info') }}
</template> </template>
<template #content> <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="m-0 flex flex-row justify-center gap-x-5">
<div <div class="rounded-full w-32 h-32 flex flex-col items-center pt-6" style="border: 1px solid green">
class="rounded-full w-32 h-32 flex flex-column align-items-center pt-4"
style="border: 1px solid green"
>
<div class="font-bold"> <div class="font-bold">
{{ t('peer_count') }} {{ t('peer_count') }}
</div> </div>
@@ -357,10 +348,7 @@ function showEventLogs() {
</div> </div>
</div> </div>
<div <div class="rounded-full w-32 h-32 flex flex-col items-center pt-6" style="border: 1px solid purple">
class="rounded-full w-32 h-32 flex flex-column align-items-center pt-4"
style="border: 1px solid purple"
>
<div class="font-bold"> <div class="font-bold">
{{ t('upload') }} {{ t('upload') }}
</div> </div>
@@ -369,10 +357,7 @@ function showEventLogs() {
</div> </div>
</div> </div>
<div <div class="rounded-full w-32 h-32 flex flex-col items-center pt-6" style="border: 1px solid fuchsia">
class="rounded-full w-32 h-32 flex flex-column align-items-center pt-4"
style="border: 1px solid fuchsia"
>
<div class="font-bold"> <div class="font-bold">
{{ t('download') }} {{ t('download') }}
</div> </div>
@@ -382,11 +367,9 @@ function showEventLogs() {
</div> </div>
</div> </div>
<div class="flex flex-row align-items-center flex-wrap w-full max-h-40 overflow-scroll"> <div class="flex flex-row items-center flex-wrap w-full max-h-40 overflow-scroll">
<Chip <Chip v-for="(chip, i) in myNodeInfoChips" :key="i" :label="chip.label" :icon="chip.icon"
v-for="(chip, i) in myNodeInfoChips" :key="i" :label="chip.label" :icon="chip.icon" class="mr-2 mt-2 text-sm" />
class="mr-2 mt-2 text-sm"
/>
</div> </div>
<div v-if="myNodeInfo" class="m-0 flex flex-row justify-center gap-x-5 text-sm"> <div v-if="myNodeInfo" class="m-0 flex flex-row justify-center gap-x-5 text-sm">
@@ -404,18 +387,44 @@ function showEventLogs() {
{{ t('peer_info') }} {{ t('peer_info') }}
</template> </template>
<template #content> <template #content>
<DataTable :value="peerRouteInfos" column-resize-mode="fit" table-style="width: 100%"> <DataTable :value="peerRouteInfos" column-resize-mode="fit" table-class="w-full">
<Column field="route.ipv4_addr" style="width: 100px;" :header="t('virtual_ipv4')" /> <Column :field="ipFormat" :header="t('virtual_ipv4')" />
<Column field="route.hostname" style="max-width: 250px;" :header="t('hostname')" /> <Column :header="t('hostname')">
<Column :field="routeCost" style="width: 100px;" :header="t('route_cost')" /> <template #body="slotProps">
<Column :field="latencyMs" style="width: 80px;" :header="t('latency')" /> <div v-if="!slotProps.data.route.cost || !slotProps.data.route.feature_flag.is_public_server"
<Column :field="txBytes" style="width: 80px;" :header="t('upload_bytes')" /> v-tooltip="slotProps.data.route.hostname">
<Column :field="rxBytes" style="width: 80px;" :header="t('download_bytes')" /> {{
<Column :field="lossRate" style="width: 100px;" :header="t('loss_rate')" /> slotProps.data.route.hostname }}
<Column :field="version" style="width: 100px;" :header="t('status.version')" /> </div>
<div v-else v-tooltip="slotProps.data.route.hostname" class="space-x-1">
<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.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="latencyMs" :header="t('latency')" />
<Column :field="txBytes" :header="t('upload_bytes')" />
<Column :field="rxBytes" :header="t('download_bytes')" />
<Column :field="lossRate" :header="t('loss_rate')" />
<Column :header="t('status.version')">
<template #body="slotProps">
<span>{{ version(slotProps.data) }}</span>
</template>
</Column>
</DataTable> </DataTable>
</template> </template>
</Card> </Card>
</template> </template>
</div> </div>
</template> </template>
<style lang="postcss" scoped>
.p-timeline :deep(.p-timeline-event-opposite) {
@apply flex-none;
}
</style>

View File

@@ -0,0 +1,2 @@
export { default as Config } from './Config.vue';
export { default as Status } from './Status.vue';

View File

@@ -0,0 +1,50 @@
import './style.css'
import type { App } from 'vue';
import { Config, Status } 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('Status', Status);
app.component('HumanEvent', HumanEvent);
app.directive('tooltip', vTooltip as any);
}
};
export { Config, Status, I18nUtils, NetworkTypes, Api, Utils };

View File

@@ -0,0 +1,115 @@
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端口
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: 显示配置
close: 关闭
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: 路由
latency: 延迟
upload_bytes: 上传
download_bytes: 下载
loss_rate: 丢包率
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地址冲突

View File

@@ -0,0 +1,114 @@
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
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
close: Close
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
hostname: Hostname
latency: Latency
upload_bytes: Upload
download_bytes: Download
loss_rate: Loss Rate
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

View File

@@ -0,0 +1,181 @@
import axios, { AxiosError, AxiosInstance, AxiosResponse, InternalAxiosRequestConfig } from 'axios';
import { Md5 } from 'ts-md5'
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 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 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';
}
}
export default ApiClient;

View File

@@ -1,6 +1,9 @@
import { createI18n } from 'vue-i18n' import { createI18n } from 'vue-i18n'
import type { Locale } from 'vue-i18n' import type { Locale } from 'vue-i18n'
import EnLocale from '../locales/en.yaml'
import CnLocale from '../locales/cn.yaml'
// Import i18n resources // Import i18n resources
// https://vitejs.dev/guide/features.html#glob-import // https://vitejs.dev/guide/features.html#glob-import
export const i18n = createI18n({ export const i18n = createI18n({
@@ -10,10 +13,10 @@ export const i18n = createI18n({
messages: {}, messages: {},
}) })
const localesMap = Object.fromEntries( const localesMap = {
Object.entries(import.meta.glob('../../locales/*.yml')) "en": EnLocale,
.map(([path, loadLocale]) => [path.match(/([\w-]*)\.yml$/)?.[1], loadLocale]), "cn": CnLocale,
) as Record<Locale, () => Promise<{ default: Record<string, string> }>> } as Record<string, any>
export const availableLocales = Object.keys(localesMap) export const availableLocales = Object.keys(localesMap)
@@ -38,13 +41,19 @@ export async function loadLanguageAsync(lang: string): Promise<Locale> {
let messages let messages
try { try {
messages = await localesMap[lang]() messages = localesMap[lang]
} }
catch { catch {
messages = await localesMap.en() messages = localesMap.en
} }
i18n.global.setLocaleMessage(lang, messages.default) i18n.global.setLocaleMessage(lang, messages)
loadedLanguages.push(lang) loadedLanguages.push(lang)
return setI18nLanguage(lang) return setI18nLanguage(lang)
} }
export default {
i18n,
localesMap,
loadLanguageAsync,
}

View 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);
}
}

View File

@@ -0,0 +1,54 @@
@import 'primeicons/primeicons.css';
@import 'floating-vue/dist/style.css';
.frontend-lib {
@layer tailwind-base, primevue, tailwind-utilities;
@layer tailwind-base {
@tailwind base;
}
@layer tailwind-utilities {
@tailwind components;
@tailwind utilities;
}
:root {
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
font-size: 12px;
line-height: 24px;
font-weight: 400;
color: #0f0f0f;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
}
.card {
background: var(--surface-card);
padding: 2rem;
border-radius: 10px;
margin-bottom: 1rem;
}
::-webkit-scrollbar {
width: 4px;
height: 4px;
border-radius: 4px;
}
::-webkit-scrollbar-track {
border-radius: 4px;
}
::-webkit-scrollbar-thumb {
border-radius: 4px;
background-color: #0000005d;
}
}

View File

@@ -1,9 +1,9 @@
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
export enum NetworkingMethod { export enum NetworkingMethod {
PublicServer = 'PublicServer', PublicServer = 0,
Manual = 'Manual', Manual = 1,
Standalone = 'Standalone', Standalone = 2,
} }
export interface NetworkConfig { export interface NetworkConfig {
@@ -11,6 +11,7 @@ export interface NetworkConfig {
dhcp: boolean dhcp: boolean
virtual_ipv4: string virtual_ipv4: string
network_length: number
hostname?: string hostname?: string
network_name: string network_name: string
network_secret: string network_secret: string
@@ -42,6 +43,7 @@ export function DEFAULT_NETWORK_CONFIG(): NetworkConfig {
dhcp: true, dhcp: true,
virtual_ipv4: '', virtual_ipv4: '',
network_length: 24,
network_name: 'easytier', network_name: 'easytier',
network_secret: '', network_secret: '',
@@ -82,8 +84,7 @@ export interface NetworkInstance {
export interface NetworkInstanceRunningInfo { export interface NetworkInstanceRunningInfo {
dev_name: string dev_name: string
my_node_info: NodeInfo my_node_info: NodeInfo
events: Record<string, any> events: Array<string>,
node_info: NodeInfo
routes: Route[] routes: Route[]
peers: PeerInfo[] peers: PeerInfo[]
peer_route_pairs: PeerRoutePair[] peer_route_pairs: PeerRoutePair[]
@@ -95,6 +96,11 @@ export interface Ipv4Addr {
addr: number addr: number
} }
export interface Ipv4Inet {
address: Ipv4Addr
network_length: number
}
export interface Ipv6Addr { export interface Ipv6Addr {
part1: number part1: number
part2: number part2: number
@@ -102,8 +108,12 @@ export interface Ipv6Addr {
part4: number part4: number
} }
export interface Url {
url: string
}
export interface NodeInfo { export interface NodeInfo {
virtual_ipv4: string virtual_ipv4: Ipv4Inet,
hostname: string hostname: string
version: string version: string
ips: { ips: {
@@ -125,7 +135,7 @@ export interface NodeInfo {
}[] }[]
} }
stun_info: StunInfo stun_info: StunInfo
listeners: string[] listeners: Url[]
vpn_portal_cfg?: string vpn_portal_cfg?: string
} }
@@ -137,7 +147,7 @@ export interface StunInfo {
export interface Route { export interface Route {
peer_id: number peer_id: number
ipv4_addr: string ipv4_addr: Ipv4Inet | string | null
next_hop_peer_id: number next_hop_peer_id: number
cost: number cost: number
proxy_cidrs: string[] proxy_cidrs: string[]
@@ -155,6 +165,7 @@ export interface PeerInfo {
export interface PeerConnInfo { export interface PeerConnInfo {
conn_id: string conn_id: string
my_peer_id: number my_peer_id: number
is_client: boolean
peer_id: number peer_id: number
features: string[] features: string[]
tunnel?: TunnelInfo tunnel?: TunnelInfo
@@ -180,3 +191,28 @@ export interface PeerConnStats {
tx_packets: number tx_packets: number
latency_us: number latency_us: number
} }
export enum EventType {
TunDeviceReady = 'TunDeviceReady', // string
TunDeviceError = 'TunDeviceError', // string
PeerAdded = 'PeerAdded', // number
PeerRemoved = 'PeerRemoved', // number
PeerConnAdded = 'PeerConnAdded', // PeerConnInfo
PeerConnRemoved = 'PeerConnRemoved', // PeerConnInfo
ListenerAdded = 'ListenerAdded', // any
ListenerAddFailed = 'ListenerAddFailed', // any, string
ListenerAcceptFailed = 'ListenerAcceptFailed', // any, string
ConnectionAccepted = 'ConnectionAccepted', // string, string
ConnectionError = 'ConnectionError', // string, string, string
Connecting = 'Connecting', // any
ConnectError = 'ConnectError', // string, string, string
VpnPortalClientConnected = 'VpnPortalClientConnected', // string, string
VpnPortalClientDisconnected = 'VpnPortalClientDisconnected', // string, string, string
DhcpIpv4Changed = 'DhcpIpv4Changed', // ipv4 | null, ipv4 | null
DhcpIpv4Conflicted = 'DhcpIpv4Conflicted', // ipv4 | null
}

View File

@@ -0,0 +1 @@
/// <reference types="vite/client" />

View File

@@ -0,0 +1,11 @@
/** @type {import('tailwindcss').Config} */
export default {
content: [
'./index.html',
'./src/**/*.{vue,js,ts,jsx,tsx}',
],
theme: {
extend: {},
},
plugins: [require('tailwindcss-primeui')],
}

View File

@@ -0,0 +1,31 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"target": "ES2020",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"skipLibCheck": true,
"allowSyntheticDefaultImports": true,
/* Bundler mode */
"moduleResolution": "Bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "preserve",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true,
"types": [
"@modyfi/vite-plugin-yaml/modules"
],
},
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"]
}

View File

@@ -0,0 +1,7 @@
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
]
}

View File

@@ -0,0 +1,24 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"target": "ES2022",
"lib": ["ES2023"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "Bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["vite.config.ts"]
}

View File

@@ -0,0 +1,38 @@
import { resolve } from 'path'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import dts from "vite-plugin-dts"
import ViteYaml from '@modyfi/vite-plugin-yaml';
// https://vite.dev/config/
export default defineConfig({
plugins: [vue(), dts({
tsconfigPath: './tsconfig.app.json',
}), ViteYaml()],
build: {
lib: {
// Could also be a dictionary or array of multiple entry points
entry: resolve(__dirname, 'src/index.ts'),
name: 'easytier-frontend-lib',
// the proper extensions will be added
fileName: 'easytier-frontend-lib',
formats: ["es", "umd", "cjs"],
},
rollupOptions: {
input: {
main: resolve(__dirname, "src/easytier-frontend-lib.ts")
},
// make sure to externalize deps that shouldn't be bundled
// into your library
external: ['vue'],
output: {
// Provide global variables to use in the UMD build
// for externalized deps
globals: {
vue: 'Vue',
},
exports: "named"
},
},
},
})

24
easytier-web/frontend/.gitignore vendored Normal file
View 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?

View 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).

View File

@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/png" href="/easytier.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>EasyTier Dashboard</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

View File

@@ -0,0 +1,32 @@
{
"name": "easytier-frontend",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vue-tsc -b && vite build",
"preview": "vite preview"
},
"dependencies": {
"@primevue/themes": "^4.2.1",
"aura": "link:@primevue/themes/aura",
"axios": "^1.7.7",
"easytier-frontend-lib": "workspace:*",
"primevue": "^4.2.1",
"tailwindcss-primeui": "^0.3.4",
"vue": "^3.5.12",
"vue-router": "4"
},
"devDependencies": {
"@types/node": "^22.8.6",
"@vitejs/plugin-vue": "^5.1.4",
"autoprefixer": "^10.4.20",
"postcss": "^8.4.47",
"tailwindcss": "^3.4.14",
"typescript": "~5.6.2",
"vite": "^5.4.10",
"vite-plugin-singlefile": "^2.0.3",
"vue-tsc": "^2.1.10"
}
}

View File

@@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@@ -0,0 +1,27 @@
<script setup lang="ts">
import { I18nUtils } from 'easytier-frontend-lib'
import { onMounted } from 'vue';
import { Toast, DynamicDialog } from 'primevue';
onMounted(async () => {
await I18nUtils.loadLanguageAsync('cn')
});
</script>
<!-- https://flowbite.com/docs/components/sidebar/#sidebar-with-navbar -->
<template>
<Toast position="bottom-right" />
<DynamicDialog />
<RouterView />
</template>
<style scoped>
button {
text-align: left;
justify-content: left;
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@@ -0,0 +1,33 @@
<script lang="ts" setup>
import { computed, inject, ref } from 'vue';
import { Card, Password, Button } from 'primevue';
import { Api } from 'easytier-frontend-lib';
const dialogRef = inject<any>('dialogRef');
const api = computed<Api.ApiClient>(() => dialogRef.value.data.api);
const password = ref('');
const changePassword = async () => {
await api.value.change_password(password.value);
dialogRef.value.close();
}
</script>
<template>
<div class="flex items-center justify-center">
<Card class="w-full max-w-md p-6">
<template #header>
<h2 class="text-2xl font-semibold text-center">Change Password
</h2>
</template>
<template #content>
<div class="flex flex-col space-y-4">
<Password v-model="password" placeholder="New Password" :feedback="false" toggleMask />
<Button @click="changePassword" label="Ok" />
</div>
</template>
</Card>
</div>
</template>

View File

@@ -0,0 +1,65 @@
<script setup lang="ts">
import { Card, useToast } from 'primevue';
import { computed, onMounted, onUnmounted, ref } from 'vue';
import { Api, Utils } from 'easytier-frontend-lib';
const props = defineProps({
api: Api.ApiClient,
});
const toast = useToast();
const summary = ref<Api.Summary | undefined>(undefined);
const loadSummary = async () => {
const resp = await props.api?.get_summary();
summary.value = resp;
};
const periodFunc = new Utils.PeriodicTask(async () => {
try {
await loadSummary();
} catch (e) {
toast.add({ severity: 'error', summary: 'Load Summary Failed', detail: e, life: 2000 });
console.error(e);
}
}, 1000);
onMounted(async () => {
periodFunc.start();
});
onUnmounted(() => {
periodFunc.stop();
});
const deviceCount = computed<number | undefined>(
() => {
return summary.value?.device_count;
},
);
</script>
<template>
<div class="grid grid-cols-3 gap-4">
<Card class="h-full">
<template #title>Device Count</template>
<template #content>
<div class="w-full flex justify-center text-7xl font-bold text-green-800 mt-4">
{{ deviceCount }}
</div>
</template>
</Card>
<div class="flex items-center justify-center rounded bg-gray-50 dark:bg-gray-800">
<p class="text-2xl text-gray-400 dark:text-gray-500">
<!-- <svg class="w-3.5 h-3.5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
viewBox="0 0 18 18">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M9 1v16M1 9h16" />
</svg> -->
</p>
</div>
</div>
</template>

View File

@@ -0,0 +1,110 @@
<script setup lang="ts">
import { computed, onMounted, onUnmounted, ref } from 'vue';
import { Button, Column, DataTable, Drawer, ProgressSpinner, useToast } from 'primevue';
import { useRoute, useRouter } from 'vue-router';
import { Api, Utils } from 'easytier-frontend-lib';
const props = defineProps({
api: Api.ApiClient,
});
const api = props.api;
const deviceList = ref<Array<Utils.DeviceInfo> | undefined>(undefined);
const selectedDeviceId = computed<string | undefined>(() => route.params.deviceId as string);
const route = useRoute();
const router = useRouter();
const toast = useToast();
const loadDevices = async () => {
const resp = await api?.list_machines();
let devices: Array<Utils.DeviceInfo> = [];
for (const device of (resp || [])) {
devices.push({
hostname: device.info?.hostname,
public_ip: device.client_url,
running_network_instances: device.info?.running_network_instances.map((instance: any) => Utils.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: Utils.UuidToStr(device.info?.machine_id),
});
}
console.debug("device list", deviceList.value);
deviceList.value = devices;
};
const periodFunc = new Utils.PeriodicTask(async () => {
try {
await loadDevices();
} catch (e) {
toast.add({ severity: 'error', summary: 'Load Device List Failed', detail: e, life: 2000 });
console.error(e);
}
}, 1000);
onMounted(async () => {
periodFunc.start();
});
onUnmounted(() => {
periodFunc.stop();
});
const deviceManageVisible = computed<boolean>({
get: () => !!selectedDeviceId.value,
set: (value) => {
if (!value) {
router.push({ name: 'deviceList', params: { deviceId: undefined } });
}
}
});
const selectedDeviceHostname = computed<string | undefined>(() => {
return deviceList.value?.find((device) => device.machine_id === selectedDeviceId.value)?.hostname;
});
</script>
<style scoped></style>
<template>
<div v-if="deviceList === undefined" class="w-full flex justify-center">
<ProgressSpinner />
</div>
<DataTable :value="deviceList" tableStyle="min-width: 50rem" :metaKeySelection="true" sortField="hostname"
:sortOrder="-1" v-if="deviceList !== undefined">
<template #header>
<div class="text-xl font-bold">Device List</div>
</template>
<Column field="hostname" header="Hostname" sortable style="width: 180px"></Column>
<Column field="public_ip" header="Public IP" style="width: 150px"></Column>
<Column field="running_network_count" header="Running Network Count" sortable style="width: 150px"></Column>
<Column field="report_time" header="Report Time" sortable style="width: 150px"></Column>
<Column field="easytier_version" header="EasyTier Version" sortable style="width: 150px"></Column>
<Column class="w-24 !text-end">
<template #body="{ data }">
<Button icon="pi pi-cog"
@click="router.push({ name: 'deviceManagement', params: { deviceId: data.machine_id, instanceId: data.running_network_instances[0] } })"
severity="secondary" rounded></Button>
</template>
</Column>
<template #footer>
<div class="flex justify-end">
<Button icon="pi pi-refresh" label="Reload" severity="info" @click="loadDevices" />
</div>
</template>
</DataTable>
<Drawer v-model:visible="deviceManageVisible" :header="`Manage ${selectedDeviceHostname}`" position="right"
class="w-1/2 min-w-96">
<RouterView v-slot="{ Component }">
<component :is="Component" :api="api" :deviceList="deviceList" @update="loadDevices" />
</RouterView>
</Drawer>
</template>

View File

@@ -0,0 +1,197 @@
<script setup lang="ts">
import { Toolbar, IftaLabel, Select, Button, ConfirmPopup, Dialog, useConfirm, useToast } from 'primevue';
import { NetworkTypes, Status, Utils, Api, } from 'easytier-frontend-lib';
import { computed, onMounted, onUnmounted, ref } from 'vue';
import { useRoute, useRouter } from 'vue-router';
const props = defineProps<{
api: Api.ApiClient;
deviceList: Array<Utils.DeviceInfo> | undefined;
}>();
const emits = defineEmits(['update']);
const route = useRoute();
const router = useRouter();
const toast = useToast();
const deviceId = computed<string>(() => {
return route.params.deviceId as string;
});
const instanceId = computed<string>(() => {
return route.params.instanceId as string;
});
const deviceInfo = computed<Utils.DeviceInfo | undefined | null>(() => {
return deviceId.value ? props.deviceList?.find((device) => device.machine_id === deviceId.value) : null;
});
const curNetworkInfo = ref<NetworkTypes.NetworkInstance | null>(null);
const isEditing = ref(false);
const showCreateNetworkDialog = ref(false);
const newNetworkConfig = ref<NetworkTypes.NetworkConfig>(NetworkTypes.DEFAULT_NETWORK_CONFIG());
const instanceIdList = computed(() => {
let insts = deviceInfo.value?.running_network_instances || [];
let options = insts.map((instance: string) => {
return { uuid: instance };
});
return options;
});
const selectedInstanceId = computed({
get() {
return instanceIdList.value.find((instance) => instance.uuid === instanceId.value);
},
set(value: any) {
console.log("set instanceId", value);
router.push({ name: 'deviceManagement', params: { deviceId: deviceId.value, instanceId: value.uuid } });
}
});
const confirm = useConfirm();
const confirmDeleteNetwork = (event: any) => {
confirm.require({
target: event.currentTarget,
message: 'Do you want to delete this network?',
icon: 'pi pi-info-circle',
rejectProps: {
label: 'Cancel',
severity: 'secondary',
outlined: true
},
acceptProps: {
label: 'Delete',
severity: 'danger'
},
accept: async () => {
try {
await props.api?.delete_network(deviceId.value, instanceId.value);
} catch (e) {
console.error(e);
}
emits('update');
},
reject: () => {
return;
}
});
};
// const verifyNetworkConfig = async (): Promise<ValidateConfigResponse | undefined> => {
// let ret = await props.api?.validate_config(deviceId.value, newNetworkConfig.value);
// console.log("verifyNetworkConfig", ret);
// return ret;
// }
const createNewNetwork = async () => {
try {
if (isEditing.value) {
await props.api?.delete_network(deviceId.value, instanceId.value);
}
let ret = await props.api?.run_network(deviceId.value, newNetworkConfig.value);
console.debug("createNewNetwork", ret);
} catch (e: any) {
console.error(e);
toast.add({ severity: 'error', summary: 'Error', detail: 'Failed to create network, error: ' + JSON.stringify(e.response.data), life: 2000 });
return;
}
emits('update');
showCreateNetworkDialog.value = false;
}
const newNetwork = () => {
newNetworkConfig.value = NetworkTypes.DEFAULT_NETWORK_CONFIG();
isEditing.value = false;
showCreateNetworkDialog.value = true;
}
const editNetwork = async () => {
if (!deviceId.value || !instanceId.value) {
toast.add({ severity: 'error', summary: 'Error', detail: 'No network instance selected', life: 2000 });
return;
}
isEditing.value = true;
try {
let ret = await props.api?.get_network_config(deviceId.value, instanceId.value);
console.debug("editNetwork", ret);
newNetworkConfig.value = ret;
showCreateNetworkDialog.value = true;
} catch (e: any) {
console.error(e);
toast.add({ severity: 'error', summary: 'Error', detail: 'Failed to edit network, error: ' + JSON.stringify(e.response.data), life: 2000 });
return;
}
}
const loadDeviceInfo = async () => {
if (!deviceId.value || !instanceId.value) {
return;
}
let ret = await props.api?.get_network_info(deviceId.value, instanceId.value);
let device_info = ret[instanceId.value];
curNetworkInfo.value = {
instance_id: instanceId.value,
running: device_info.running,
error_msg: device_info.error_msg,
detail: device_info,
} as NetworkTypes.NetworkInstance;
}
let periodFunc = new Utils.PeriodicTask(async () => {
try {
await loadDeviceInfo();
} catch (e) {
console.debug(e);
}
}, 1000);
onMounted(async () => {
periodFunc.start();
});
onUnmounted(() => {
periodFunc.stop();
});
</script>
<template>
<ConfirmPopup></ConfirmPopup>
<Dialog v-model:visible="showCreateNetworkDialog" modal :header="!isEditing ? 'Create New Network' : 'Edit Network'"
:style="{ width: '55rem' }">
<Config :cur-network="newNetworkConfig" @run-network="createNewNetwork"></Config>
</Dialog>
<Toolbar>
<template #start>
<IftaLabel>
<Select v-model="selectedInstanceId" :options="instanceIdList" optionLabel="uuid" inputId="dd-inst-id"
placeholder="Select Instance" />
<label class="mr-3" for="dd-inst-id">Network</label>
</IftaLabel>
</template>
<template #end>
<div class="gap-x-3 flex">
<Button @click="confirmDeleteNetwork($event)" icon="pi pi-minus" severity="danger" label="Delete"
iconPos="right" />
<Button @click="editNetwork" icon="pi pi-pen-to-square" label="Edit" iconPos="right" severity="info" />
<Button @click="newNetwork" icon="pi pi-plus" label="Create" iconPos="right" />
</div>
</template>
</Toolbar>
<Status v-bind:cur-network-inst="curNetworkInfo" v-if="!!selectedInstanceId">
</Status>
<div class="grid grid-cols-1 gap-4 place-content-center h-full" v-if="!selectedInstanceId">
<div class="text-center text-xl"> Select or create a network instance to manage </div>
</div>
</template>

View File

@@ -0,0 +1,122 @@
<script setup lang="ts">
import { computed, ref } from 'vue';
import { Card, InputText, Password, Button, AutoComplete } from 'primevue';
import { useRouter } from 'vue-router';
import { useToast } from 'primevue/usetoast';
import { Api } from 'easytier-frontend-lib';
defineProps<{
isRegistering: boolean;
}>();
const api = computed<Api.ApiClient>(() => new Api.ApiClient(apiHost.value));
const router = useRouter();
const toast = useToast();
const username = ref('');
const password = ref('');
const registerUsername = ref('');
const registerPassword = ref('');
const captcha = ref('');
const captchaSrc = computed(() => api.value.captcha_url());
const onSubmit = async () => {
// Add your login logic here
const credential: Api.Credential = { username: username.value, password: password.value, };
let ret = await api.value?.login(credential);
if (ret.success) {
localStorage.setItem('apiHost', btoa(apiHost.value));
router.push({
name: 'dashboard',
params: { apiHost: btoa(apiHost.value) },
});
} else {
toast.add({ severity: 'error', summary: 'Login Failed', detail: ret.message, life: 2000 });
}
};
const onRegister = async () => {
const credential: Api.Credential = { username: registerUsername.value, password: registerPassword.value };
const registerReq: Api.RegisterData = { credentials: credential, captcha: captcha.value };
let ret = await api.value?.register(registerReq);
if (ret.success) {
toast.add({ severity: 'success', summary: 'Register Success', detail: ret.message, life: 2000 });
router.push({ name: 'login' });
} else {
toast.add({ severity: 'error', summary: 'Register Failed', detail: ret.message, life: 2000 });
}
};
const defaultApiHost = 'https://config-server.easytier.cn'
const apiHost = ref<string>(defaultApiHost)
const apiHostSuggestions = ref<Array<string>>([])
const apiHostSearch = async (event: { query: string }) => {
apiHostSuggestions.value = [];
if (event.query) {
apiHostSuggestions.value.push(event.query);
}
apiHostSuggestions.value.push(defaultApiHost);
}
</script>
<template>
<div class="flex items-center justify-center min-h-screen">
<Card class="w-full max-w-md p-6">
<template #header>
<h2 class="text-2xl font-semibold text-center">{{ isRegistering ? 'Register' : 'Login' }}
</h2>
</template>
<template #content>
<div class="p-field mb-4">
<label for="api-host" class="block text-sm font-medium">Api Host</label>
<AutoComplete id="api-host" v-model="apiHost" dropdown :suggestions="apiHostSuggestions"
@complete="apiHostSearch" class="w-full" />
</div>
<form v-if="!isRegistering" @submit.prevent="onSubmit" class="space-y-4">
<div class="p-field">
<label for="username" class="block text-sm font-medium">Username</label>
<InputText id="username" v-model="username" required class="w-full" />
</div>
<div class="p-field">
<label for="password" class="block text-sm font-medium">Password</label>
<Password id="password" v-model="password" required toggleMask :feedback="false" />
</div>
<div class="flex items-center justify-between">
<Button label="Login" type="submit" class="w-full" />
</div>
<div class="flex items-center justify-between">
<Button label="Register" type="button" class="w-full"
@click="$router.replace({ name: 'register' })" severity="secondary" />
</div>
</form>
<form v-else @submit.prevent="onRegister" class="space-y-4">
<div class="p-field">
<label for="register-username" class="block text-sm font-medium">Username</label>
<InputText id="register-username" v-model="registerUsername" required class="w-full" />
</div>
<div class="p-field">
<label for="register-password" class="block text-sm font-medium">Password</label>
<Password id="register-password" v-model="registerPassword" required toggleMask
:feedback="false" class="w-full" />
</div>
<div class="p-field">
<label for="captcha" class="block text-sm font-medium">Captcha</label>
<InputText id="captcha" v-model="captcha" required class="w-full" />
<img :src="captchaSrc" alt="Captcha" class="mt-2 mb-2" />
</div>
<div class="flex items-center justify-between">
<Button label="Register" type="submit" class="w-full" />
</div>
<div class="flex items-center justify-between">
<Button label="Back to Login" type="button" class="w-full"
@click="$router.replace({ name: 'login' })" severity="secondary" />
</div>
</form>
</template>
</Card>
</div>
</template>
<style scoped></style>

View File

@@ -0,0 +1,173 @@
<script setup lang="ts">
import { Api, I18nUtils } from 'easytier-frontend-lib'
import { computed, onMounted, ref } from 'vue';
import { Button, TieredMenu } from 'primevue';
import { useRoute, useRouter } from 'vue-router';
import { useDialog } from 'primevue/usedialog';
import ChangePassword from './ChangePassword.vue';
import Icon from '../assets/easytier.png'
const route = useRoute();
const router = useRouter();
const api = computed<Api.ApiClient | undefined>(() => {
try {
return new Api.ApiClient(atob(route.params.apiHost as string), () => {
router.push({ name: 'login' });
})
} catch (e) {
router.push({ name: 'login' });
}
});
const dialog = useDialog();
onMounted(async () => {
await I18nUtils.loadLanguageAsync('cn')
});
const userMenu = ref();
const userMenuItems = ref([
{
label: 'Change Password',
icon: 'pi pi-key',
command: () => {
console.log('File');
let ret = dialog.open(ChangePassword, {
props: {
modal: true,
},
data: {
api: api.value,
}
});
console.log("return", ret)
},
},
{
label: 'Logout',
icon: 'pi pi-sign-out',
command: async () => {
try {
await api.value?.logout();
} catch (e) {
console.error("logout failed", e);
}
router.push({ name: 'login' });
},
},
])
const forceShowSideBar = ref(false)
</script>
<!-- https://flowbite.com/docs/components/sidebar/#sidebar-with-navbar -->
<template>
<nav class="fixed top-0 z-50 w-full bg-white border-b border-gray-200 dark:bg-gray-800 dark:border-gray-700">
<div class="px-3 py-3 lg:px-5 lg:pl-3">
<div class="flex items-center justify-between">
<div class="flex items-center justify-start rtl:justify-end">
<div class="sm:hidden">
<Button type="button" aria-haspopup="true" icon="pi pi-list" variant="text" size="large"
severity="contrast" @click="forceShowSideBar = !forceShowSideBar" />
</div>
<a href="https://easytier.top" class="flex ms-2 md:me-24">
<img :src="Icon" class="h-9 me-3" alt="FlowBite Logo" />
<span
class="self-center text-xl font-semibold sm:text-2xl whitespace-nowrap dark:text-white">EasyTier</span>
</a>
</div>
<div class="flex items-center">
<div class="flex items-center ms-3">
<div>
<Button type="button" @click="userMenu.toggle($event)" aria-haspopup="true"
aria-controls="user-menu" icon="pi pi-user" raised rounded />
<TieredMenu ref="userMenu" id="user-menu" :model="userMenuItems" popup />
</div>
<div class="z-50 hidden my-4 text-base list-none bg-white divide-y divide-gray-100 rounded shadow dark:bg-gray-700 dark:divide-gray-600"
id="dropdown-user">
<div class="px-4 py-3" role="none">
<p class="text-sm text-gray-900 dark:text-white" role="none">
Neil Sims
</p>
<p class="text-sm font-medium text-gray-900 truncate dark:text-gray-300" role="none">
neil.sims@flowbite.com
</p>
</div>
<ul class="py-1" role="none">
<li>
<a href="#"
class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-600 dark:hover:text-white"
role="menuitem">Dashboard</a>
</li>
<li>
<a href="#"
class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-600 dark:hover:text-white"
role="menuitem">Settings</a>
</li>
<li>
<a href="#"
class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-600 dark:hover:text-white"
role="menuitem">Earnings</a>
</li>
<li>
<a href="#"
class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-600 dark:hover:text-white"
role="menuitem">Sign out</a>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</nav>
<aside id="logo-sidebar"
class="fixed top-1 left-0 z-40 w-64 h-screen pt-20 transition-transform bg-white border-r border-gray-201 sm:translate-x-0 dark:bg-gray-800 dark:border-gray-700"
:class="{ '-translate-x-full': !forceShowSideBar }" aria-label="Sidebar">
<div class="h-full px-3 pb-4 overflow-y-auto bg-white dark:bg-gray-800">
<ul class="space-y-2 font-medium">
<li>
<Button variant="text" class="w-full justify-start gap-x-3 pl-1.5 sidebar-button"
severity="contrast" @click="router.push({ name: 'dashboard' })">
<i class="pi pi-chart-pie text-xl"></i>
<span class="mb-0.5">DashBoard</span>
</Button>
</li>
<li>
<Button variant="text" class="w-full justify-start gap-x-3 pl-1.5 sidebar-button"
severity="contrast" @click="router.push({ name: 'deviceList' })">
<i class="pi pi-server text-xl"></i>
<span class="mb-0.5">Devices</span>
</Button>
</li>
<li>
<Button variant="text" class="w-full justify-start gap-x-3 pl-1.5 sidebar-button"
severity="contrast" @click="router.push({ name: 'login' })">
<i class="pi pi-sign-in text-xl"></i>
<span class="mb-0.5">Login Page</span>
</Button>
</li>
</ul>
</div>
</aside>
<div class="p-4 sm:ml-64">
<div class="p-4 border-2 border-gray-200 border-dashed rounded-lg dark:border-gray-700 mt-14">
<div class="grid grid-cols-1 gap-4">
<RouterView v-slot="{ Component }">
<component :is="Component" :api="api" />
</RouterView>
</div>
</div>
</div>
</template>
<style scoped>
.sidebar-button {
text-align: left;
justify-content: left;
}
</style>

View File

@@ -0,0 +1,90 @@
import { createApp } from 'vue'
import './style.css'
import 'easytier-frontend-lib/style.css'
import App from './App.vue'
import EasytierFrontendLib from 'easytier-frontend-lib'
import PrimeVue from 'primevue/config'
import Aura from '@primevue/themes/aura'
import ConfirmationService from 'primevue/confirmationservice';
import { createRouter, createWebHashHistory } from 'vue-router'
import MainPage from './components/MainPage.vue'
import Login from './components/Login.vue'
import DeviceList from './components/DeviceList.vue'
import DeviceManagement from './components/DeviceManagement.vue'
import Dashboard from './components/Dashboard.vue'
import DialogService from 'primevue/dialogservice';
import ToastService from 'primevue/toastservice';
const routes = [
{
path: '/auth', children: [
{
name: 'login',
path: '',
component: Login,
alias: 'login',
props: { isRegistering: false }
},
{
name: 'register',
path: 'register',
component: Login,
props: { isRegistering: true }
}
]
},
{
path: '/h/:apiHost', component: MainPage, children: [
{
path: '',
alias: 'dashboard',
name: 'dashboard',
component: Dashboard,
},
{
path: 'deviceList',
name: 'deviceList',
component: DeviceList,
children: [
{
path: 'device/:deviceId/:instanceId?',
name: 'deviceManagement',
component: DeviceManagement,
}
]
},
]
},
{
path: '/:pathMatch(.*)*', name: 'notFound', redirect: () => {
let apiHost = localStorage.getItem('apiHost');
if (apiHost) {
return { name: 'dashboard', params: { apiHost: apiHost } }
} else {
return { name: 'login' }
}
}
},
]
const router = createRouter({
history: createWebHashHistory(),
routes,
})
createApp(App).use(PrimeVue,
{
theme: {
preset: Aura,
options: {
prefix: 'p',
darkModeSelector: 'system',
cssLayer: {
name: 'primevue',
order: 'tailwind-base, primevue, tailwind-utilities'
}
}
}
}
).use(ToastService as any).use(DialogService as any).use(router).use(ConfirmationService as any).use(EasytierFrontendLib).mount('#app')

View File

@@ -0,0 +1,33 @@
@layer tailwind-base, primevue, tailwind-utilities;
@layer tailwind-base {
@tailwind base;
}
@layer tailwind-utilities {
@tailwind components;
@tailwind utilities;
}
.p-password {
width: 100%;
}
.p-password>input {
width: 100%;
}
:root {
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
font-size: 0.9rem;
line-height: 24px;
font-weight: 400;
color: #0f0f0f;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
}

View File

@@ -0,0 +1 @@
/// <reference types="vite/client" />

View File

@@ -0,0 +1,11 @@
/** @type {import('tailwindcss').Config} */
export default {
content: [
'./index.html',
'./src/**/*.{vue,js,ts,jsx,tsx}',
],
theme: {
extend: {},
},
plugins: [require('tailwindcss-primeui')],
}

View File

@@ -0,0 +1,26 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"target": "ES2020",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "Bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "preserve",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue", "../frontend-lib/src/modules/api.ts"]
}

View File

@@ -0,0 +1,7 @@
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
]
}

View File

@@ -0,0 +1,24 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"target": "ES2022",
"lib": ["ES2023"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "Bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["vite.config.ts"]
}

View File

@@ -0,0 +1,9 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { viteSingleFile } from "vite-plugin-singlefile"
// https://vite.dev/config/
export default defineConfig({
base: '',
plugins: [vue(), viteSingleFile()],
})

View File

@@ -0,0 +1,85 @@
-- # Entity schema.
-- Create `users` table.
create table if not exists users (
id integer primary key autoincrement,
username text not null unique,
password text not null
);
-- Create `groups` table.
create table if not exists groups (
id integer primary key autoincrement,
name text not null unique
);
-- Create `permissions` table.
create table if not exists permissions (
id integer primary key autoincrement,
name text not null unique
);
-- # Join tables.
-- Create `users_groups` table for many-to-many relationships between users and groups.
create table if not exists users_groups (
user_id integer references users(id),
group_id integer references groups(id),
primary key (user_id, group_id)
);
-- Create `groups_permissions` table for many-to-many relationships between groups and permissions.
create table if not exists groups_permissions (
group_id integer references groups(id),
permission_id integer references permissions(id),
primary key (group_id, permission_id)
);
-- # Fixture hydration.
-- Insert "user" user. password: "user"
insert into users (username, password)
values (
'user',
'$argon2i$v=19$m=16,t=2,p=1$dHJ5dXZkYmZkYXM$UkrNqWz0BbSVBq4ykLSuJw'
);
-- Insert "admin" user. password: "admin"
insert into users (username, password)
values (
'admin',
'$argon2i$v=19$m=16,t=2,p=1$Ymd1Y2FlcnQ$x0q4oZinW9S1ZB9BcaHEpQ'
);
-- Insert "users" and "superusers" groups.
insert into groups (name) values ('users');
insert into groups (name) values ('superusers');
-- Insert individual permissions.
insert into permissions (name) values ('sessions');
insert into permissions (name) values ('devices');
-- Insert group permissions.
insert into groups_permissions (group_id, permission_id)
values (
(select id from groups where name = 'users'),
(select id from permissions where name = 'devices')
), (
(select id from groups where name = 'superusers'),
(select id from permissions where name = 'sessions')
);
-- Insert users into groups.
insert into users_groups (user_id, group_id)
values (
(select id from users where username = 'user'),
(select id from groups where name = 'users')
), (
(select id from users where username = 'admin'),
(select id from groups where name = 'users')
), (
(select id from users where username = 'admin'),
(select id from groups where name = 'superusers')
);

Binary file not shown.

View File

@@ -0,0 +1,152 @@
pub mod session;
pub mod storage;
use std::sync::Arc;
use dashmap::DashMap;
use easytier::{
common::scoped_task::ScopedTask, proto::web::HeartbeatRequest, tunnel::TunnelListener,
};
use session::Session;
use storage::{Storage, StorageToken};
use crate::db::Db;
#[derive(Debug)]
pub struct ClientManager {
accept_task: Option<ScopedTask<()>>,
clear_task: Option<ScopedTask<()>>,
client_sessions: Arc<DashMap<url::Url, Arc<Session>>>,
storage: Storage,
}
impl ClientManager {
pub fn new(db: Db) -> Self {
ClientManager {
accept_task: None,
clear_task: None,
client_sessions: Arc::new(DashMap::new()),
storage: Storage::new(db),
}
}
pub async fn serve<L: TunnelListener + 'static>(
&mut self,
mut listener: L,
) -> Result<(), anyhow::Error> {
listener.listen().await?;
let sessions = self.client_sessions.clone();
let storage = self.storage.weak_ref();
let task = tokio::spawn(async move {
while let Ok(tunnel) = listener.accept().await {
let info = tunnel.info().unwrap();
let client_url: url::Url = info.remote_addr.unwrap().into();
println!("New session from {:?}", tunnel.info());
let mut session = Session::new(storage.clone(), client_url.clone());
session.serve(tunnel).await;
sessions.insert(client_url, Arc::new(session));
}
});
self.accept_task = Some(ScopedTask::from(task));
let sessions = self.client_sessions.clone();
let task = tokio::spawn(async move {
loop {
tokio::time::sleep(std::time::Duration::from_secs(15)).await;
sessions.retain(|_, session| session.is_running());
}
});
self.clear_task = Some(ScopedTask::from(task));
Ok(())
}
pub fn is_running(&self) -> bool {
self.accept_task.is_some() && self.clear_task.is_some()
}
pub async fn list_sessions(&self) -> Vec<StorageToken> {
let sessions = self
.client_sessions
.iter()
.map(|item| item.value().clone())
.collect::<Vec<_>>();
let mut ret: Vec<StorageToken> = vec![];
for s in sessions {
if let Some(t) = s.get_token().await {
ret.push(t);
}
}
ret
}
pub fn get_session_by_machine_id(&self, machine_id: &uuid::Uuid) -> Option<Arc<Session>> {
let c_url = self.storage.get_client_url_by_machine_id(machine_id)?;
self.client_sessions
.get(&c_url)
.map(|item| item.value().clone())
}
pub fn list_machine_by_token(&self, token: String) -> Vec<url::Url> {
self.storage.list_token_clients(&token)
}
pub async fn get_heartbeat_requests(&self, client_url: &url::Url) -> Option<HeartbeatRequest> {
let s = self.client_sessions.get(client_url)?.clone();
s.data().read().await.req()
}
pub fn db(&self) -> &Db {
self.storage.db()
}
}
#[cfg(test)]
mod tests {
use std::time::Duration;
use easytier::{
tunnel::{
common::tests::wait_for_condition,
udp::{UdpTunnelConnector, UdpTunnelListener},
},
web_client::WebClient,
};
use crate::{client_manager::ClientManager, db::Db};
#[tokio::test]
async fn test_client() {
let listener = UdpTunnelListener::new("udp://0.0.0.0:54333".parse().unwrap());
let mut mgr = ClientManager::new(Db::memory_db().await);
mgr.serve(Box::new(listener)).await.unwrap();
let connector = UdpTunnelConnector::new("udp://127.0.0.1:54333".parse().unwrap());
let _c = WebClient::new(connector, "test");
wait_for_condition(
|| async { mgr.client_sessions.len() == 1 },
Duration::from_secs(6),
)
.await;
let mut a = mgr
.client_sessions
.iter()
.next()
.unwrap()
.data()
.read()
.await
.heartbeat_waiter();
let req = a.recv().await.unwrap();
println!("{:?}", req);
println!("{:?}", mgr);
}
}

View File

@@ -0,0 +1,264 @@
use std::{fmt::Debug, sync::Arc};
use easytier::{
common::scoped_task::ScopedTask,
proto::{
rpc_impl::bidirect::BidirectRpcManager,
rpc_types::{self, controller::BaseController},
web::{
HeartbeatRequest, HeartbeatResponse, NetworkConfig, RunNetworkInstanceRequest,
WebClientService, WebClientServiceClientFactory, WebServerService,
WebServerServiceServer,
},
},
tunnel::Tunnel,
};
use tokio::sync::{broadcast, RwLock};
use super::storage::{Storage, StorageToken, WeakRefStorage};
#[derive(Debug)]
pub struct SessionData {
storage: WeakRefStorage,
client_url: url::Url,
storage_token: Option<StorageToken>,
notifier: broadcast::Sender<HeartbeatRequest>,
req: Option<HeartbeatRequest>,
}
impl SessionData {
fn new(storage: WeakRefStorage, client_url: url::Url) -> Self {
let (tx, _rx1) = broadcast::channel(2);
SessionData {
storage,
client_url,
storage_token: None,
notifier: tx,
req: None,
}
}
pub fn req(&self) -> Option<HeartbeatRequest> {
self.req.clone()
}
pub fn heartbeat_waiter(&self) -> broadcast::Receiver<HeartbeatRequest> {
self.notifier.subscribe()
}
}
impl Drop for SessionData {
fn drop(&mut self) {
if let Ok(storage) = Storage::try_from(self.storage.clone()) {
if let Some(token) = self.storage_token.as_ref() {
storage.remove_client(token);
}
}
}
}
pub type SharedSessionData = Arc<RwLock<SessionData>>;
#[derive(Clone)]
struct SessionRpcService {
data: SharedSessionData,
}
#[async_trait::async_trait]
impl WebServerService for SessionRpcService {
type Controller = BaseController;
async fn heartbeat(
&self,
_: BaseController,
req: HeartbeatRequest,
) -> rpc_types::error::Result<HeartbeatResponse> {
let mut data = self.data.write().await;
if data.req.replace(req.clone()).is_none() {
assert!(data.storage_token.is_none());
data.storage_token = Some(StorageToken {
token: req.user_token.clone().into(),
client_url: data.client_url.clone(),
machine_id: req
.machine_id
.clone()
.map(Into::into)
.unwrap_or(uuid::Uuid::new_v4()),
});
if let Ok(storage) = Storage::try_from(data.storage.clone()) {
storage.add_client(data.storage_token.as_ref().unwrap().clone());
}
}
let _ = data.notifier.send(req);
Ok(HeartbeatResponse {})
}
}
pub struct Session {
rpc_mgr: BidirectRpcManager,
data: SharedSessionData,
run_network_on_start_task: Option<ScopedTask<()>>,
}
impl Debug for Session {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Session").field("data", &self.data).finish()
}
}
type SessionRpcClient = Box<dyn WebClientService<Controller = BaseController> + Send>;
impl Session {
pub fn new(storage: WeakRefStorage, client_url: url::Url) -> Self {
let session_data = SessionData::new(storage, client_url);
let data = Arc::new(RwLock::new(session_data));
let rpc_mgr =
BidirectRpcManager::new().set_rx_timeout(Some(std::time::Duration::from_secs(30)));
rpc_mgr.rpc_server().registry().register(
WebServerServiceServer::new(SessionRpcService { data: data.clone() }),
"",
);
Session {
rpc_mgr,
data,
run_network_on_start_task: None,
}
}
pub async fn serve(&mut self, tunnel: Box<dyn Tunnel>) {
self.rpc_mgr.run_with_tunnel(tunnel);
let data = self.data.read().await;
self.run_network_on_start_task.replace(
tokio::spawn(Self::run_network_on_start(
data.heartbeat_waiter(),
data.storage.clone(),
self.scoped_rpc_client(),
))
.into(),
);
}
async fn run_network_on_start(
mut heartbeat_waiter: broadcast::Receiver<HeartbeatRequest>,
storage: WeakRefStorage,
rpc_client: SessionRpcClient,
) {
loop {
heartbeat_waiter = heartbeat_waiter.resubscribe();
let req = heartbeat_waiter.recv().await;
if req.is_err() {
tracing::error!(
"Failed to receive heartbeat request, error: {:?}",
req.err()
);
return;
}
let req = req.unwrap();
if req.machine_id.is_none() {
tracing::warn!(?req, "Machine id is not set, ignore");
continue;
}
let running_inst_ids = req
.running_network_instances
.iter()
.map(|x| x.to_string())
.collect::<Vec<_>>();
let Some(storage) = storage.upgrade() else {
tracing::error!("Failed to get storage");
return;
};
let user_id = match storage
.db
.get_user_id_by_token(req.user_token.clone())
.await
{
Ok(Some(user_id)) => user_id,
Ok(None) => {
tracing::info!("User not found by token: {:?}", req.user_token);
return;
}
Err(e) => {
tracing::error!("Failed to get user id by token, error: {:?}", e);
return;
}
};
let local_configs = match storage
.db
.list_network_configs(user_id, Some(req.machine_id.unwrap().into()), true)
.await
{
Ok(configs) => configs,
Err(e) => {
tracing::error!("Failed to list network configs, error: {:?}", e);
return;
}
};
let mut has_failed = false;
for c in local_configs {
if running_inst_ids.contains(&c.network_instance_id) {
continue;
}
let ret = rpc_client
.run_network_instance(
BaseController::default(),
RunNetworkInstanceRequest {
inst_id: Some(c.network_instance_id.clone().into()),
config: Some(
serde_json::from_str::<NetworkConfig>(&c.network_config).unwrap(),
),
},
)
.await;
tracing::info!(
?user_id,
"Run network instance: {:?}, user_token: {:?}",
ret,
req.user_token
);
has_failed |= ret.is_err();
}
if !has_failed {
tracing::info!(?req, "All network instances are running");
break;
}
}
}
pub fn is_running(&self) -> bool {
self.rpc_mgr.is_running()
}
pub fn data(&self) -> SharedSessionData {
self.data.clone()
}
pub fn scoped_rpc_client(&self) -> SessionRpcClient {
self.rpc_mgr
.rpc_client()
.scoped_client::<WebClientServiceClientFactory<BaseController>>(1, 1, "".to_string())
}
pub async fn get_token(&self) -> Option<StorageToken> {
self.data.read().await.storage_token.clone()
}
pub async fn get_heartbeat_req(&self) -> Option<HeartbeatRequest> {
self.data.read().await.req()
}
}

View File

@@ -0,0 +1,96 @@
use std::sync::{Arc, Weak};
use dashmap::{DashMap, DashSet};
use crate::db::Db;
// use this to maintain Storage
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct StorageToken {
pub token: String,
pub client_url: url::Url,
pub machine_id: uuid::Uuid,
}
#[derive(Debug)]
pub struct StorageInner {
// some map for indexing
pub token_clients_map: DashMap<String, DashSet<url::Url>>,
pub machine_client_url_map: DashMap<uuid::Uuid, DashSet<url::Url>>,
pub db: Db,
}
#[derive(Debug, Clone)]
pub struct Storage(Arc<StorageInner>);
pub type WeakRefStorage = Weak<StorageInner>;
impl TryFrom<WeakRefStorage> for Storage {
type Error = ();
fn try_from(weak: Weak<StorageInner>) -> Result<Self, Self::Error> {
weak.upgrade().map(|inner| Storage(inner)).ok_or(())
}
}
impl Storage {
pub fn new(db: Db) -> Self {
Storage(Arc::new(StorageInner {
token_clients_map: DashMap::new(),
machine_client_url_map: DashMap::new(),
db,
}))
}
pub fn add_client(&self, stoken: StorageToken) {
let inner = self
.0
.token_clients_map
.entry(stoken.token)
.or_insert_with(DashSet::new);
inner.insert(stoken.client_url.clone());
self.0
.machine_client_url_map
.entry(stoken.machine_id)
.or_insert_with(DashSet::new)
.insert(stoken.client_url.clone());
}
pub fn remove_client(&self, stoken: &StorageToken) {
self.0.token_clients_map.remove_if(&stoken.token, |_, set| {
set.remove(&stoken.client_url);
set.is_empty()
});
self.0
.machine_client_url_map
.remove_if(&stoken.machine_id, |_, set| {
set.remove(&stoken.client_url);
set.is_empty()
});
}
pub fn weak_ref(&self) -> WeakRefStorage {
Arc::downgrade(&self.0)
}
pub fn get_client_url_by_machine_id(&self, machine_id: &uuid::Uuid) -> Option<url::Url> {
self.0
.machine_client_url_map
.get(&machine_id)
.map(|url| url.iter().next().map(|url| url.clone()))
.flatten()
}
pub fn list_token_clients(&self, token: &str) -> Vec<url::Url> {
self.0
.token_clients_map
.get(token)
.map(|set| set.iter().map(|url| url.clone()).collect())
.unwrap_or_default()
}
pub fn db(&self) -> &Db {
&self.0.db
}
}

View File

@@ -0,0 +1,35 @@
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0
use sea_orm::entity::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
#[sea_orm(table_name = "groups")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
#[sea_orm(unique)]
pub name: String,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(has_many = "super::groups_permissions::Entity")]
GroupsPermissions,
#[sea_orm(has_many = "super::users_groups::Entity")]
UsersGroups,
}
impl Related<super::groups_permissions::Entity> for Entity {
fn to() -> RelationDef {
Relation::GroupsPermissions.def()
}
}
impl Related<super::users_groups::Entity> for Entity {
fn to() -> RelationDef {
Relation::UsersGroups.def()
}
}
impl ActiveModelBehavior for ActiveModel {}

View File

@@ -0,0 +1,47 @@
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0
use sea_orm::entity::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
#[sea_orm(table_name = "groups_permissions")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
pub group_id: i32,
pub permission_id: i32,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(
belongs_to = "super::groups::Entity",
from = "Column::GroupId",
to = "super::groups::Column::Id",
on_update = "Cascade",
on_delete = "Cascade"
)]
Groups,
#[sea_orm(
belongs_to = "super::permissions::Entity",
from = "Column::PermissionId",
to = "super::permissions::Column::Id",
on_update = "Cascade",
on_delete = "Cascade"
)]
Permissions,
}
impl Related<super::groups::Entity> for Entity {
fn to() -> RelationDef {
Relation::Groups.def()
}
}
impl Related<super::permissions::Entity> for Entity {
fn to() -> RelationDef {
Relation::Permissions.def()
}
}
impl ActiveModelBehavior for ActiveModel {}

View File

@@ -0,0 +1,11 @@
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0
pub mod prelude;
pub mod groups;
pub mod groups_permissions;
pub mod permissions;
pub mod tower_sessions;
pub mod user_running_network_configs;
pub mod users;
pub mod users_groups;

View File

@@ -0,0 +1,27 @@
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0
use sea_orm::entity::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
#[sea_orm(table_name = "permissions")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
#[sea_orm(unique)]
pub name: String,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(has_many = "super::groups_permissions::Entity")]
GroupsPermissions,
}
impl Related<super::groups_permissions::Entity> for Entity {
fn to() -> RelationDef {
Relation::GroupsPermissions.def()
}
}
impl ActiveModelBehavior for ActiveModel {}

View File

@@ -0,0 +1,9 @@
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0
pub use super::groups::Entity as Groups;
pub use super::groups_permissions::Entity as GroupsPermissions;
pub use super::permissions::Entity as Permissions;
pub use super::tower_sessions::Entity as TowerSessions;
pub use super::user_running_network_configs::Entity as UserRunningNetworkConfigs;
pub use super::users::Entity as Users;
pub use super::users_groups::Entity as UsersGroups;

View File

@@ -0,0 +1,19 @@
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0
use sea_orm::entity::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
#[sea_orm(table_name = "tower_sessions")]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false, column_type = "Text")]
pub id: String,
#[sea_orm(column_type = "Blob")]
pub data: Vec<u8>,
pub expiry_date: i32,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}
impl ActiveModelBehavior for ActiveModel {}

View File

@@ -0,0 +1,41 @@
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0
use sea_orm::entity::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
#[sea_orm(table_name = "user_running_network_configs")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
pub user_id: i32,
#[sea_orm(column_type = "Text")]
pub device_id: String,
#[sea_orm(column_type = "Text", unique)]
pub network_instance_id: String,
#[sea_orm(column_type = "Text")]
pub network_config: String,
pub disabled: bool,
pub create_time: DateTimeWithTimeZone,
pub update_time: DateTimeWithTimeZone,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(
belongs_to = "super::users::Entity",
from = "Column::UserId",
to = "super::users::Column::Id",
on_update = "Cascade",
on_delete = "Cascade"
)]
Users,
}
impl Related<super::users::Entity> for Entity {
fn to() -> RelationDef {
Relation::Users.def()
}
}
impl ActiveModelBehavior for ActiveModel {}

View File

@@ -0,0 +1,36 @@
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0
use sea_orm::entity::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
#[sea_orm(table_name = "users")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
#[sea_orm(unique)]
pub username: String,
pub password: String,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(has_many = "super::user_running_network_configs::Entity")]
UserRunningNetworkConfigs,
#[sea_orm(has_many = "super::users_groups::Entity")]
UsersGroups,
}
impl Related<super::user_running_network_configs::Entity> for Entity {
fn to() -> RelationDef {
Relation::UserRunningNetworkConfigs.def()
}
}
impl Related<super::users_groups::Entity> for Entity {
fn to() -> RelationDef {
Relation::UsersGroups.def()
}
}
impl ActiveModelBehavior for ActiveModel {}

View File

@@ -0,0 +1,47 @@
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0
use sea_orm::entity::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
#[sea_orm(table_name = "users_groups")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
pub user_id: i32,
pub group_id: i32,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(
belongs_to = "super::groups::Entity",
from = "Column::GroupId",
to = "super::groups::Column::Id",
on_update = "Cascade",
on_delete = "Cascade"
)]
Groups,
#[sea_orm(
belongs_to = "super::users::Entity",
from = "Column::UserId",
to = "super::users::Column::Id",
on_update = "Cascade",
on_delete = "Cascade"
)]
Users,
}
impl Related<super::groups::Entity> for Entity {
fn to() -> RelationDef {
Relation::Groups.def()
}
}
impl Related<super::users::Entity> for Entity {
fn to() -> RelationDef {
Relation::Users.def()
}
}
impl ActiveModelBehavior for ActiveModel {}

227
easytier-web/src/db/mod.rs Normal file
View File

@@ -0,0 +1,227 @@
// sea-orm-cli generate entity -u sqlite:./et.db -o easytier-web/src/db/entity/ --with-serde both --with-copy-enums
#[allow(unused_imports)]
pub mod entity;
use entity::user_running_network_configs;
use sea_orm::{
sea_query::OnConflict, ColumnTrait as _, DatabaseConnection, DbErr, EntityTrait as _,
QueryFilter as _, SqlxSqliteConnector, TransactionTrait as _,
};
use sea_orm_migration::MigratorTrait as _;
use sqlx::{migrate::MigrateDatabase as _, types::chrono, Sqlite, SqlitePool};
use crate::migrator;
type UserIdInDb = i32;
#[derive(Debug, Clone)]
pub struct Db {
db_path: String,
db: SqlitePool,
orm_db: DatabaseConnection,
}
impl Db {
pub async fn new<T: ToString>(db_path: T) -> anyhow::Result<Self> {
let db = Self::prepare_db(db_path.to_string().as_str()).await?;
let orm_db = SqlxSqliteConnector::from_sqlx_sqlite_pool(db.clone());
migrator::Migrator::up(&orm_db, None).await?;
Ok(Self {
db_path: db_path.to_string(),
db,
orm_db,
})
}
pub async fn memory_db() -> Self {
Self::new(":memory:").await.unwrap()
}
#[tracing::instrument(ret)]
async fn prepare_db(db_path: &str) -> anyhow::Result<SqlitePool> {
if !Sqlite::database_exists(db_path).await.unwrap_or(false) {
tracing::info!("Database not found, creating a new one");
Sqlite::create_database(db_path).await?;
}
let db = sqlx::pool::PoolOptions::new()
.max_lifetime(None)
.idle_timeout(None)
.connect(db_path)
.await?;
Ok(db)
}
pub fn inner(&self) -> SqlitePool {
self.db.clone()
}
pub fn orm_db(&self) -> &DatabaseConnection {
&self.orm_db
}
pub async fn insert_or_update_user_network_config<T: ToString>(
&self,
user_id: UserIdInDb,
device_id: uuid::Uuid,
network_inst_id: uuid::Uuid,
network_config: T,
) -> Result<(), DbErr> {
let txn = self.orm_db().begin().await?;
use entity::user_running_network_configs as urnc;
let on_conflict = OnConflict::column(urnc::Column::NetworkInstanceId)
.update_columns([
urnc::Column::NetworkConfig,
urnc::Column::Disabled,
urnc::Column::UpdateTime,
])
.to_owned();
let insert_m = urnc::ActiveModel {
user_id: sea_orm::Set(user_id),
device_id: sea_orm::Set(device_id.to_string()),
network_instance_id: sea_orm::Set(network_inst_id.to_string()),
network_config: sea_orm::Set(network_config.to_string()),
disabled: sea_orm::Set(false),
create_time: sea_orm::Set(chrono::Local::now().fixed_offset()),
update_time: sea_orm::Set(chrono::Local::now().fixed_offset()),
..Default::default()
};
urnc::Entity::insert(insert_m)
.on_conflict(on_conflict)
.do_nothing()
.exec(&txn)
.await?;
txn.commit().await
}
pub async fn delete_network_config(
&self,
user_id: UserIdInDb,
network_inst_id: uuid::Uuid,
) -> Result<(), DbErr> {
use entity::user_running_network_configs as urnc;
urnc::Entity::delete_many()
.filter(urnc::Column::UserId.eq(user_id))
.filter(urnc::Column::NetworkInstanceId.eq(network_inst_id.to_string()))
.exec(self.orm_db())
.await?;
Ok(())
}
pub async fn list_network_configs(
&self,
user_id: UserIdInDb,
device_id: Option<uuid::Uuid>,
only_enabled: bool,
) -> Result<Vec<user_running_network_configs::Model>, DbErr> {
use entity::user_running_network_configs as urnc;
let configs = urnc::Entity::find().filter(urnc::Column::UserId.eq(user_id));
let configs = if only_enabled {
configs.filter(urnc::Column::Disabled.eq(false))
} else {
configs
};
let configs = if let Some(device_id) = device_id {
configs.filter(urnc::Column::DeviceId.eq(device_id.to_string()))
} else {
configs
};
let configs = configs.all(self.orm_db()).await?;
Ok(configs)
}
pub async fn get_user_id<T: ToString>(
&self,
user_name: T,
) -> Result<Option<UserIdInDb>, DbErr> {
use entity::users as u;
let user = u::Entity::find()
.filter(u::Column::Username.eq(user_name.to_string()))
.one(self.orm_db())
.await?;
Ok(user.map(|u| u.id))
}
// TODO: currently we don't have a token system, so we just use the user name as token
pub async fn get_user_id_by_token<T: ToString>(
&self,
token: T,
) -> Result<Option<UserIdInDb>, DbErr> {
self.get_user_id(token).await
}
}
#[cfg(test)]
mod tests {
use sea_orm::{ColumnTrait, EntityTrait, QueryFilter as _};
use crate::db::{entity::user_running_network_configs, Db};
#[tokio::test]
async fn test_user_network_config_management() {
let db = Db::memory_db().await;
let user_id = 1;
let network_config = "test_config";
let inst_id = uuid::Uuid::new_v4();
let device_id = uuid::Uuid::new_v4();
db.insert_or_update_user_network_config(user_id, device_id, inst_id, network_config)
.await
.unwrap();
let result = user_running_network_configs::Entity::find()
.filter(user_running_network_configs::Column::UserId.eq(user_id))
.one(db.orm_db())
.await
.unwrap()
.unwrap();
println!("{:?}", result);
assert_eq!(result.network_config, network_config);
// overwrite the config
let network_config = "test_config2";
db.insert_or_update_user_network_config(user_id, device_id, inst_id, network_config)
.await
.unwrap();
let result2 = user_running_network_configs::Entity::find()
.filter(user_running_network_configs::Column::UserId.eq(user_id))
.one(db.orm_db())
.await
.unwrap()
.unwrap();
println!("device: {}, {:?}", device_id, result2);
assert_eq!(result2.network_config, network_config);
assert_eq!(result.create_time, result2.create_time);
assert_ne!(result.update_time, result2.update_time);
assert_eq!(
db.list_network_configs(user_id, Some(device_id), true)
.await
.unwrap()
.len(),
1
);
db.delete_network_config(user_id, inst_id).await.unwrap();
let result3 = user_running_network_configs::Entity::find()
.filter(user_running_network_configs::Column::UserId.eq(user_id))
.one(db.orm_db())
.await
.unwrap();
assert!(result3.is_none());
}
}

38
easytier-web/src/main.rs Normal file
View File

@@ -0,0 +1,38 @@
#![allow(dead_code)]
use std::sync::Arc;
use easytier::{
common::config::{ConfigLoader, ConsoleLoggerConfig, TomlConfigLoader},
tunnel::udp::UdpTunnelListener,
utils::init_logger,
};
mod client_manager;
mod db;
mod migrator;
mod restful;
#[tokio::main]
async fn main() {
let config = TomlConfigLoader::default();
config.set_console_logger_config(ConsoleLoggerConfig {
level: Some("trace".to_string()),
});
init_logger(config, false).unwrap();
// let db = db::Db::new(":memory:").await.unwrap();
let db = db::Db::new("et.db").await.unwrap();
let listener = UdpTunnelListener::new("udp://0.0.0.0:22020".parse().unwrap());
let mut mgr = client_manager::ClientManager::new(db.clone());
mgr.serve(listener).await.unwrap();
let mgr = Arc::new(mgr);
let mut restful_server =
restful::RestfulServer::new("0.0.0.0:11211".parse().unwrap(), mgr.clone(), db)
.await
.unwrap();
restful_server.start().await.unwrap();
tokio::signal::ctrl_c().await.unwrap();
}

View File

@@ -0,0 +1,364 @@
// src/migrator/m20220602_000001_create_bakery_table.rs (create new file)
use sea_orm_migration::{prelude::*, schema::*};
pub struct Migration;
impl MigrationName for Migration {
fn name(&self) -> &str {
"m20241029_000001_init"
}
}
#[derive(DeriveIden)]
pub enum Users {
Table,
Id,
Username,
Password,
}
#[derive(DeriveIden)]
enum Groups {
Table,
Id,
Name,
}
#[derive(DeriveIden)]
enum Permissions {
Table,
Id,
Name,
}
#[derive(DeriveIden)]
enum UsersGroups {
Table,
Id,
UserId,
GroupId,
}
#[derive(DeriveIden)]
enum GroupsPermissions {
Table,
Id,
GroupId,
PermissionId,
}
#[derive(DeriveIden)]
enum UserRunningNetworkConfigs {
Table,
Id,
UserId,
DeviceId,
NetworkInstanceId,
NetworkConfig,
Disabled,
CreateTime,
UpdateTime,
}
#[async_trait::async_trait]
impl MigrationTrait for Migration {
// Define how to apply this migration: Create the Bakery table.
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
// Create the `users` table.
manager
.create_table(
Table::create()
.if_not_exists()
.table(Users::Table)
.col(pk_auto(Users::Id).not_null())
.col(string(Users::Username).not_null().unique_key())
.col(string(Users::Password).not_null())
.to_owned(),
)
.await?;
manager
.create_index(
Index::create()
.name("idx_users_username")
.table(Users::Table)
.col(Users::Username)
.to_owned(),
)
.await?;
// Create the `groups` table.
manager
.create_table(
Table::create()
.if_not_exists()
.table(Groups::Table)
.col(pk_auto(Groups::Id).not_null())
.col(string(Groups::Name).not_null().unique_key())
.to_owned(),
)
.await?;
manager
.create_index(
Index::create()
.name("idx_groups_name")
.table(Groups::Table)
.col(Groups::Name)
.to_owned(),
)
.await?;
// Create the `permissions` table.
manager
.create_table(
Table::create()
.if_not_exists()
.table(Permissions::Table)
.col(pk_auto(Permissions::Id).not_null())
.col(string(Permissions::Name).not_null().unique_key())
.to_owned(),
)
.await?;
// Create the `users_groups` table.
manager
.create_table(
Table::create()
.if_not_exists()
.table(UsersGroups::Table)
.col(pk_auto(UsersGroups::Id).not_null())
.col(integer(UsersGroups::UserId).not_null())
.col(integer(UsersGroups::GroupId).not_null())
.foreign_key(
ForeignKey::create()
.name("fk_users_groups_user_id_to_users_id")
.from(UsersGroups::Table, UsersGroups::UserId)
.to(Users::Table, Users::Id)
.on_delete(ForeignKeyAction::Cascade)
.on_update(ForeignKeyAction::Cascade),
)
.foreign_key(
ForeignKey::create()
.name("fk_users_groups_group_id_to_groups_id")
.from(UsersGroups::Table, UsersGroups::GroupId)
.to(Groups::Table, Groups::Id)
.on_delete(ForeignKeyAction::Cascade)
.on_update(ForeignKeyAction::Cascade),
)
.to_owned(),
)
.await?;
// Create the `groups_permissions` table.
manager
.create_table(
Table::create()
.if_not_exists()
.table(GroupsPermissions::Table)
.col(pk_auto(GroupsPermissions::Id).not_null())
.col(integer(GroupsPermissions::GroupId).not_null())
.col(integer(GroupsPermissions::PermissionId).not_null())
.foreign_key(
ForeignKey::create()
.name("fk_groups_permissions_group_id_to_groups_id")
.from(GroupsPermissions::Table, GroupsPermissions::GroupId)
.to(Groups::Table, Groups::Id)
.on_delete(ForeignKeyAction::Cascade)
.on_update(ForeignKeyAction::Cascade),
)
.foreign_key(
ForeignKey::create()
.name("fk_groups_permissions_permission_id_to_permissions_id")
.from(GroupsPermissions::Table, GroupsPermissions::PermissionId)
.to(Permissions::Table, Permissions::Id)
.on_delete(ForeignKeyAction::Cascade)
.on_update(ForeignKeyAction::Cascade),
)
.to_owned(),
)
.await?;
// create user running network configs table
manager
.create_table(
Table::create()
.if_not_exists()
.table(UserRunningNetworkConfigs::Table)
.col(pk_auto(UserRunningNetworkConfigs::Id).not_null())
.col(integer(UserRunningNetworkConfigs::UserId).not_null())
.col(text(UserRunningNetworkConfigs::DeviceId).not_null())
.col(
text(UserRunningNetworkConfigs::NetworkInstanceId)
.unique_key()
.not_null(),
)
.col(text(UserRunningNetworkConfigs::NetworkConfig).not_null())
.col(
boolean(UserRunningNetworkConfigs::Disabled)
.not_null()
.default(false),
)
.col(timestamp_with_time_zone(UserRunningNetworkConfigs::CreateTime).not_null())
.col(timestamp_with_time_zone(UserRunningNetworkConfigs::UpdateTime).not_null())
.foreign_key(
ForeignKey::create()
.name("fk_user_running_network_configs_user_id_to_users_id")
.from(
UserRunningNetworkConfigs::Table,
UserRunningNetworkConfigs::UserId,
)
.to(Users::Table, Users::Id)
.on_delete(ForeignKeyAction::Cascade)
.on_update(ForeignKeyAction::Cascade),
)
.to_owned(),
)
.await?;
manager
.create_index(
Index::create()
.name("idx_user_running_network_configs_user_id")
.table(UserRunningNetworkConfigs::Table)
.col(UserRunningNetworkConfigs::UserId)
.to_owned(),
)
.await?;
// prepare data
let user = Query::insert()
.into_table(Users::Table)
.columns(vec![Users::Username, Users::Password])
.values_panic(vec![
"user".into(),
"$argon2i$v=19$m=16,t=2,p=1$aGVyRDBrcnRycnlaMDhkbw$449SEcv/qXf+0fnI9+fYVQ".into(), // user (md5summed)
])
.to_owned();
manager.exec_stmt(user).await?;
let admin = Query::insert()
.into_table(Users::Table)
.columns(vec![Users::Username, Users::Password])
.values_panic(vec![
"admin".into(),
"$argon2i$v=19$m=16,t=2,p=1$bW5idXl0cmY$61n+JxL4r3dwLPAEDlDdtg".into(), // admin (md5summed)
])
.to_owned();
manager.exec_stmt(admin).await?;
let users = Query::insert()
.into_table(Groups::Table)
.columns(vec![Groups::Name])
.values_panic(vec!["users".into()])
.to_owned();
manager.exec_stmt(users).await?;
let admins = Query::insert()
.into_table(Groups::Table)
.columns(vec![Groups::Name])
.values_panic(vec!["admins".into()])
.to_owned();
manager.exec_stmt(admins).await?;
let sessions = Query::insert()
.into_table(Permissions::Table)
.columns(vec![Permissions::Name])
.values_panic(vec!["sessions".into()])
.to_owned();
manager.exec_stmt(sessions).await?;
let devices = Query::insert()
.into_table(Permissions::Table)
.columns(vec![Permissions::Name])
.values_panic(vec!["devices".into()])
.to_owned();
manager.exec_stmt(devices).await?;
let users_devices = Query::insert()
.into_table(GroupsPermissions::Table)
.columns(vec![
GroupsPermissions::GroupId,
GroupsPermissions::PermissionId,
])
.select_from(
Query::select()
.column((Groups::Table, Groups::Id))
.column((Permissions::Table, Permissions::Id))
.from(Groups::Table)
.full_outer_join(Permissions::Table, all![])
.cond_where(any![
// users have devices permission
Expr::col((Groups::Table, Groups::Name))
.eq("users")
.and(Expr::col((Permissions::Table, Permissions::Name)).eq("devices")),
// admins have all permissions
Expr::col((Groups::Table, Groups::Name)).eq("admins"),
])
.to_owned(),
)
.unwrap()
.to_owned();
manager.exec_stmt(users_devices).await?;
let add_user_to_users = Query::insert()
.into_table(UsersGroups::Table)
.columns(vec![UsersGroups::UserId, UsersGroups::GroupId])
.select_from(
Query::select()
.column((Users::Table, Users::Id))
.column((Groups::Table, Groups::Id))
.from(Users::Table)
.full_outer_join(Groups::Table, all![])
.cond_where(
Expr::col(Users::Username)
.eq("user")
.and(Expr::col(Groups::Name).eq("users")),
)
.to_owned(),
)
.unwrap()
.to_owned();
manager.exec_stmt(add_user_to_users).await?;
let add_admin_to_admins = Query::insert()
.into_table(UsersGroups::Table)
.columns(vec![UsersGroups::UserId, UsersGroups::GroupId])
.select_from(
Query::select()
.column((Users::Table, Users::Id))
.column((Groups::Table, Groups::Id))
.from(Users::Table)
.full_outer_join(Groups::Table, all![])
.cond_where(
Expr::col(Users::Username)
.eq("admin")
.and(Expr::col(Groups::Name).eq("admins")),
)
.to_owned(),
)
.unwrap()
.to_owned();
manager.exec_stmt(add_admin_to_admins).await?;
Ok(())
}
// Define how to rollback this migration: Drop the Bakery table.
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.drop_table(Table::drop().table(Users::Table).to_owned())
.await?;
manager
.drop_table(Table::drop().table(Groups::Table).to_owned())
.await?;
manager
.drop_table(Table::drop().table(Permissions::Table).to_owned())
.await?;
manager
.drop_table(Table::drop().table(UsersGroups::Table).to_owned())
.await?;
manager
.drop_table(Table::drop().table(GroupsPermissions::Table).to_owned())
.await?;
Ok(())
}
}

Some files were not shown because too many files have changed in this diff Show More