Compare commits

..

20 Commits

Author SHA1 Message Date
sijie.sun f14875aa3f bump version to v1.2.2 2024-08-13 00:46:32 +08:00
sijie.sun 6391dceb62 udpate release.yml 2024-08-13 00:46:32 +08:00
Sijie.Sun 29806b899a add draft release github action (#246) 2024-08-13 00:06:12 +08:00
Sunakier d63a3c01e4 Fix the installation description in the README (#243)
* fix README wording error
2024-08-11 19:43:54 +08:00
Sijie.Sun b6fb7ac962 add two cmd line option (#241)
1. disable_p2p: only using specified peers to relay packets.
2. relay_all_peer_rpc: allow relay route info for networks not in whitelist
2024-08-10 00:26:54 +08:00
Sunakier 1d22fdc972 Add one-click shell script (#196)
* Add one-click install shell script
2024-08-10 00:04:39 +08:00
Xiao Tan d135dd5a6f Revise the description of relay_network_whitelist (#235) 2024-08-09 23:52:05 +08:00
WillisXue 7cae63cb17 fix win tun name, clean up custom tun name (#234) 2024-08-08 23:03:41 +08:00
Sijie.Sun 232165eff3 fix bugs (#236) 2024-08-08 22:03:22 +08:00
Sijie.Sun 2bc4dd8c53 Update install_rust.sh (#237) 2024-08-08 20:07:22 +08:00
sijie.sun cca105e91d add chinese help message 2024-08-08 08:05:42 +08:00
sijie.sun 3e52490d1b update dep and bump version 2024-08-07 23:48:03 +08:00
sijie.sun d1293276ce use tun2
meh/rust-tun has bug on windows and may stuck on large traffic.
2024-08-07 15:58:23 +08:00
sijie.sun 4a5e426730 fix android route and peer conn disconn bug
1. android correctly add route
2. use mock nic ctx to consume packets
2024-08-06 22:19:30 +08:00
sijie.sun fdc2755291 fix set dev name not work on windows 2024-08-05 11:47:43 +08:00
sijie.sun b4fbcd8d80 use random ip from dns record instead of only first one 2024-08-04 23:42:33 +08:00
sijie.sun 2415cb211e run on v7 need rustc < 1.78 2024-08-04 11:07:24 +08:00
sijie.sun 5e51784803 support unicode hostname 2024-08-04 11:07:24 +08:00
Sijie.Sun 5f0d71b0fe check trigger user for docker action (#219) 2024-08-04 00:37:58 +08:00
sijie.sun 71d41f0a70 introduce docker image updater 2024-08-04 00:28:34 +08:00
37 changed files with 3953 additions and 2288 deletions
+35
View File
@@ -0,0 +1,35 @@
FROM alpine:latest AS builder
ARG TARGETPLATFORM
COPY . /tmp/artifacts
RUN mkdir -p /tmp/output; \
cd /tmp/artifacts; \
ARTIFACT_ARCH=""; \
if [ "$TARGETPLATFORM" = "linux/amd64" ]; then \
ARTIFACT_ARCH="x86_64"; \
elif [ "$TARGETPLATFORM" = "linux/arm64" ]; then \
ARTIFACT_ARCH="aarch64"; \
else \
echo "Unsupported architecture: $TARGETARCH"; \
exit 1; \
fi; \
cp /tmp/artifacts/easytier-linux-${ARTIFACT_ARCH}/* /tmp/output;
FROM alpine:latest
WORKDIR /app
COPY --from=builder --chmod=755 /tmp/output/* /usr/local/bin
# tcp
EXPOSE 11010/tcp
# udp
EXPOSE 11010/udp
# wg
EXPOSE 11011/udp
# ws
EXPOSE 11011/tcp
# wss
EXPOSE 11012/tcp
ENTRYPOINT ["easytier-core"]
+1 -1
View File
@@ -28,7 +28,7 @@ jobs:
# All of these options are optional, so you can remove them if you are happy with the defaults
concurrent_skipping: 'never'
skip_after_successful_duplicate: 'true'
paths: '["Cargo.toml", "Cargo.lock", "easytier/**", ".github/workflows/core.yml"]'
paths: '["Cargo.toml", "Cargo.lock", "easytier/**", ".github/workflows/core.yml", ".github/workflows/install_rust.sh"]'
build:
strategy:
fail-fast: false
+61
View File
@@ -0,0 +1,61 @@
name: EasyTier Docker
on:
workflow_dispatch:
inputs:
run_id:
description: 'The run id of EasyTier-Core Action in EasyTier repo'
type: number
default: 10228239965
required: true
image_tag:
description: 'Tag for this image build'
type: string
default: 'v1.2.0'
required: true
mark_latest:
description: 'Mark this image as latest'
type: boolean
default: false
required: true
jobs:
docker:
if: contains('["KKRainbow"]', github.actor)
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v4
-
name: Set up QEMU
uses: docker/setup-qemu-action@v3
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
-
name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Download artifact
id: download-artifact
uses: dawidd6/action-download-artifact@v6
with:
github_token: ${{secrets.GITHUB_TOKEN}}
run_id: ${{ inputs.run_id }}
repo: EasyTier/EasyTier
path: docker_context
- name: List files
run: |
ls -l -R .
-
name: Build and push
uses: docker/build-push-action@v6
with:
context: ./docker_context
platforms: linux/amd64,linux/arm64
push: true
file: .github/workflows/Dockerfile
tags: easytier/easytier:${{ inputs.image_tag }}${{ inputs.mark_latest && ',easytier/easytier:latest' || '' }},
+1 -1
View File
@@ -28,7 +28,7 @@ jobs:
# All of these options are optional, so you can remove them if you are happy with the defaults
concurrent_skipping: 'never'
skip_after_successful_duplicate: 'true'
paths: '["Cargo.toml", "Cargo.lock", "easytier/**", "easytier-gui/**", ".github/workflows/gui.yml"]'
paths: '["Cargo.toml", "Cargo.lock", "easytier/**", "easytier-gui/**", ".github/workflows/gui.yml", ".github/workflows/install_rust.sh"]'
build-gui:
strategy:
fail-fast: false
+9 -3
View File
@@ -57,8 +57,8 @@ fi
# see https://github.com/rust-lang/rustup/issues/3709
rustup set auto-self-update disable
rustup install 1.79
rustup default 1.79
rustup install 1.77
rustup default 1.77
# mips/mipsel cannot add target from rustup, need compile by ourselves
if [[ $OS =~ ^ubuntu.*$ && $TARGET =~ ^mips.*$ ]]; then
@@ -72,7 +72,13 @@ if [[ $OS =~ ^ubuntu.*$ && $TARGET =~ ^mips.*$ ]]; then
rustup toolchain install nightly-x86_64-unknown-linux-gnu
rustup component add rust-src --toolchain nightly-x86_64-unknown-linux-gnu
cd -
# https://github.com/rust-lang/rust/issues/128808
# remove it after Cargo or rustc fix this.
RUST_LIB_SRC=$HOME/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/
if [[ -f $RUST_LIB_SRC/library/Cargo.lock && ! -f $RUST_LIB_SRC/Cargo.lock ]]; then
cp -f $RUST_LIB_SRC/library/Cargo.lock $RUST_LIB_SRC/Cargo.lock
fi
else
rustup target add $TARGET
if [[ $GUI_TARGET != '' ]]; then
+1 -1
View File
@@ -28,7 +28,7 @@ jobs:
# All of these options are optional, so you can remove them if you are happy with the defaults
concurrent_skipping: 'never'
skip_after_successful_duplicate: 'true'
paths: '["Cargo.toml", "Cargo.lock", "easytier/**", "easytier-gui/**", "tauri-plugin-vpnservice/**", ".github/workflows/mobile.yml"]'
paths: '["Cargo.toml", "Cargo.lock", "easytier/**", "easytier-gui/**", "tauri-plugin-vpnservice/**", ".github/workflows/mobile.yml", ".github/workflows/install_rust.sh"]'
build-mobile:
strategy:
fail-fast: false
+89
View File
@@ -0,0 +1,89 @@
name: EasyTier Release
on:
workflow_dispatch:
inputs:
core_run_id:
description: 'The run id of EasyTier-Core Action in EasyTier repo'
type: number
default: 10322498549
required: true
gui_run_id:
description: 'The run id of EasyTier-GUI Action in EasyTier repo'
type: number
default: 10322498557
required: true
mobile_run_id:
description: 'The run id of EasyTier-Mobile Action in EasyTier repo'
type: number
default: 10322498555
required: true
version:
description: 'version for this release'
type: string
default: 'v1.2.2'
required: true
make_latest:
description: 'Mark this release as latest'
type: boolean
default: true
required: true
permissions:
contents: write
jobs:
release:
if: contains('["KKRainbow"]', github.actor)
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v4
- name: Download Core Artifact
uses: dawidd6/action-download-artifact@v6
with:
github_token: ${{secrets.GITHUB_TOKEN}}
run_id: ${{ inputs.core_run_id }}
repo: EasyTier/EasyTier
path: release_assets
- name: Download GUI Artifact
uses: dawidd6/action-download-artifact@v6
with:
github_token: ${{secrets.GITHUB_TOKEN}}
run_id: ${{ inputs.gui_run_id }}
repo: EasyTier/EasyTier
path: release_assets
- name: Download GUI Artifact
uses: dawidd6/action-download-artifact@v6
with:
github_token: ${{secrets.GITHUB_TOKEN}}
run_id: ${{ inputs.mobile_run_id }}
repo: EasyTier/EasyTier
path: release_assets
- name: Zip release assets
env:
VERSION: ${{ inputs.version }}
run: |
cd release_assets
ls -l -R ./
chmod -R 755 .
mkdir ../zipped_assets
for x in `ls`; do
zip ../zipped_assets/$x-${VERSION}.zip $x/*;
done
- name: Release
uses: softprops/action-gh-release@v2
with:
name: ${{ inputs.version }}
draft: true
files: |
./zipped_assets/*
token: ${{ secrets.GITHUB_TOKEN }}
tag_name: ${{ inputs.version }}
Generated
+694 -526
View File
File diff suppressed because it is too large Load Diff
+12 -1
View File
@@ -48,7 +48,18 @@
```sh
cargo install --git https://github.com/EasyTier/EasyTier.git
```
4. **Install by Docker Compose**
Please visit the [EasyTier Official Website](https://www.easytier.top/en/) to view the full documentation.
5. **Install by script (For Linux Only)**
```sh
wget -O /tmp/easytier.sh "https://raw.githubusercontent.com/EasyTier/EasyTier/main/script/easytier.sh" && bash /tmp/easytier.sh install
```
You can also uninstall/update Easytier by the command "uninstall" or "update" of this script
## Quick Start
> The following text only describes the use of the command-line tool; the GUI program can be configured by referring to the following concepts.
+12
View File
@@ -49,6 +49,18 @@
cargo install --git https://github.com/EasyTier/EasyTier.git
```
4. **通过Docker Compose安装**
请访问 [EasyTier 官网](https://www.easytier.top/) 以查看完整的文档。
5. **使用一键脚本安装 (仅适用于 Linux)**
```sh
wget -O /tmp/easytier.sh "https://raw.githubusercontent.com/EasyTier/EasyTier/main/script/easytier.sh" && bash /tmp/easytier.sh install
```
使用本脚本安装的 Easytier 可以使用脚本的 uninstall/update 对其卸载/升级
## 快速开始
> 下文仅描述命令行工具的使用,图形界面程序可参考下述概念自行配置。
+31 -29
View File
@@ -1,7 +1,7 @@
{
"name": "easytier-gui",
"type": "module",
"version": "1.2.0",
"version": "1.2.2",
"private": true,
"scripts": {
"dev": "vite",
@@ -12,47 +12,49 @@
"lint:fix": "eslint . --ignore-pattern src-tauri --fix"
},
"dependencies": {
"@primevue/themes": "^4.0.0",
"@tauri-apps/plugin-clipboard-manager": "2.1.0-beta.4",
"@tauri-apps/plugin-os": "2.0.0-beta.6",
"@tauri-apps/plugin-process": "2.0.0-beta.6",
"@tauri-apps/plugin-shell": "2.0.0-beta.7",
"@primevue/themes": "^4.0.4",
"@tauri-apps/plugin-clipboard-manager": "2.0.0-rc.0",
"@tauri-apps/plugin-os": "2.0.0-rc.0",
"@tauri-apps/plugin-process": "2.0.0-rc.0",
"@tauri-apps/plugin-shell": "2.0.0-rc.0",
"aura": "link:@primevue/themes/aura",
"pinia": "^2.1.7",
"pinia": "^2.2.1",
"primeflex": "^3.3.1",
"primeicons": "^7.0.0",
"primevue": "^4.0.0",
"tauri-plugin-vpnservice-api": "link:../tauri-plugin-vpnservice/",
"vue": "^3.4.31",
"primevue": "^4.0.4",
"tauri-plugin-vpnservice-api": "link:../tauri-plugin-vpnservice",
"vue": "^3.4.36",
"vue-i18n": "^9.13.1",
"vue-router": "^4.4.0"
"vue-router": "^4.4.3"
},
"devDependencies": {
"@antfu/eslint-config": "^2.21.3",
"@antfu/eslint-config": "^2.24.1",
"@intlify/unplugin-vue-i18n": "^4.0.0",
"@primevue/auto-import-resolver": "^4.0.0",
"@tauri-apps/api": "2.0.0-beta.14",
"@tauri-apps/cli": "2.0.0-beta.21",
"@types/node": "^20.14.10",
"@primevue/auto-import-resolver": "^4.0.4",
"@sveltejs/vite-plugin-svelte": "^3.1.1",
"@tauri-apps/api": "2.0.0-rc.0",
"@tauri-apps/cli": "2.0.0-rc.1",
"@types/node": "^20.14.14",
"@types/uuid": "^9.0.8",
"@vitejs/plugin-vue": "^5.0.5",
"@vitejs/plugin-vue": "^5.1.2",
"@vue-macros/volar": "^0.19.1",
"autoprefixer": "^10.4.19",
"eslint": "^9.6.0",
"autoprefixer": "^10.4.20",
"eslint": "^9.8.0",
"eslint-plugin-format": "^0.1.2",
"postcss": "^8.4.39",
"tailwindcss": "^3.4.4",
"typescript": "^5.5.3",
"unplugin-auto-import": "^0.17.6",
"unplugin-vue-components": "^0.27.2",
"unplugin-vue-macros": "^2.9.5",
"internal-ip": "^8.0.0",
"postcss": "^8.4.41",
"tailwindcss": "^3.4.7",
"typescript": "^5.5.4",
"unplugin-auto-import": "^0.17.8",
"unplugin-vue-components": "^0.27.3",
"unplugin-vue-macros": "^2.11.4",
"unplugin-vue-markdown": "^0.26.2",
"unplugin-vue-router": "^0.8.8",
"uuid": "^9.0.1",
"vite": "^5.3.3",
"vite-plugin-vue-devtools": "^7.3.5",
"vite": "^5.3.5",
"vite-plugin-vue-devtools": "^7.3.7",
"vite-plugin-vue-layouts": "^0.11.0",
"vue-i18n": "^9.13.1",
"vue-tsc": "^2.0.26"
"vue-tsc": "^2.0.29"
}
}
}
+2107 -1314
View File
File diff suppressed because it is too large Load Diff
+14 -10
View File
@@ -1,6 +1,6 @@
[package]
name = "easytier-gui"
version = "1.2.0"
version = "1.2.2"
description = "EasyTier GUI"
authors = ["you"]
edition = "2021"
@@ -12,10 +12,14 @@ name = "app_lib"
crate-type = ["staticlib", "cdylib", "rlib"]
[build-dependencies]
tauri-build = { version = "2.0.0-beta", features = [] }
tauri-build = { version = "2.0.0-rc", features = [] }
[dependencies]
tauri = { version = "2.0.0-beta", features = [ "tray-icon", "image-png", "image-ico"] }
tauri = { version = "2.0.0-rc", features = [
"tray-icon",
"image-png",
"image-ico",
] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
@@ -26,20 +30,20 @@ anyhow = "1.0"
chrono = { version = "0.4.37", features = ["serde"] }
once_cell = "1.18.0"
dashmap = "5.5.3"
dashmap = "6.0"
privilege = "0.3"
gethostname = "0.4.3"
gethostname = "0.5"
auto-launch = "0.5.0"
dunce = "1.0.4"
tauri-plugin-shell = "2.0.0-beta.8"
tauri-plugin-process = "2.0.0-beta.7"
tauri-plugin-clipboard-manager = "2.1.0-beta.5"
tauri-plugin-positioner = { version = "2.0.0-beta", features = ["tray-icon"] }
tauri-plugin-shell = "2.0.0-rc"
tauri-plugin-process = "2.0.0-rc"
tauri-plugin-clipboard-manager = "2.0.0-rc"
tauri-plugin-positioner = { version = "2.0.0-rc", features = ["tray-icon"] }
tauri-plugin-vpnservice = { path = "../../tauri-plugin-vpnservice" }
tauri-plugin-os = "2.0.0-beta.7"
tauri-plugin-os = "2.0.0-rc"
[features]
@@ -6,17 +6,17 @@
"main"
],
"permissions": [
"path:default",
"event:default",
"window:default",
"window:allow-is-visible",
"window:allow-show",
"window:allow-hide",
"window:allow-set-focus",
"app:default",
"resources:default",
"menu:default",
"tray:default",
"core:path:default",
"core:event:default",
"core:window:default",
"core:window:allow-is-visible",
"core:window:allow-show",
"core:window:allow-hide",
"core:window:allow-set-focus",
"core:app:default",
"core:resources:default",
"core:menu:default",
"core:tray:default",
"shell:allow-open",
"process:allow-exit",
"clipboard-manager:allow-read-text",
@@ -24,16 +24,16 @@
"shell:default",
"process:default",
"clipboard-manager:default",
"tray:default",
"tray:allow-new",
"tray:allow-set-menu",
"tray:allow-set-title",
"tray:allow-remove-by-id",
"tray:allow-get-by-id",
"tray:allow-set-icon",
"tray:allow-set-icon-as-template",
"tray:allow-set-show-menu-on-left-click",
"tray:allow-set-tooltip",
"core:tray:default",
"core:tray:allow-new",
"core:tray:allow-set-menu",
"core:tray:allow-set-title",
"core:tray:allow-remove-by-id",
"core:tray:allow-get-by-id",
"core:tray:allow-set-icon",
"core:tray:allow-set-icon-as-template",
"core:tray:allow-set-show-menu-on-left-click",
"core:tray:allow-set-tooltip",
"vpnservice:allow-ping",
"vpnservice:allow-prepare-vpn",
"vpnservice:allow-start-vpn",
+1 -1
View File
@@ -17,7 +17,7 @@
"createUpdaterArtifacts": false
},
"productName": "easytier-gui",
"version": "1.2.0",
"version": "1.2.2",
"identifier": "com.kkrainbow.easytier",
"plugins": {},
"app": {
-95
View File
@@ -201,98 +201,3 @@ declare module 'vue' {
readonly watchSyncEffect: UnwrapRef<typeof import('vue')['watchSyncEffect']>
}
}
declare module '@vue/runtime-core' {
interface GlobalComponents {}
interface ComponentCustomProperties {
readonly EffectScope: UnwrapRef<typeof import('vue')['EffectScope']>
readonly MenuItemExit: UnwrapRef<typeof import('./composables/tray')['MenuItemExit']>
readonly MenuItemShow: UnwrapRef<typeof import('./composables/tray')['MenuItemShow']>
readonly acceptHMRUpdate: UnwrapRef<typeof import('pinia')['acceptHMRUpdate']>
readonly collectNetworkInfos: UnwrapRef<typeof import('./composables/network')['collectNetworkInfos']>
readonly computed: UnwrapRef<typeof import('vue')['computed']>
readonly createApp: UnwrapRef<typeof import('vue')['createApp']>
readonly createPinia: UnwrapRef<typeof import('pinia')['createPinia']>
readonly customRef: UnwrapRef<typeof import('vue')['customRef']>
readonly defineAsyncComponent: UnwrapRef<typeof import('vue')['defineAsyncComponent']>
readonly defineComponent: UnwrapRef<typeof import('vue')['defineComponent']>
readonly definePage: UnwrapRef<typeof import('unplugin-vue-router/runtime')['definePage']>
readonly defineStore: UnwrapRef<typeof import('pinia')['defineStore']>
readonly effectScope: UnwrapRef<typeof import('vue')['effectScope']>
readonly generateMenuItem: UnwrapRef<typeof import('./composables/tray')['generateMenuItem']>
readonly getActivePinia: UnwrapRef<typeof import('pinia')['getActivePinia']>
readonly getCurrentInstance: UnwrapRef<typeof import('vue')['getCurrentInstance']>
readonly getCurrentScope: UnwrapRef<typeof import('vue')['getCurrentScope']>
readonly getOsHostname: UnwrapRef<typeof import('./composables/network')['getOsHostname']>
readonly h: UnwrapRef<typeof import('vue')['h']>
readonly initMobileVpnService: UnwrapRef<typeof import('./composables/mobile_vpn')['initMobileVpnService']>
readonly inject: UnwrapRef<typeof import('vue')['inject']>
readonly isProxy: UnwrapRef<typeof import('vue')['isProxy']>
readonly isReactive: UnwrapRef<typeof import('vue')['isReactive']>
readonly isReadonly: UnwrapRef<typeof import('vue')['isReadonly']>
readonly isRef: UnwrapRef<typeof import('vue')['isRef']>
readonly loadRunningInstanceIdsFromLocalStorage: UnwrapRef<typeof import('./stores/network')['loadRunningInstanceIdsFromLocalStorage']>
readonly mapActions: UnwrapRef<typeof import('pinia')['mapActions']>
readonly mapGetters: UnwrapRef<typeof import('pinia')['mapGetters']>
readonly mapState: UnwrapRef<typeof import('pinia')['mapState']>
readonly mapStores: UnwrapRef<typeof import('pinia')['mapStores']>
readonly mapWritableState: UnwrapRef<typeof import('pinia')['mapWritableState']>
readonly markRaw: UnwrapRef<typeof import('vue')['markRaw']>
readonly nextTick: UnwrapRef<typeof import('vue')['nextTick']>
readonly onActivated: UnwrapRef<typeof import('vue')['onActivated']>
readonly onBeforeMount: UnwrapRef<typeof import('vue')['onBeforeMount']>
readonly onBeforeRouteLeave: UnwrapRef<typeof import('vue-router/auto')['onBeforeRouteLeave']>
readonly onBeforeRouteUpdate: UnwrapRef<typeof import('vue-router/auto')['onBeforeRouteUpdate']>
readonly onBeforeUnmount: UnwrapRef<typeof import('vue')['onBeforeUnmount']>
readonly onBeforeUpdate: UnwrapRef<typeof import('vue')['onBeforeUpdate']>
readonly onDeactivated: UnwrapRef<typeof import('vue')['onDeactivated']>
readonly onErrorCaptured: UnwrapRef<typeof import('vue')['onErrorCaptured']>
readonly onMounted: UnwrapRef<typeof import('vue')['onMounted']>
readonly onRenderTracked: UnwrapRef<typeof import('vue')['onRenderTracked']>
readonly onRenderTriggered: UnwrapRef<typeof import('vue')['onRenderTriggered']>
readonly onScopeDispose: UnwrapRef<typeof import('vue')['onScopeDispose']>
readonly onServerPrefetch: UnwrapRef<typeof import('vue')['onServerPrefetch']>
readonly onUnmounted: UnwrapRef<typeof import('vue')['onUnmounted']>
readonly onUpdated: UnwrapRef<typeof import('vue')['onUpdated']>
readonly parseNetworkConfig: UnwrapRef<typeof import('./composables/network')['parseNetworkConfig']>
readonly prepareVpnService: UnwrapRef<typeof import('./composables/mobile_vpn')['prepareVpnService']>
readonly provide: UnwrapRef<typeof import('vue')['provide']>
readonly reactive: UnwrapRef<typeof import('vue')['reactive']>
readonly readonly: UnwrapRef<typeof import('vue')['readonly']>
readonly ref: UnwrapRef<typeof import('vue')['ref']>
readonly resolveComponent: UnwrapRef<typeof import('vue')['resolveComponent']>
readonly retainNetworkInstance: UnwrapRef<typeof import('./composables/network')['retainNetworkInstance']>
readonly runNetworkInstance: UnwrapRef<typeof import('./composables/network')['runNetworkInstance']>
readonly setActivePinia: UnwrapRef<typeof import('pinia')['setActivePinia']>
readonly setAutoLaunchStatus: UnwrapRef<typeof import('./composables/network')['setAutoLaunchStatus']>
readonly setLoggingLevel: UnwrapRef<typeof import('./composables/network')['setLoggingLevel']>
readonly setMapStoreSuffix: UnwrapRef<typeof import('pinia')['setMapStoreSuffix']>
readonly setTrayMenu: UnwrapRef<typeof import('./composables/tray')['setTrayMenu']>
readonly setTrayRunState: UnwrapRef<typeof import('./composables/tray')['setTrayRunState']>
readonly setTrayTooltip: UnwrapRef<typeof import('./composables/tray')['setTrayTooltip']>
readonly setTunFd: UnwrapRef<typeof import('./composables/network')['setTunFd']>
readonly shallowReactive: UnwrapRef<typeof import('vue')['shallowReactive']>
readonly shallowReadonly: UnwrapRef<typeof import('vue')['shallowReadonly']>
readonly shallowRef: UnwrapRef<typeof import('vue')['shallowRef']>
readonly storeToRefs: UnwrapRef<typeof import('pinia')['storeToRefs']>
readonly toRaw: UnwrapRef<typeof import('vue')['toRaw']>
readonly toRef: UnwrapRef<typeof import('vue')['toRef']>
readonly toRefs: UnwrapRef<typeof import('vue')['toRefs']>
readonly toValue: UnwrapRef<typeof import('vue')['toValue']>
readonly triggerRef: UnwrapRef<typeof import('vue')['triggerRef']>
readonly unref: UnwrapRef<typeof import('vue')['unref']>
readonly useAttrs: UnwrapRef<typeof import('vue')['useAttrs']>
readonly useCssModule: UnwrapRef<typeof import('vue')['useCssModule']>
readonly useCssVars: UnwrapRef<typeof import('vue')['useCssVars']>
readonly useI18n: UnwrapRef<typeof import('vue-i18n')['useI18n']>
readonly useLink: UnwrapRef<typeof import('vue-router/auto')['useLink']>
readonly useNetworkStore: UnwrapRef<typeof import('./stores/network')['useNetworkStore']>
readonly useRoute: UnwrapRef<typeof import('vue-router/auto')['useRoute']>
readonly useRouter: UnwrapRef<typeof import('vue-router/auto')['useRouter']>
readonly useSlots: UnwrapRef<typeof import('vue')['useSlots']>
readonly useTray: UnwrapRef<typeof import('./composables/tray')['useTray']>
readonly watch: UnwrapRef<typeof import('vue')['watch']>
readonly watchEffect: UnwrapRef<typeof import('vue')['watchEffect']>
readonly watchPostEffect: UnwrapRef<typeof import('vue')['watchPostEffect']>
readonly watchSyncEffect: UnwrapRef<typeof import('vue')['watchSyncEffect']>
}
}
@@ -159,14 +159,11 @@ async function watchNetworkInstance() {
if (subscribe_running) {
return
}
console.log('network instance change')
subscribe_running = true
try {
await onNetworkInstanceChange()
} catch (_) {
}
console.log('network instance change done')
subscribe_running = false
})
}
+6 -6
View File
@@ -1,4 +1,4 @@
import { getCurrent } from '@tauri-apps/api/window'
import { getCurrentWindow } from '@tauri-apps/api/window'
import { Menu, MenuItem, PredefinedMenuItem } from '@tauri-apps/api/menu'
import { TrayIcon } from '@tauri-apps/api/tray'
import pkg from '~/../package.json'
@@ -6,11 +6,11 @@ import pkg from '~/../package.json'
const DEFAULT_TRAY_NAME = 'main'
async function toggleVisibility() {
if (await getCurrent().isVisible()) {
await getCurrent().hide()
if (await getCurrentWindow().isVisible()) {
await getCurrentWindow().hide()
} else {
await getCurrent().show()
await getCurrent().setFocus()
await getCurrentWindow().show()
await getCurrentWindow().setFocus()
}
}
@@ -54,7 +54,7 @@ export async function generateMenuItem() {
await MenuItemExit('Exit'),
await PredefinedMenuItem.new({ item: 'Separator' }),
await MenuItemShow('Show / Hide'),
] || []
]
}
export async function MenuItemExit(text: string) {
+13 -5
View File
@@ -10,6 +10,10 @@ import VueDevTools from 'vite-plugin-vue-devtools'
import VueRouter from 'unplugin-vue-router/vite'
import { VueRouterAutoImports } from 'unplugin-vue-router'
import { PrimeVueResolver } from '@primevue/auto-import-resolver';
import { svelte } from '@sveltejs/vite-plugin-svelte';
import { internalIpV4Sync } from 'internal-ip';
const host = process.env.TAURI_DEV_HOST;
// https://vitejs.dev/config/
export default defineConfig(async () => ({
@@ -19,6 +23,7 @@ export default defineConfig(async () => ({
},
},
plugins: [
svelte(),
VueMacros({
plugins: {
vue: Vue({
@@ -87,15 +92,18 @@ export default defineConfig(async () => ({
// 2. tauri expects a fixed port, fail if that port is not available
server: {
port: 1420,
host: '10.147.223.128',
host: host || false,
strictPort: true,
watch: {
// 3. tell vite to ignore watching `src-tauri`
ignored: ['**/src-tauri/**'],
},
hmr: {
host: "10.147.223.128",
protocol: "ws",
},
hmr: host
? {
protocol: 'ws',
host: internalIpV4Sync(),
port: 1430,
}
: undefined,
},
}))
+27 -16
View File
@@ -3,7 +3,7 @@ name = "easytier"
description = "A full meshed p2p VPN, connecting all your devices in one network with one command."
homepage = "https://github.com/EasyTier/EasyTier"
repository = "https://github.com/EasyTier/EasyTier"
version = "1.2.0"
version = "1.2.2"
edition = "2021"
authors = ["kkrainbow"]
keywords = ["vpn", "p2p", "network", "easytier"]
@@ -43,7 +43,7 @@ time = "0.3"
toml = "0.8.12"
chrono = { version = "0.4.37", features = ["serde"] }
gethostname = "0.4.3"
gethostname = "0.5.0"
futures = { version = "0.3", features = ["bilock", "unstable"] }
@@ -54,7 +54,7 @@ tokio-util = { version = "0.7.9", features = ["codec", "net"] }
async-stream = "0.3.5"
async-trait = "0.1.74"
dashmap = "5.5.3"
dashmap = "6.0"
timedmap = "=1.0.1"
# for full-path zero-copy
@@ -62,7 +62,7 @@ zerocopy = { version = "0.7.32", features = ["derive", "simd"] }
bytes = "1.5.0"
pin-project-lite = "0.2.13"
atomicbox = "0.4.0"
tachyonix = "0.2.1"
tachyonix = "0.3.0"
quinn = { version = "0.11.0", optional = true, features = ["ring"] }
rustls = { version = "0.23.0", features = [
@@ -71,7 +71,7 @@ rustls = { version = "0.23.0", features = [
rcgen = { version = "0.11.1", optional = true }
# for websocket
tokio-websockets = { version = "0.8.2", optional = true, features = [
tokio-websockets = { version = "0.8", optional = true, features = [
"rustls-webpki-roots",
"client",
"server",
@@ -84,7 +84,7 @@ http = { version = "1", default-features = false, features = [
tokio-rustls = { version = "0.26", default-features = false, optional = true }
# for tap device
tun = { package = "tun-easytier", version = "0.7.1", features = [
tun = { package = "tun-easytier", version = "1.1.1", features = [
"async",
], optional = true }
# for net ns
@@ -105,8 +105,8 @@ once_cell = "1.18.0"
postcard = { "version" = "1.0.8", features = ["alloc"] }
# for rpc
tonic = "0.10"
prost = "0.12"
tonic = "0.12"
prost = "0.13"
anyhow = "1.0"
tarpc = { version = "0.32", features = ["tokio1", "serde1"] }
@@ -126,13 +126,18 @@ bytecodec = "0.4.15"
rand = "0.8.5"
serde = { version = "1.0", features = ["derive"] }
pnet = { version = "0.34.0", features = ["serde"] }
pnet = { version = "0.35.0", features = ["serde"] }
clap = { version = "4.4.8", features = ["unicode", "derive", "wrap_help"] }
clap = { version = "4.4.8", features = [
"string",
"unicode",
"derive",
"wrap_help",
] }
async-recursion = "1.0.5"
network-interface = "1.1.1"
network-interface = "2.0"
# for ospf route
petgraph = "0.6.5"
@@ -143,15 +148,16 @@ bitflags = "2.5"
aes-gcm = { version = "0.10.3", optional = true }
# for cli
tabled = "0.15.*"
tabled = "0.16"
humansize = "2.1.3"
base64 = "0.21.7"
base64 = "0.22"
derivative = "2.2.0"
mimalloc-rust = { version = "0.2.1", optional = true }
# for mips
indexmap = { version = "~1.9.3", optional = false, features = ["std"] }
atomic-shim = "0.2.0"
@@ -168,6 +174,9 @@ parking_lot = { version = "0.12.0", optional = true }
wildmatch = "2.3.4"
rust-i18n = "3"
sys-locale = "0.3"
[target.'cfg(windows)'.dependencies]
windows-sys = { version = "0.52", features = [
"Win32_Networking_WinSock",
@@ -176,10 +185,12 @@ windows-sys = { version = "0.52", features = [
"Win32_System_IO",
] }
encoding = "0.2"
winreg = "0.11"
winreg = "0.52"
[build-dependencies]
tonic-build = "0.10"
tonic-build = "0.12"
globwalk = "0.8.1"
regex = "1"
[target.'cfg(windows)'.build-dependencies]
reqwest = { version = "0.11", features = ["blocking"] }
@@ -205,7 +216,7 @@ full = [
"smoltcp",
"tun",
]
mips = ["aes-gcm", "mimalloc", "wireguard"]
mips = ["aes-gcm", "mimalloc", "wireguard", "tun", "smoltcp"]
bsd = ["aes-gcm", "mimalloc", "smoltcp"]
wireguard = ["dep:boringtun", "dep:ring"]
quic = ["dep:quinn", "dep:rustls", "dep:rcgen"]
+40
View File
@@ -86,6 +86,45 @@ impl WindowsBuild {
}
}
fn workdir() -> Option<String> {
if let Ok(cargo_manifest_dir) = std::env::var("CARGO_MANIFEST_DIR") {
return Some(cargo_manifest_dir);
}
let dest = std::env::var("OUT_DIR");
if dest.is_err() {
return None;
}
let dest = dest.unwrap();
let seperator = regex::Regex::new(r"(/target/(.+?)/build/)|(\\target\\(.+?)\\build\\)")
.expect("Invalid regex");
let parts = seperator.split(dest.as_str()).collect::<Vec<_>>();
if parts.len() >= 2 {
return Some(parts[0].to_string());
}
None
}
fn check_locale() {
let workdir = workdir().unwrap_or("./".to_string());
let locale_path = format!("{workdir}/**/locales/**/*");
if let Ok(globs) = globwalk::glob(locale_path) {
for entry in globs {
if let Err(e) = entry {
println!("cargo:i18n-error={}", e);
continue;
}
let entry = entry.unwrap().into_path();
println!("cargo:rerun-if-changed={}", entry.display());
}
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
#[cfg(target_os = "windows")]
WindowsBuild::check_for_win();
@@ -98,5 +137,6 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
.compile(&["proto/cli.proto"], &["proto/"])
.unwrap();
// tonic_build::compile_protos("proto/cli.proto")?;
check_locale();
Ok(())
}
+113
View File
@@ -0,0 +1,113 @@
_version: 2
core_clap:
config_file:
en: "path to the config file, NOTE: if this is set, all other options will be ignored"
zh-CN: "配置文件路径,注意:如果设置了这个选项,其他所有选项都将被忽略"
network_name:
en: "network name to identify this vpn network"
zh-CN: "用于标识此VPN网络的网络名称"
network_secret:
en: "network secret to verify this node belongs to the vpn network"
zh-CN: "网络密钥,用于验证此节点属于VPN网络"
ipv4:
en: "ipv4 address of this vpn node, if empty, this node will only forward packets and no TUN device will be created"
zh-CN: "此VPN节点的IPv4地址,如果为空,则此节点将仅转发数据包,不会创建TUN设备"
dhcp:
en: "automatically determine and set IP address by Easytier, and the IP address starts from 10.0.0.1 by default. Warning, if there is an IP conflict in the network when using DHCP, the IP will be automatically changed."
zh-CN: "由Easytier自动确定并设置IP地址,默认从10.0.0.1开始。警告:在使用DHCP时,如果网络中出现IP冲突,IP将自动更改。"
peers:
en: "peers to connect initially"
zh-CN: "最初要连接的对等节点"
external_node:
en: "use a public shared node to discover peers"
zh-CN: "使用公共共享节点来发现对等节点"
proxy_networks:
en: "export local networks to other peers in the vpn"
zh-CN: "将本地网络导出到VPN中的其他对等节点"
rpc_portal:
en: "rpc portal address to listen for management. 0 means random port, 12345 means listen on 12345 of localhost, 0.0.0.0:12345 means listen on 12345 of all interfaces. default is 0 and will try 15888 first"
zh-CN: "用于管理的RPC门户地址。0表示随机端口,12345表示在localhost的12345上监听,0.0.0.0:12345表示在所有接口的12345上监听。默认是0,首先尝试15888"
listeners:
en: |+
listeners to accept connections, allow format:
port number: <11010>. means tcp/udp will listen on 11010, ws/wss will listen on 11010 and 11011, wg will listen on 11011
url: <tcp://0.0.0.0:11010>. tcp can be tcp, udp, ring, wg, ws, wss\n
proto & port pair: <proto:port>. wg:11011, means listen on 11011 with wireguard protocol url and proto:port can occur multiple times.
zh-CN: |+
监听器用于接受连接,允许以下格式:
端口号:<11010>,意味着tcp/udp将在11010端口监听,ws/wss将在11010和11011端口监听,wg将在11011端口监听。
url<tcp://0.0.0.0:11010>,其中tcp可以是tcp、udp、ring、wg、ws、wss协议。
协议和端口对:<proto:port>,例如wg:11011,表示使用WireGuard协议在11011端口监听。URL 和 协议端口对 可以多次出现。
no_listener:
en: "do not listen on any port, only connect to peers"
zh-CN: "不监听任何端口,只连接到对等节点"
console_log_level:
en: "console log level"
zh-CN: "控制台日志级别"
file_log_level:
en: "file log level"
zh-CN: "文件日志级别"
file_log_dir:
en: "directory to store log files"
zh-CN: "存储日志文件的目录"
hostname:
en: "host name to identify this device"
zh-CN: "用于标识此设备的主机名"
instance_name:
en: "instance name to identify this vpn node in same machine"
zh-CN: "实例名称,用于在同一台机器上标识此VPN节点"
vpn_portal:
en: "url that defines the vpn portal, allow other vpn clients to connect. example: wg://0.0.0.0:11010/10.14.14.0/24, means the vpn portal is a wireguard server listening on vpn.example.com:11010, and the vpn client is in network of 10.14.14.0/24"
zh-CN: "定义VPN门户的URL,允许其他VPN客户端连接。示例:wg://0.0.0.0:11010/10.14.14.0/24,表示VPN门户是监听在vpn.example.com:11010的wireguard服务器,VPN客户端在10.14.14.0/24网络中"
default_protocol:
en: "default protocol to use when connecting to peers"
zh-CN: "连接到对等节点时使用的默认协议"
disable_encryption:
en: "disable encryption for peers communication, default is false, must be same with peers"
zh-CN: "禁用对等节点通信的加密,默认为false,必须与对等节点相同"
multi_thread:
en: "use multi-thread runtime, default is single-thread"
zh-CN: "使用多线程运行时,默认为单线程"
disable_ipv6:
en: "do not use ipv6"
zh-CN: "不使用IPv6"
dev_name:
en: "optional tun interface name"
zh-CN: "可选的TUN接口名称"
mtu:
en: "mtu of the TUN device, default is 1420 for non-encryption, 1400 for encryption"
zh-CN: "TUN设备的MTU,默认为非加密时为1420,加密时为1400"
latency_first:
en: "latency first mode, will try to relay traffic with lowest latency path, default is using shortest path"
zh-CN: "延迟优先模式,将尝试使用最低延迟路径转发流量,默认使用最短路径"
exit_nodes:
en: "exit nodes to forward all traffic to, a virtual ipv4 address, priority is determined by the order of the list"
zh-CN: "转发所有流量的出口节点,虚拟IPv4地址,优先级由列表顺序决定"
enable_exit_node:
en: "allow this node to be an exit node"
zh-CN: "允许此节点成为出口节点"
no_tun:
en: "do not create TUN device, can use subnet proxy to access node"
zh-CN: "不创建TUN设备,可以使用子网代理访问节点"
use_smoltcp:
en: "enable smoltcp stack for subnet proxy"
zh-CN: "为子网代理启用smoltcp堆栈"
manual_routes:
en: "assign routes cidr manually, will disable subnet proxy and wireguard routes propagated from peers. e.g.: 192.168.0.0/16"
zh-CN: "手动分配路由CIDR,将禁用子网代理和从对等节点传播的wireguard路由。例如:192.168.0.0/16"
relay_network_whitelist:
en: |+
only forward traffic from the whitelist networks, supporting wildcard strings, multiple network names can be separated by spaces.
if this parameter is empty, forwarding is disabled. by default, all networks are allowed.
e.g.: '*' (all networks), 'def*' (networks with the prefix 'def'), 'net1 net2' (only allow net1 and net2)"
zh-CN: |+
仅转发白名单网络的流量,支持通配符字符串。多个网络名称间可以使用英文空格间隔。
如果该参数为空,则禁用转发。默认允许所有网络。
例如:'*'(所有网络),'def*'(以def为前缀的网络),'net1 net2'(只允许net1和net2"
disable_p2p:
en: "disable p2p communication, will only relay packets with peers specified by --peers"
zh-CN: "禁用P2P通信,只通过--peers指定的节点转发数据包"
relay_all_peer_rpc:
en: "relay all peer rpc packets, even if the peer is not in the relay network whitelist. this can help peers not in relay network whitelist to establish p2p connection."
zh-CN: "转发所有对等节点的RPC数据包,即使对等节点不在转发网络白名单中。这可以帮助白名单外网络中的对等节点建立P2P连接。"
+12 -14
View File
@@ -171,6 +171,10 @@ pub struct Flags {
pub use_smoltcp: bool,
#[derivative(Default(value = "\"*\".to_string()"))]
pub foreign_network_whitelist: String,
#[derivative(Default(value = "false"))]
pub disable_p2p: bool,
#[derivative(Default(value = "false"))]
pub relay_all_peer_rpc: bool,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
@@ -258,21 +262,15 @@ impl ConfigLoader for TomlConfigLoader {
match hostname {
Some(hostname) => {
let hostname = hostname
.chars()
.filter(|c| !c.is_control())
.take(32)
.collect::<String>();
if !hostname.is_empty() {
let mut name = hostname
.chars()
.filter(|c| c.is_ascii_alphanumeric() || *c == '-' || *c == '_')
.take(32)
.collect::<String>();
if name.len() > 32 {
name = name.chars().take(32).collect::<String>();
}
if hostname != name {
self.set_hostname(Some(name.clone()));
}
name
self.set_hostname(Some(hostname.clone()));
hostname
} else {
self.set_hostname(None);
gethostname::gethostname().to_string_lossy().to_string()
+4
View File
@@ -121,6 +121,10 @@ impl DirectConnectorManager {
}
pub fn run(&mut self) {
if self.global_ctx.get_flags().disable_p2p {
return;
}
self.run_as_server();
self.run_as_client();
}
+1
View File
@@ -316,6 +316,7 @@ impl ManualConnectorManager {
ip_versions.push(IpVersion::Both);
} else {
let addrs = u.socket_addrs(|| Some(1000))?;
tracing::info!(?addrs, ?dead_url, "get ip from url done");
let mut has_ipv4 = false;
let mut has_ipv6 = false;
for addr in addrs {
+4
View File
@@ -602,6 +602,10 @@ impl UdpHolePunchConnector {
}
pub async fn run(&mut self) -> Result<(), Error> {
if self.data.global_ctx.get_flags().disable_p2p {
return Ok(());
}
self.run_as_client().await?;
self.run_as_server().await?;
+102 -59
View File
@@ -10,6 +10,9 @@ use std::{
path::PathBuf,
};
#[macro_use]
extern crate rust_i18n;
use anyhow::Context;
use clap::Parser;
@@ -52,19 +55,20 @@ struct Cli {
#[arg(
short,
long,
help = "path to the config file, NOTE: if this is set, all other options will be ignored"
help = t!("core_clap.config_file").to_string()
)]
config_file: Option<PathBuf>,
#[arg(
long,
help = "network name to identify this vpn network",
help = t!("core_clap.network_name").to_string(),
default_value = "default"
)]
network_name: String,
#[arg(
long,
help = "network secret to verify this node belongs to the vpn network",
help = t!("core_clap.network_secret").to_string(),
default_value = ""
)]
network_secret: String,
@@ -72,171 +76,207 @@ struct Cli {
#[arg(
short,
long,
help = "ipv4 address of this vpn node, if empty, this node will only forward packets and no TUN device will be created"
help = t!("core_clap.ipv4").to_string()
)]
ipv4: Option<String>,
#[arg(
short,
long,
help = "automatically determine and set IP address by Easytier, and the
IP address starts from 10.0.0.1 by default. Warning, if there is an IP
conflict in the network when using DHCP, the IP will be automatically
changed."
help = t!("core_clap.dhcp").to_string()
)]
dhcp: bool,
#[arg(short, long, help = "peers to connect initially", num_args = 0..)]
#[arg(
short,
long,
help = t!("core_clap.peers").to_string(),
num_args = 0..
)]
peers: Vec<String>,
#[arg(short, long, help = "use a public shared node to discover peers")]
#[arg(
short,
long,
help = t!("core_clap.external_node").to_string()
)]
external_node: Option<String>,
#[arg(
short = 'n',
long,
help = "export local networks to other peers in the vpn"
help = t!("core_clap.proxy_networks").to_string()
)]
proxy_networks: Vec<String>,
#[arg(
short,
long,
default_value = "0",
help = "rpc portal address to listen for management. 0 means random
port, 12345 means listen on 12345 of localhost, 0.0.0.0:12345 means
listen on 12345 of all interfaces. default is 0 and will try 15888 first"
help = t!("core_clap.rpc_portal").to_string(),
default_value = "0"
)]
rpc_portal: String,
#[arg(short, long, help = "listeners to accept connections, allow format:
a port number: 11010, means tcp/udp will listen on 11010, ws/wss will listen on 11010 and 11011, wg will listen on 11011
url: tcp://0.0.0.0:11010, tcp can be tcp, udp, ring, wg, ws, wss,
proto:port: wg:11011, means listen on 11011 with wireguard protocol
url and proto:port can occur multiple times.
", default_values_t = ["11010".to_string()],
num_args = 0..)]
#[arg(
short,
long,
help = t!("core_clap.listeners").to_string(),
default_values_t = ["11010".to_string()],
num_args = 0..
)]
listeners: Vec<String>,
#[arg(
long,
help = "do not listen on any port, only connect to peers",
help = t!("core_clap.no_listener").to_string(),
default_value = "false"
)]
no_listener: bool,
#[arg(long, help = "console log level",
value_parser = clap::builder::PossibleValuesParser::new(["trace", "debug", "info", "warn", "error", "off"]))]
#[arg(
long,
help = t!("core_clap.console_log_level").to_string()
)]
console_log_level: Option<String>,
#[arg(long, help = "file log level",
value_parser = clap::builder::PossibleValuesParser::new(["trace", "debug", "info", "warn", "error", "off"]))]
#[arg(
long,
help = t!("core_clap.file_log_level").to_string()
)]
file_log_level: Option<String>,
#[arg(long, help = "directory to store log files")]
#[arg(
long,
help = t!("core_clap.file_log_dir").to_string()
)]
file_log_dir: Option<String>,
#[arg(long, help = "host name to identify this device")]
#[arg(
long,
help = t!("core_clap.hostname").to_string()
)]
hostname: Option<String>,
#[arg(
short = 'm',
long,
default_value = "default",
help = "instance name to identify this vpn node in same machine"
help = t!("core_clap.instance_name").to_string(),
default_value = "default"
)]
instance_name: String,
#[arg(
long,
help = "url that defines the vpn portal, allow other vpn clients to connect.
example: wg://0.0.0.0:11010/10.14.14.0/24, means the vpn portal is a wireguard server listening on vpn.example.com:11010,
and the vpn client is in network of 10.14.14.0/24"
help = t!("core_clap.vpn_portal").to_string()
)]
vpn_portal: Option<String>,
#[arg(long, help = "default protocol to use when connecting to peers")]
#[arg(
long,
help = t!("core_clap.default_protocol").to_string()
)]
default_protocol: Option<String>,
#[arg(
short = 'u',
long,
help = "disable encryption for peers communication, default is false, must be same with peers",
help = t!("core_clap.disable_encryption").to_string(),
default_value = "false"
)]
disable_encryption: bool,
#[arg(
long,
help = "use multi-thread runtime, default is single-thread",
help = t!("core_clap.multi_thread").to_string(),
default_value = "false"
)]
multi_thread: bool,
#[arg(long, help = "do not use ipv6", default_value = "false")]
#[arg(
long,
help = t!("core_clap.disable_ipv6").to_string(),
default_value = "false"
)]
disable_ipv6: bool,
#[arg(long, help = "optional tun interface name")]
#[arg(
long,
help = t!("core_clap.dev_name").to_string()
)]
dev_name: Option<String>,
#[arg(
long,
help = "mtu of the TUN device, default is 1420 for non-encryption, 1400 for encryption"
help = t!("core_clap.mtu").to_string()
)]
mtu: Option<u16>,
#[arg(
long,
help = "latency first mode, will try to relay traffic with lowest latency path, default is using shortest path",
help = t!("core_clap.latency_first").to_string(),
default_value = "false"
)]
latency_first: bool,
#[arg(
long,
help = "exit nodes to forward all traffic to, a virtual ipv4 address, priority is determined by the order of the list",
help = t!("core_clap.exit_nodes").to_string(),
num_args = 0..
)]
exit_nodes: Vec<Ipv4Addr>,
#[arg(
long,
help = "allow this node to be an exit node, default is false",
help = t!("core_clap.enable_exit_node").to_string(),
default_value = "false"
)]
enable_exit_node: bool,
#[arg(
long,
help = "do not create TUN device, can use subnet proxy to access node",
help = t!("core_clap.no_tun").to_string(),
default_value = "false"
)]
no_tun: bool,
#[arg(
long,
help = "enable smoltcp stack for subnet proxy",
help = t!("core_clap.use_smoltcp").to_string(),
default_value = "false"
)]
use_smoltcp: bool,
#[arg(
long,
help = "assign routes cidr manually, will disable subnet proxy and
wireguard routes propogated from peers. e.g.: 192.168.0.0/16",
help = t!("core_clap.manual_routes").to_string(),
num_args = 0..
)]
manual_routes: Option<Vec<String>>,
#[arg(
long,
help = "only relay traffic of whitelisted networks, input is a wildcard
string, e.g.: '*' (all networks), 'def*' (network prefixed with def), can specify multiple networks
disable relay if arg is empty. default is allowing all networks",
num_args = 0..,
help = t!("core_clap.relay_network_whitelist").to_string(),
num_args = 0..
)]
relay_network_whitelist: Option<Vec<String>>,
#[arg(
long,
help = t!("core_clap.disable_p2p").to_string(),
default_value = "false"
)]
disable_p2p: bool,
#[arg(
long,
help = t!("core_clap.relay_all_peer_rpc").to_string(),
default_value = "false"
)]
relay_all_peer_rpc: bool,
}
rust_i18n::i18n!("locales");
impl Cli {
fn parse_listeners(&self) -> Vec<String> {
println!("parsing listeners: {:?}", self.listeners);
@@ -334,14 +374,12 @@ impl From<Cli> for TomlConfigLoader {
cfg.set_dhcp(cli.dhcp);
if !cli.dhcp {
if let Some(ipv4) = &cli.ipv4 {
cfg.set_ipv4(Some(
ipv4.parse()
.with_context(|| format!("failed to parse ipv4 address: {}", ipv4))
.unwrap(),
))
}
if let Some(ipv4) = &cli.ipv4 {
cfg.set_ipv4(Some(
ipv4.parse()
.with_context(|| format!("failed to parse ipv4 address: {}", ipv4))
.unwrap(),
))
}
cfg.set_peers(
@@ -470,6 +508,8 @@ impl From<Cli> for TomlConfigLoader {
if let Some(wl) = cli.relay_network_whitelist {
f.foreign_network_whitelist = wl.join(" ");
}
f.disable_p2p = cli.disable_p2p;
f.relay_all_peer_rpc = cli.relay_all_peer_rpc;
cfg.set_flags(f);
cfg.set_exit_nodes(cli.exit_nodes.clone());
@@ -628,6 +668,9 @@ pub async fn async_main(cli: Cli) {
fn main() {
setup_panic_handler();
let locale = sys_locale::get_locale().unwrap_or_else(|| String::from("en-US"));
rust_i18n::set_locale(&locale);
let cli = Cli::parse();
tracing::info!(cli = ?cli, "cli args parsed");
+23 -8
View File
@@ -1,3 +1,4 @@
use std::any::Any;
use std::collections::HashSet;
use std::net::Ipv4Addr;
use std::sync::atomic::{AtomicBool, Ordering};
@@ -91,7 +92,7 @@ impl NicCtx {
}
}
type ArcNicCtx = Arc<Mutex<Option<NicCtx>>>;
type ArcNicCtx = Arc<Mutex<Option<Box<dyn Any + 'static + Send>>>>;
pub struct Instance {
inst_name: String,
@@ -197,14 +198,28 @@ impl Instance {
Ok(())
}
async fn clear_nic_ctx(arc_nic_ctx: ArcNicCtx) {
// use a mock nic ctx to consume packets.
async fn clear_nic_ctx(
arc_nic_ctx: ArcNicCtx,
packet_recv: Arc<Mutex<PacketRecvChanReceiver>>,
) {
let _ = arc_nic_ctx.lock().await.take();
let mut tasks = JoinSet::new();
tasks.spawn(async move {
let mut packet_recv = packet_recv.lock().await;
while let Some(packet) = packet_recv.recv().await {
tracing::trace!("packet consumed by mock nic ctx: {:?}", packet);
}
});
arc_nic_ctx.lock().await.replace(Box::new(tasks));
tracing::debug!("nic ctx cleared.");
}
async fn use_new_nic_ctx(arc_nic_ctx: ArcNicCtx, nic_ctx: NicCtx) {
let mut g = arc_nic_ctx.lock().await;
*g = Some(nic_ctx);
*g = Some(Box::new(nic_ctx));
tracing::debug!("nic ctx updated.");
}
@@ -274,7 +289,7 @@ impl Instance {
"dhcp start changing ip"
);
Self::clear_nic_ctx(nic_ctx.clone()).await;
Self::clear_nic_ctx(nic_ctx.clone(), _peer_packet_receiver.clone()).await;
if let Some(ip) = candidate_ipv4_addr {
if global_ctx_c.no_tun() {
@@ -329,9 +344,9 @@ impl Instance {
self.listener_manager.lock().await.run().await?;
self.peer_manager.run().await?;
if self.global_ctx.config.get_flags().no_tun {
self.peer_packet_receiver.lock().await.close();
} else {
Self::clear_nic_ctx(self.nic_ctx.clone(), self.peer_packet_receiver.clone()).await;
if !self.global_ctx.config.get_flags().no_tun {
#[cfg(not(target_os = "android"))]
if let Some(ipv4_addr) = self.global_ctx.get_ipv4() {
let mut new_nic_ctx = NicCtx::new(
@@ -532,7 +547,7 @@ impl Instance {
fd: i32,
) -> Result<(), anyhow::Error> {
println!("setup_nic_ctx_for_android, fd: {}", fd);
Self::clear_nic_ctx(nic_ctx.clone()).await;
Self::clear_nic_ctx(nic_ctx.clone(), peer_packet_receiver.clone()).await;
if fd <= 0 {
return Ok(());
}
+34 -54
View File
@@ -31,7 +31,7 @@ use tokio::{
task::JoinSet,
};
use tokio_util::bytes::Bytes;
use tun::{create_as_async, AsyncDevice, Configuration, Device as _, Layer};
use tun::{AbstractDevice, AsyncDevice, Configuration, Layer};
use zerocopy::{NativeEndian, NetworkEndian};
pin_project! {
@@ -237,20 +237,15 @@ impl AsyncWrite for TunAsyncWrite {
}
pub struct VirtualNic {
dev_name: String,
queue_num: usize,
global_ctx: ArcGlobalCtx,
ifname: Option<String>,
ifcfg: Box<dyn IfConfiguerTrait + Send + Sync + 'static>,
}
#[cfg(target_os = "windows")]
pub fn checkreg() -> io::Result<()> {
use winreg::{enums::HKEY_LOCAL_MACHINE, RegKey,enums::KEY_ALL_ACCESS};
// 打开根键
pub fn checkreg(dev_name:&str) -> io::Result<()> {
use winreg::{enums::HKEY_LOCAL_MACHINE, enums::KEY_ALL_ACCESS, RegKey};
let hklm = RegKey::predef(HKEY_LOCAL_MACHINE);
// 打开指定的子键
let profiles_key = hklm.open_subkey_with_flags(
"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkList\\Profiles",
KEY_ALL_ACCESS,
@@ -259,21 +254,19 @@ pub fn checkreg() -> io::Result<()> {
"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkList\\Signatures\\Unmanaged",
KEY_ALL_ACCESS,
)?;
// 收集要删除的子键名称
// collect subkeys to delete
let mut keys_to_delete = Vec::new();
let mut keys_to_delete_unmanaged = Vec::new();
for subkey_name in profiles_key.enum_keys().filter_map(Result::ok) {
let subkey = profiles_key.open_subkey(&subkey_name)?;
// 尝试读取 ProfileName
// check if ProfileName contains "et"
match subkey.get_value::<String, _>("ProfileName") {
Ok(profile_name) => {
// 检查 ProfileName 是否包含 "et"
if profile_name.contains("et_") {
if profile_name.contains("et_") || (!dev_name.is_empty() && dev_name == profile_name) {
keys_to_delete.push(subkey_name);
}
}
Err(e) => {
// 打印错误信息
tracing::error!(
"Failed to read ProfileName for subkey {}: {}",
subkey_name,
@@ -284,16 +277,14 @@ pub fn checkreg() -> io::Result<()> {
}
for subkey_name in unmanaged_key.enum_keys().filter_map(Result::ok) {
let subkey = unmanaged_key.open_subkey(&subkey_name)?;
// 尝试读取 ProfileName
// check if ProfileName contains "et"
match subkey.get_value::<String, _>("Description") {
Ok(profile_name) => {
// 检查 ProfileName 是否包含 "et"
if profile_name.contains("et_") {
if profile_name.contains("et_") || (!dev_name.is_empty() && dev_name == profile_name) {
keys_to_delete_unmanaged.push(subkey_name);
}
}
Err(e) => {
// 打印错误信息
tracing::error!(
"Failed to read ProfileName for subkey {}: {}",
subkey_name,
@@ -302,7 +293,7 @@ pub fn checkreg() -> io::Result<()> {
}
}
}
//删除收集到的子键
// delete collected subkeys
if !keys_to_delete.is_empty() {
for subkey_name in keys_to_delete {
match profiles_key.delete_subkey_all(&subkey_name) {
@@ -325,25 +316,13 @@ pub fn checkreg() -> io::Result<()> {
impl VirtualNic {
pub fn new(global_ctx: ArcGlobalCtx) -> Self {
Self {
dev_name: "".to_owned(),
queue_num: 1,
global_ctx,
ifname: None,
ifcfg: Box::new(IfConfiger {}),
}
}
pub fn set_dev_name(mut self, dev_name: &str) -> Result<Self, Error> {
self.dev_name = dev_name.to_owned();
Ok(self)
}
pub fn set_queue_num(mut self, queue_num: usize) -> Result<Self, Error> {
self.queue_num = queue_num;
Ok(self)
}
async fn create_tun(&mut self) -> Result<AsyncDevice, Error> {
async fn create_tun(&mut self) -> Result<tun::platform::Device, Error> {
let mut config = Configuration::default();
config.layer(Layer::L3);
@@ -351,22 +330,25 @@ impl VirtualNic {
{
let dev_name = self.global_ctx.get_flags().dev_name;
if !dev_name.is_empty() {
config.name(format!("{}", dev_name));
config.tun_name(format!("{}", dev_name));
}
config.platform(|config| {
// detect protocol by ourselves for cross platform
config.packet_information(false);
});
}
#[cfg(target_os = "macos")]
config.platform_config(|config| {
// disable packet information so we can process the header by ourselves, see tun2 impl for more details
config.packet_information(false);
});
#[cfg(target_os = "windows")]
{
match checkreg(){
let dev_name = self.global_ctx.get_flags().dev_name;
match checkreg(&dev_name) {
Ok(_) => tracing::trace!("delete successful!"),
Err(e) => tracing::error!("An error occurred: {}", e),
}
use rand::distributions::Distribution as _;
use std::net::IpAddr;
let c = crate::arch::windows::interface_count()?;
let mut rng = rand::thread_rng();
let s: String = rand::distributions::Alphanumeric
@@ -375,14 +357,15 @@ impl VirtualNic {
.map(char::from)
.collect::<String>()
.to_lowercase();
if !dev_name.is_empty() {
config.tun_name(format!("{}", dev_name));
} else {
config.tun_name(format!("et_{}_{}", c, s));
}
config.name(format!("et{}_{}_{}", self.dev_name, c, s));
// set a temporary address
config.address(format!("172.0.{}.3", c).parse::<IpAddr>().unwrap());
config.platform(|config| {
config.platform_config(|config| {
config.skip_config(true);
config.guid(None);
config.ring_cap(Some(std::cmp::min(
config.min_ring_cap() * 32,
config.max_ring_cap(),
@@ -390,14 +373,10 @@ impl VirtualNic {
});
}
if self.queue_num != 1 {
todo!("queue_num != 1")
}
config.queues(self.queue_num);
config.up();
let _g = self.global_ctx.net_ns.guard();
Ok(create_as_async(&config)?)
Ok(tun::create(&config)?)
}
#[cfg(target_os = "android")]
@@ -409,12 +388,11 @@ impl VirtualNic {
let mut config = Configuration::default();
config.layer(Layer::L3);
config.raw_fd(tun_fd);
config.platform(|config| {
config.no_close_fd_on_drop(true);
});
config.close_fd_on_drop(false);
config.up();
let dev = create_as_async(&config)?;
let dev = tun::create(&config)?;
let dev = AsyncDevice::new(dev)?;
let (a, b) = BiLock::new(dev);
let ft = TunnelWrapper::new(
TunStream::new(a, false),
@@ -432,9 +410,11 @@ impl VirtualNic {
pub async fn create_dev(&mut self) -> Result<Box<dyn Tunnel>, Error> {
let dev = self.create_tun().await?;
let ifname = dev.get_ref().name()?;
let ifname = dev.tun_name()?;
self.ifcfg.wait_interface_show(ifname.as_str()).await?;
let dev = AsyncDevice::new(dev)?;
let flags = self.global_ctx.config.get_flags();
let mut mtu_in_config = flags.mtu;
if flags.enable_encryption {
+58 -1
View File
@@ -37,6 +37,7 @@ use super::{
struct ForeignNetworkEntry {
network: NetworkIdentity,
peer_map: Arc<PeerMap>,
relay_data: bool,
}
impl ForeignNetworkEntry {
@@ -45,9 +46,14 @@ impl ForeignNetworkEntry {
packet_sender: PacketRecvChan,
global_ctx: ArcGlobalCtx,
my_peer_id: PeerId,
relay_data: bool,
) -> Self {
let peer_map = Arc::new(PeerMap::new(packet_sender, global_ctx, my_peer_id));
Self { network, peer_map }
Self {
network,
peer_map,
relay_data,
}
}
}
@@ -195,9 +201,30 @@ impl ForeignNetworkManager {
}
}
fn check_network_in_whitelist(&self, network_name: &str) -> Result<(), Error> {
if self
.global_ctx
.get_flags()
.foreign_network_whitelist
.split(" ")
.map(wildmatch::WildMatch::new)
.any(|wl| wl.matches(network_name))
{
Ok(())
} else {
Err(anyhow::anyhow!("network {} not in whitelist", network_name).into())
}
}
pub async fn add_peer_conn(&self, peer_conn: PeerConn) -> Result<(), Error> {
tracing::info!(peer_conn = ?peer_conn.get_conn_info(), network = ?peer_conn.get_network_identity(), "add new peer conn in foreign network manager");
let relay_peer_rpc = self.global_ctx.get_flags().relay_all_peer_rpc;
let ret = self.check_network_in_whitelist(&peer_conn.get_network_identity().network_name);
if ret.is_err() && !relay_peer_rpc {
return ret;
}
let entry = self
.data
.network_peer_maps
@@ -208,6 +235,7 @@ impl ForeignNetworkManager {
self.packet_sender.clone(),
self.global_ctx.clone(),
self.my_peer_id,
!ret.is_err(),
))
})
.clone();
@@ -280,6 +308,10 @@ impl ForeignNetworkManager {
}
if let Some(entry) = data.get_network_entry(&from_network) {
if !entry.relay_data && hdr.packet_type == PacketType::Data as u8 {
continue;
}
let ret = entry
.peer_map
.send_msg(packet_bytes, to_peer_id, NextHopPolicy::LeastHop)
@@ -424,6 +456,31 @@ mod tests {
foreign_network_whitelist_helper("net2abc".to_string()).await;
}
#[tokio::test]
async fn only_relay_peer_rpc() {
let pm_center = create_mock_peer_manager_with_mock_stun(crate::rpc::NatType::Unknown).await;
let mut flag = pm_center.get_global_ctx().get_flags();
flag.foreign_network_whitelist = "".to_string();
flag.relay_all_peer_rpc = true;
pm_center.get_global_ctx().config.set_flags(flag);
tracing::debug!("pm_center: {:?}", pm_center.my_peer_id());
let pma_net1 = create_mock_peer_manager_for_foreign_network("net1").await;
let pmb_net1 = create_mock_peer_manager_for_foreign_network("net1").await;
tracing::debug!(
"pma_net1: {:?}, pmb_net1: {:?}",
pma_net1.my_peer_id(),
pmb_net1.my_peer_id()
);
connect_peer_manager(pma_net1.clone(), pm_center.clone()).await;
connect_peer_manager(pmb_net1.clone(), pm_center.clone()).await;
wait_route_appear(pma_net1.clone(), pmb_net1.clone())
.await
.unwrap();
assert_eq!(1, pma_net1.list_routes().await.len());
assert_eq!(1, pmb_net1.list_routes().await.len());
}
#[tokio::test]
#[should_panic]
async fn foreign_network_whitelist_fail() {
-16
View File
@@ -309,21 +309,6 @@ impl PeerManager {
self.add_client_tunnel(t).await
}
fn check_network_in_whitelist(&self, network_name: &str) -> Result<(), Error> {
if self
.global_ctx
.get_flags()
.foreign_network_whitelist
.split(" ")
.map(wildmatch::WildMatch::new)
.any(|wl| wl.matches(network_name))
{
Ok(())
} else {
Err(anyhow::anyhow!("network {} not in whitelist", network_name).into())
}
}
#[tracing::instrument]
pub async fn add_tunnel_as_server(&self, tunnel: Box<dyn Tunnel>) -> Result<(), Error> {
tracing::info!("add tunnel as server start");
@@ -334,7 +319,6 @@ impl PeerManager {
{
self.add_new_peer_conn(peer).await?;
} else {
self.check_network_in_whitelist(&peer.get_network_identity().network_name)?;
self.foreign_network_manager.add_peer_conn(peer).await?;
}
tracing::info!("add tunnel as server done");
+8 -2
View File
@@ -205,7 +205,7 @@ impl FromUrl for SocketAddr {
fn from_url(url: url::Url, ip_version: IpVersion) -> Result<Self, TunnelError> {
let addrs = url.socket_addrs(|| None)?;
tracing::debug!(?addrs, ?ip_version, ?url, "convert url to socket addrs");
let mut addrs = addrs
let addrs = addrs
.into_iter()
.filter(|addr| match ip_version {
IpVersion::V4 => addr.is_ipv4(),
@@ -213,7 +213,13 @@ impl FromUrl for SocketAddr {
IpVersion::Both => true,
})
.collect::<Vec<_>>();
addrs.pop().ok_or(TunnelError::NoDnsRecordFound(ip_version))
use rand::seq::SliceRandom;
// randomly select one address
addrs
.choose(&mut rand::thread_rng())
.copied()
.ok_or(TunnelError::NoDnsRecordFound(ip_version))
}
}
+318
View File
@@ -0,0 +1,318 @@
#!/bin/bash
# This script copy from alist , Thank for it!
# INSTALL_PATH='/opt/easytier'
VERSION='latest'
SKIP_FOLDER_VERIFY=false
SKIP_FOLDER_FIX=false
COMMEND=$1
shift
# Check path
if [[ "$#" -ge 1 && ! "$1" == --* ]]; then
INSTALL_PATH=$1
shift
fi
# Check other option
while [[ "$#" -gt 0 ]]; do
case $1 in
--skip-folder-verify) SKIP_FOLDER_VERIFY=true ;;
--skip-folder-fix) SKIP_FOLDER_FIX=true ;;
*) echo "Unknown option: $1"; exit 1 ;;
esac
shift
done
if [ -z "$INSTALL_PATH" ]; then
INSTALL_PATH='/opt/easytier'
fi
if [[ "$INSTALL_PATH" == */ ]]; then
INSTALL_PATH=${INSTALL_PATH%?}
fi
if ! $SKIP_FOLDER_FIX && ! [[ "$INSTALL_PATH" == */easytier ]]; then
INSTALL_PATH="$INSTALL_PATH/easytier"
fi
echo INSTALL PATH : $INSTALL_PATH
echo SKIP FOLDER FIX : $SKIP_FOLDER_FIX
echo SKIP FOLDER VERIFY : $SKIP_FOLDER_VERIFY
RED_COLOR='\e[1;31m'
GREEN_COLOR='\e[1;32m'
YELLOW_COLOR='\e[1;33m'
BLUE_COLOR='\e[1;34m'
PINK_COLOR='\e[1;35m'
SHAN='\e[1;33;5m'
RES='\e[0m'
# clear
echo -e "\r\n${RED_COLOR}----------------------NOTICE----------------------${RES}\r\n"
echo " This is a temporary script to install EasyTier "
echo " EasyTier requires a dedicated empty folder to install"
echo " EasyTier is a developing product and may have some issues "
echo " Using EasyTier requires some basic skills "
echo " You need to face the risks brought by using EasyTier at your own risk "
echo -e "\r\n${RED_COLOR}-------------------------------------------------${RES}\r\n"
read -p "Enter \"yes\" to accept our policy and continue: " -r agreement
if [[ ! "$agreement" =~ ^[Yy]es$ ]]
then
echo "You do not accept your policy, the script will exit ..."
exit 1
fi
# Get platform
if command -v arch >/dev/null 2>&1; then
platform=$(arch)
else
platform=$(uname -m)
fi
case "$platform" in
amd64 | x86_64)
ARCH="x86_64"
;;
arm64 | aarch64 | *armv8*)
ARCH="aarch64"
;;
*armv7*)
ARCH="armv7"
;;
*arm*)
ARCH="arm"
;;
mips)
ARCH="mips"
;;
mipsel)
ARCH="mipsel"
;;
*)
ARCH="UNKNOWN"
;;
esac
# support hf
if [[ "$ARCH" == "armv7" || "$ARCH" == "arm" ]]; then
if cat /proc/cpuinfo | grep Features | grep -i 'half' >/dev/null 2>&1; then
ARCH=${ARCH}hf
fi
fi
echo -e "\r\n${GREEN_COLOR}Your platform: ${ARCH} (${platform}) ${RES}\r\n" 1>&2
GH_PROXY='https://mirror.ghproxy.com/'
if [ "$(id -u)" != "0" ]; then
echo -e "\r\n${RED_COLOR}This script requires run as Root !${RES}\r\n" 1>&2
exit 1
elif [ "$ARCH" == "UNKNOWN" ]; then
echo -e "\r\n${RED_COLOR}Opus${RES}, this script do not support your platfrom\r\nTry ${GREEN_COLOR}install by band${RES}\r\n"
exit 1
elif ! command -v systemctl >/dev/null 2>&1; then
echo -e "\r\n${RED_COLOR}Opus${RES}, your Linux do not support systemctl\r\nnTry ${GREEN_COLOR}install by band${RES}\r\n"
exit 1
else
if command -v netstat >/dev/null 2>&1; then
check_port=$(netstat -lnp | grep 11010 | awk '{print $7}' | awk -F/ '{print $1}')
else
echo -e "${GREEN_COLOR}Check port ...${RES}"
if command -v yum >/dev/null 2>&1; then
yum install net-tools -y >/dev/null 2>&1
check_port=$(netstat -lnp | grep 11010 | awk '{print $7}' | awk -F/ '{print $1}')
else
apt-get update >/dev/null 2>&1
apt-get install net-tools -y >/dev/null 2>&1
check_port=$(netstat -lnp | grep 11010 | awk '{print $7}' | awk -F/ '{print $1}')
fi
fi
fi
CHECK() {
if ! $SKIP_FOLDER_VERIFY; then
if [ -f "$INSTALL_PATH/easytier-core" ]; then
echo "There is EasyTier in $INSTALL_PATH. Please choose other path or use \"update\""
echo -e "Or use Try ${GREEN_COLOR}--skip-folder-verify${RES} to skip"
exit 0
fi
fi
if [ $check_port ]; then
kill -9 $check_port
fi
if [ ! -d "$INSTALL_PATH/" ]; then
mkdir -p $INSTALL_PATH
else
# Check weather path is empty
if ! $SKIP_FOLDER_VERIFY; then
if [ -n "$(ls -A $INSTALL_PATH)" ]; then
echo "EasyTier requires to be installed in an empty directory. Please choose a empty path"
echo -e "Or use Try ${GREEN_COLOR}--skip-folder-verify${RES} to skip"
echo -e "Current path: $INSTALL_PATH ( use ${GREEN_COLOR}--skip-folder-fix${RES} to disable folder fix )"
exit 1
fi
fi
fi
}
INSTALL() {
# Get version number
RESPONSE=$(curl -s "https://api.github.com/repos/EasyTier/EasyTier/releases/latest")
LATEST_VERSION=$(echo "$RESPONSE" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
LATEST_VERSION=$(echo -e "$LATEST_VERSION" | tr -d '[:space:]')
if [ -z "$LATEST_VERSION" ]; then
echo -e "\r\n${RED_COLOR}Opus${RES}, failure to get latest version. Check your internel\r\nOr try ${GREEN_COLOR}install by band${RES}\r\n"
exit 1
fi
# Download
echo -e "\r\n${GREEN_COLOR}Downloading EasyTier $LATEST_VERSION ...${RES}"
rm -rf /tmp/easytier_tmp_install.zip
curl -L ${GH_PROXY}https://github.com/EasyTier/EasyTier/releases/latest/download/easytier-linux-${ARCH}-${LATEST_VERSION}.zip -o /tmp/easytier_tmp_install.zip $CURL_BAR
# Unzip resource
echo -e "\r\n${GREEN_COLOR}Unzip resource ...${RES}"
unzip -o /tmp/easytier_tmp_install.zip -d $INSTALL_PATH/
mv $INSTALL_PATH/easytier-linux-${ARCH}/* $INSTALL_PATH/
rm -rf $INSTALL_PATH/easytier-linux-${ARCH}/
if [ -f $INSTALL_PATH/easytier-core ] || [ -f $INSTALL_PATH/easytier-cli ]; then
echo -e "${GREEN_COLOR} Download successfully! ${RES}"
else
echo -e "${RED_COLOR} Download failed! ${RES}"
exit 1
fi
}
INIT() {
if [ ! -f "$INSTALL_PATH/easytier-core" ]; then
echo -e "\r\n${RED_COLOR}Opus${RES}, unable to find EasyTier\r\n"
exit 1
fi
# Create systemd
cat >/etc/systemd/system/easytier.service <<EOF
[Unit]
Description=EasyTier Service
Wants=network.target
After=network.target network.service
[Service]
Type=simple
WorkingDirectory=$INSTALL_PATH
ExecStart=/bin/bash $INSTALL_PATH/run.sh
[Install]
WantedBy=multi-user.target
EOF
# Create run script
cat >$INSTALL_PATH/run.sh <<EOF
$INSTALL_PATH/easytier-core
EOF
# Startup
systemctl daemon-reload
systemctl enable easytier >/dev/null 2>&1
systemctl start easytier
# For issues from the previous version
rm -rf /usr/bin/easytier-core
rm -rf /usr/bin/easytier-cli
# Add link
ln -s $INSTALL_PATH/easytier-core /usr/sbin/easytier-core
ln -s $INSTALL_PATH/easytier-cli /usr/sbin/easytier-cli
}
SUCCESS() {
clear
echo " Install EasyTier successfully!"
echo -e "\r\nDefault Port: ${GREEN_COLOR}11010(UDP+TCP)${RES}, Notice allowing in firewall!\r\n"
echo -e "Staartup script path: ${GREEN_COLOR}$INSTALL_PATH/run.sh${RES}\n\r\n\rFor more advanced opinions, please modify the startup script"
echo
echo -e "Status: ${GREEN_COLOR}systemctl status easytier${RES}"
echo -e "Start: ${GREEN_COLOR}systemctl start easytier${RES}"
echo -e "Restart: ${GREEN_COLOR}systemctl restart easytier${RES}"
echo -e "Stop: ${GREEN_COLOR}systemctl stop easytier${RES}"
echo
}
UNINSTALL() {
echo -e "\r\n${GREEN_COLOR}Uninstall EasyTier ...${RES}\r\n"
echo -e "${GREEN_COLOR}Stop process ...${RES}"
systemctl disable easytier >/dev/null 2>&1
systemctl stop easytier >/dev/null 2>&1
echo -e "${GREEN_COLOR}Delete files ...${RES}"
rm -rf $INSTALL_PATH /etc/systemd/system/easytier.service /usr/bin/easytier-core /usr/bin/easytier-cli
systemctl daemon-reload
echo -e "\r\n${GREEN_COLOR}EasyTier was removed successfully! ${RES}\r\n"
}
UPDATE() {
if [ ! -f "$INSTALL_PATH/easytier-core" ]; then
echo -e "\r\n${RED_COLOR}Opus${RES}, unable to find EasyTier\r\n"
exit 1
else
echo
echo -e "${GREEN_COLOR}Stopping EasyTier process${RES}\r\n"
systemctl stop easytier
# Backup
rm -rf /tmp/easytier_tmp_update
mkdir -p /tmp/easytier_tmp_update
cp -a $INSTALL_PATH/* /tmp/easytier_tmp_update/
INSTALL
if [ -f $INSTALL_PATH/easytier-core ]; then
echo -e "${GREEN_COLOR} Vrify successfully ${RES}"
else
echo -e "${RED_COLOR} Download failed, unable to update${RES}"
echo "Rollback all ..."
rm -rf $INSTALL_PATH/*
mv /tmp/easytier_tmp_update/* $INSTALL_PATH/
systemctl start easytier
exit 1
fi
echo -e "\r\n${GREEN_COLOR} Starting EasyTier process${RES}"
systemctl start easytier
echo -e "\r\n${GREEN_COLOR} EasyTier was the latest stable version! ${RES}\r\n"
fi
}
# CURL progress
if curl --help | grep progress-bar >/dev/null 2>&1; then # $CURL_BAR
CURL_BAR="--progress-bar"
fi
# The temp directory must exist
if [ ! -d "/tmp" ]; then
mkdir -p /tmp
fi
echo $COMMEND
if [ $COMMEND = "uninstall" ]; then
UNINSTALL
elif [ $COMMEND = "update" ]; then
UPDATE
elif [ $COMMEND = "install" ]; then
CHECK
INSTALL
INIT
if [ -f "$INSTALL_PATH/easytier-core" ]; then
SUCCESS
else
echo -e "${RED_COLOR} Install fail, try install by hand${RES}"
fi
else
echo -e "${RED_COLOR} Error Commend ${RES}\n\r"
echo " ALLOW:"
echo -e "\n\r${GREEN_COLOR} install, uninstall, update ${RES}"
fi
rm -rf /tmp/easytier_tmp_*
+3 -3
View File
@@ -1,7 +1,7 @@
[package]
name = "tauri-plugin-vpnservice"
version = "0.0.0"
authors = [ "You" ]
authors = ["You"]
description = ""
edition = "2021"
rust-version = "1.70"
@@ -9,9 +9,9 @@ exclude = ["/examples", "/webview-dist", "/webview-src", "/node_modules"]
links = "tauri-plugin-vpnservice"
[dependencies]
tauri = { version = "2.0.0-beta.23" }
tauri = { version = "2.0.0-rc" }
serde = "1.0"
thiserror = "1.0"
[build-dependencies]
tauri-plugin = { version = "2.0.0-beta.18", features = ["build"] }
tauri-plugin = { version = "2.0.0-rc", features = ["build"] }
@@ -88,9 +88,9 @@ class TauriVpnService : VpnService() {
builder.addDnsServer(dns)
for (route in routes) {
val ipParts = ipv4Addr.split("/")
val ipParts = route.split("/")
if (ipParts.size != 2) throw IllegalArgumentException("Invalid IP addr string")
builder.addAddress(ipParts[0], ipParts[1].toInt())
builder.addRoute(ipParts[0], ipParts[1].toInt())
}
for (app in disallowedApplications) {
+5 -5
View File
@@ -22,12 +22,12 @@
"pretest": "yarn build"
},
"dependencies": {
"@tauri-apps/api": ">=2.0.0-beta.6"
"@tauri-apps/api": "2.0.0-rc.0"
},
"devDependencies": {
"@rollup/plugin-typescript": "^11.1.6",
"rollup": "^4.9.6",
"typescript": "^5.3.3",
"tslib": "^2.6.2"
"rollup": "^4.20.0",
"tslib": "^2.6.3",
"typescript": "^5.5.4"
}
}
}
+91 -91
View File
@@ -9,21 +9,21 @@ importers:
.:
dependencies:
'@tauri-apps/api':
specifier: '>=2.0.0-beta.6'
version: 2.0.0-beta.14
specifier: 2.0.0-rc.0
version: 2.0.0-rc.0
devDependencies:
'@rollup/plugin-typescript':
specifier: ^11.1.6
version: 11.1.6(rollup@4.18.1)(tslib@2.6.3)(typescript@5.5.3)
version: 11.1.6(rollup@4.20.0)(tslib@2.6.3)(typescript@5.5.4)
rollup:
specifier: ^4.9.6
version: 4.18.1
specifier: ^4.20.0
version: 4.20.0
tslib:
specifier: ^2.6.2
specifier: ^2.6.3
version: 2.6.3
typescript:
specifier: ^5.3.3
version: 5.5.3
specifier: ^5.5.4
version: 5.5.4
packages:
@@ -49,88 +49,88 @@ packages:
rollup:
optional: true
'@rollup/rollup-android-arm-eabi@4.18.1':
resolution: {integrity: sha512-lncuC4aHicncmbORnx+dUaAgzee9cm/PbIqgWz1PpXuwc+sa1Ct83tnqUDy/GFKleLiN7ZIeytM6KJ4cAn1SxA==}
'@rollup/rollup-android-arm-eabi@4.20.0':
resolution: {integrity: sha512-TSpWzflCc4VGAUJZlPpgAJE1+V60MePDQnBd7PPkpuEmOy8i87aL6tinFGKBFKuEDikYpig72QzdT3QPYIi+oA==}
cpu: [arm]
os: [android]
'@rollup/rollup-android-arm64@4.18.1':
resolution: {integrity: sha512-F/tkdw0WSs4ojqz5Ovrw5r9odqzFjb5LIgHdHZG65dFI1lWTWRVy32KDJLKRISHgJvqUeUhdIvy43fX41znyDg==}
'@rollup/rollup-android-arm64@4.20.0':
resolution: {integrity: sha512-u00Ro/nok7oGzVuh/FMYfNoGqxU5CPWz1mxV85S2w9LxHR8OoMQBuSk+3BKVIDYgkpeOET5yXkx90OYFc+ytpQ==}
cpu: [arm64]
os: [android]
'@rollup/rollup-darwin-arm64@4.18.1':
resolution: {integrity: sha512-vk+ma8iC1ebje/ahpxpnrfVQJibTMyHdWpOGZ3JpQ7Mgn/3QNHmPq7YwjZbIE7km73dH5M1e6MRRsnEBW7v5CQ==}
'@rollup/rollup-darwin-arm64@4.20.0':
resolution: {integrity: sha512-uFVfvzvsdGtlSLuL0ZlvPJvl6ZmrH4CBwLGEFPe7hUmf7htGAN+aXo43R/V6LATyxlKVC/m6UsLb7jbG+LG39Q==}
cpu: [arm64]
os: [darwin]
'@rollup/rollup-darwin-x64@4.18.1':
resolution: {integrity: sha512-IgpzXKauRe1Tafcej9STjSSuG0Ghu/xGYH+qG6JwsAUxXrnkvNHcq/NL6nz1+jzvWAnQkuAJ4uIwGB48K9OCGA==}
'@rollup/rollup-darwin-x64@4.20.0':
resolution: {integrity: sha512-xbrMDdlev53vNXexEa6l0LffojxhqDTBeL+VUxuuIXys4x6xyvbKq5XqTXBCEUA8ty8iEJblHvFaWRJTk/icAQ==}
cpu: [x64]
os: [darwin]
'@rollup/rollup-linux-arm-gnueabihf@4.18.1':
resolution: {integrity: sha512-P9bSiAUnSSM7EmyRK+e5wgpqai86QOSv8BwvkGjLwYuOpaeomiZWifEos517CwbG+aZl1T4clSE1YqqH2JRs+g==}
'@rollup/rollup-linux-arm-gnueabihf@4.20.0':
resolution: {integrity: sha512-jMYvxZwGmoHFBTbr12Xc6wOdc2xA5tF5F2q6t7Rcfab68TT0n+r7dgawD4qhPEvasDsVpQi+MgDzj2faOLsZjA==}
cpu: [arm]
os: [linux]
'@rollup/rollup-linux-arm-musleabihf@4.18.1':
resolution: {integrity: sha512-5RnjpACoxtS+aWOI1dURKno11d7krfpGDEn19jI8BuWmSBbUC4ytIADfROM1FZrFhQPSoP+KEa3NlEScznBTyQ==}
'@rollup/rollup-linux-arm-musleabihf@4.20.0':
resolution: {integrity: sha512-1asSTl4HKuIHIB1GcdFHNNZhxAYEdqML/MW4QmPS4G0ivbEcBr1JKlFLKsIRqjSwOBkdItn3/ZDlyvZ/N6KPlw==}
cpu: [arm]
os: [linux]
'@rollup/rollup-linux-arm64-gnu@4.18.1':
resolution: {integrity: sha512-8mwmGD668m8WaGbthrEYZ9CBmPug2QPGWxhJxh/vCgBjro5o96gL04WLlg5BA233OCWLqERy4YUzX3bJGXaJgQ==}
'@rollup/rollup-linux-arm64-gnu@4.20.0':
resolution: {integrity: sha512-COBb8Bkx56KldOYJfMf6wKeYJrtJ9vEgBRAOkfw6Ens0tnmzPqvlpjZiLgkhg6cA3DGzCmLmmd319pmHvKWWlQ==}
cpu: [arm64]
os: [linux]
'@rollup/rollup-linux-arm64-musl@4.18.1':
resolution: {integrity: sha512-dJX9u4r4bqInMGOAQoGYdwDP8lQiisWb9et+T84l2WXk41yEej8v2iGKodmdKimT8cTAYt0jFb+UEBxnPkbXEQ==}
'@rollup/rollup-linux-arm64-musl@4.20.0':
resolution: {integrity: sha512-+it+mBSyMslVQa8wSPvBx53fYuZK/oLTu5RJoXogjk6x7Q7sz1GNRsXWjn6SwyJm8E/oMjNVwPhmNdIjwP135Q==}
cpu: [arm64]
os: [linux]
'@rollup/rollup-linux-powerpc64le-gnu@4.18.1':
resolution: {integrity: sha512-V72cXdTl4EI0x6FNmho4D502sy7ed+LuVW6Ym8aI6DRQ9hQZdp5sj0a2usYOlqvFBNKQnLQGwmYnujo2HvjCxQ==}
'@rollup/rollup-linux-powerpc64le-gnu@4.20.0':
resolution: {integrity: sha512-yAMvqhPfGKsAxHN8I4+jE0CpLWD8cv4z7CK7BMmhjDuz606Q2tFKkWRY8bHR9JQXYcoLfopo5TTqzxgPUjUMfw==}
cpu: [ppc64]
os: [linux]
'@rollup/rollup-linux-riscv64-gnu@4.18.1':
resolution: {integrity: sha512-f+pJih7sxoKmbjghrM2RkWo2WHUW8UbfxIQiWo5yeCaCM0TveMEuAzKJte4QskBp1TIinpnRcxkquY+4WuY/tg==}
'@rollup/rollup-linux-riscv64-gnu@4.20.0':
resolution: {integrity: sha512-qmuxFpfmi/2SUkAw95TtNq/w/I7Gpjurx609OOOV7U4vhvUhBcftcmXwl3rqAek+ADBwSjIC4IVNLiszoj3dPA==}
cpu: [riscv64]
os: [linux]
'@rollup/rollup-linux-s390x-gnu@4.18.1':
resolution: {integrity: sha512-qb1hMMT3Fr/Qz1OKovCuUM11MUNLUuHeBC2DPPAWUYYUAOFWaxInaTwTQmc7Fl5La7DShTEpmYwgdt2hG+4TEg==}
'@rollup/rollup-linux-s390x-gnu@4.20.0':
resolution: {integrity: sha512-I0BtGXddHSHjV1mqTNkgUZLnS3WtsqebAXv11D5BZE/gfw5KoyXSAXVqyJximQXNvNzUo4GKlCK/dIwXlz+jlg==}
cpu: [s390x]
os: [linux]
'@rollup/rollup-linux-x64-gnu@4.18.1':
resolution: {integrity: sha512-7O5u/p6oKUFYjRbZkL2FLbwsyoJAjyeXHCU3O4ndvzg2OFO2GinFPSJFGbiwFDaCFc+k7gs9CF243PwdPQFh5g==}
'@rollup/rollup-linux-x64-gnu@4.20.0':
resolution: {integrity: sha512-y+eoL2I3iphUg9tN9GB6ku1FA8kOfmF4oUEWhztDJ4KXJy1agk/9+pejOuZkNFhRwHAOxMsBPLbXPd6mJiCwew==}
cpu: [x64]
os: [linux]
'@rollup/rollup-linux-x64-musl@4.18.1':
resolution: {integrity: sha512-pDLkYITdYrH/9Cv/Vlj8HppDuLMDUBmgsM0+N+xLtFd18aXgM9Nyqupb/Uw+HeidhfYg2lD6CXvz6CjoVOaKjQ==}
'@rollup/rollup-linux-x64-musl@4.20.0':
resolution: {integrity: sha512-hM3nhW40kBNYUkZb/r9k2FKK+/MnKglX7UYd4ZUy5DJs8/sMsIbqWK2piZtVGE3kcXVNj3B2IrUYROJMMCikNg==}
cpu: [x64]
os: [linux]
'@rollup/rollup-win32-arm64-msvc@4.18.1':
resolution: {integrity: sha512-W2ZNI323O/8pJdBGil1oCauuCzmVd9lDmWBBqxYZcOqWD6aWqJtVBQ1dFrF4dYpZPks6F+xCZHfzG5hYlSHZ6g==}
'@rollup/rollup-win32-arm64-msvc@4.20.0':
resolution: {integrity: sha512-psegMvP+Ik/Bg7QRJbv8w8PAytPA7Uo8fpFjXyCRHWm6Nt42L+JtoqH8eDQ5hRP7/XW2UiIriy1Z46jf0Oa1kA==}
cpu: [arm64]
os: [win32]
'@rollup/rollup-win32-ia32-msvc@4.18.1':
resolution: {integrity: sha512-ELfEX1/+eGZYMaCIbK4jqLxO1gyTSOIlZr6pbC4SRYFaSIDVKOnZNMdoZ+ON0mrFDp4+H5MhwNC1H/AhE3zQLg==}
'@rollup/rollup-win32-ia32-msvc@4.20.0':
resolution: {integrity: sha512-GabekH3w4lgAJpVxkk7hUzUf2hICSQO0a/BLFA11/RMxQT92MabKAqyubzDZmMOC/hcJNlc+rrypzNzYl4Dx7A==}
cpu: [ia32]
os: [win32]
'@rollup/rollup-win32-x64-msvc@4.18.1':
resolution: {integrity: sha512-yjk2MAkQmoaPYCSu35RLJ62+dz358nE83VfTePJRp8CG7aMg25mEJYpXFiD+NcevhX8LxD5OP5tktPXnXN7GDw==}
'@rollup/rollup-win32-x64-msvc@4.20.0':
resolution: {integrity: sha512-aJ1EJSuTdGnM6qbVC4B5DSmozPTqIag9fSzXRNNo+humQLG89XpPgdt16Ia56ORD7s+H8Pmyx44uczDQ0yDzpg==}
cpu: [x64]
os: [win32]
'@tauri-apps/api@2.0.0-beta.14':
resolution: {integrity: sha512-YLYgHqdwWswr4Y70+hRzaLD6kLIUgHhE3shLXNquPiTaQ9+cX3Q2dB0AFfqsua6NXYFNe7LfkmMzaqEzqv3yQg==}
'@tauri-apps/api@2.0.0-rc.0':
resolution: {integrity: sha512-v454Qs3REHc3Za59U+/eSmBsdmF+3NE5+76+lFDaitVqN4ZglDHENDaMARYKGJVZuxiSkzyqG0SeG7lLQjVkPA==}
engines: {node: '>= 18.18', npm: '>= 6.6.0', yarn: '>= 1.19.1'}
'@types/estree@1.0.5':
@@ -151,8 +151,8 @@ packages:
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
engines: {node: '>= 0.4'}
is-core-module@2.14.0:
resolution: {integrity: sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A==}
is-core-module@2.15.0:
resolution: {integrity: sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==}
engines: {node: '>= 0.4'}
path-parse@1.0.7:
@@ -166,8 +166,8 @@ packages:
resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==}
hasBin: true
rollup@4.18.1:
resolution: {integrity: sha512-Elx2UT8lzxxOXMpy5HWQGZqkrQOtrVDDa/bm9l10+U4rQnVzbL/LgZ4NOM1MPIDyHk69W4InuYDF5dzRh4Kw1A==}
rollup@4.20.0:
resolution: {integrity: sha512-6rbWBChcnSGzIlXeIdNIZTopKYad8ZG8ajhl78lGRLsI2rX8IkaotQhVas2Ma+GPxJav19wrSzvRvuiv0YKzWw==}
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
hasBin: true
@@ -178,79 +178,79 @@ packages:
tslib@2.6.3:
resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==}
typescript@5.5.3:
resolution: {integrity: sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==}
typescript@5.5.4:
resolution: {integrity: sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==}
engines: {node: '>=14.17'}
hasBin: true
snapshots:
'@rollup/plugin-typescript@11.1.6(rollup@4.18.1)(tslib@2.6.3)(typescript@5.5.3)':
'@rollup/plugin-typescript@11.1.6(rollup@4.20.0)(tslib@2.6.3)(typescript@5.5.4)':
dependencies:
'@rollup/pluginutils': 5.1.0(rollup@4.18.1)
'@rollup/pluginutils': 5.1.0(rollup@4.20.0)
resolve: 1.22.8
typescript: 5.5.3
typescript: 5.5.4
optionalDependencies:
rollup: 4.18.1
rollup: 4.20.0
tslib: 2.6.3
'@rollup/pluginutils@5.1.0(rollup@4.18.1)':
'@rollup/pluginutils@5.1.0(rollup@4.20.0)':
dependencies:
'@types/estree': 1.0.5
estree-walker: 2.0.2
picomatch: 2.3.1
optionalDependencies:
rollup: 4.18.1
rollup: 4.20.0
'@rollup/rollup-android-arm-eabi@4.18.1':
'@rollup/rollup-android-arm-eabi@4.20.0':
optional: true
'@rollup/rollup-android-arm64@4.18.1':
'@rollup/rollup-android-arm64@4.20.0':
optional: true
'@rollup/rollup-darwin-arm64@4.18.1':
'@rollup/rollup-darwin-arm64@4.20.0':
optional: true
'@rollup/rollup-darwin-x64@4.18.1':
'@rollup/rollup-darwin-x64@4.20.0':
optional: true
'@rollup/rollup-linux-arm-gnueabihf@4.18.1':
'@rollup/rollup-linux-arm-gnueabihf@4.20.0':
optional: true
'@rollup/rollup-linux-arm-musleabihf@4.18.1':
'@rollup/rollup-linux-arm-musleabihf@4.20.0':
optional: true
'@rollup/rollup-linux-arm64-gnu@4.18.1':
'@rollup/rollup-linux-arm64-gnu@4.20.0':
optional: true
'@rollup/rollup-linux-arm64-musl@4.18.1':
'@rollup/rollup-linux-arm64-musl@4.20.0':
optional: true
'@rollup/rollup-linux-powerpc64le-gnu@4.18.1':
'@rollup/rollup-linux-powerpc64le-gnu@4.20.0':
optional: true
'@rollup/rollup-linux-riscv64-gnu@4.18.1':
'@rollup/rollup-linux-riscv64-gnu@4.20.0':
optional: true
'@rollup/rollup-linux-s390x-gnu@4.18.1':
'@rollup/rollup-linux-s390x-gnu@4.20.0':
optional: true
'@rollup/rollup-linux-x64-gnu@4.18.1':
'@rollup/rollup-linux-x64-gnu@4.20.0':
optional: true
'@rollup/rollup-linux-x64-musl@4.18.1':
'@rollup/rollup-linux-x64-musl@4.20.0':
optional: true
'@rollup/rollup-win32-arm64-msvc@4.18.1':
'@rollup/rollup-win32-arm64-msvc@4.20.0':
optional: true
'@rollup/rollup-win32-ia32-msvc@4.18.1':
'@rollup/rollup-win32-ia32-msvc@4.20.0':
optional: true
'@rollup/rollup-win32-x64-msvc@4.18.1':
'@rollup/rollup-win32-x64-msvc@4.20.0':
optional: true
'@tauri-apps/api@2.0.0-beta.14': {}
'@tauri-apps/api@2.0.0-rc.0': {}
'@types/estree@1.0.5': {}
@@ -265,7 +265,7 @@ snapshots:
dependencies:
function-bind: 1.1.2
is-core-module@2.14.0:
is-core-module@2.15.0:
dependencies:
hasown: 2.0.2
@@ -275,34 +275,34 @@ snapshots:
resolve@1.22.8:
dependencies:
is-core-module: 2.14.0
is-core-module: 2.15.0
path-parse: 1.0.7
supports-preserve-symlinks-flag: 1.0.0
rollup@4.18.1:
rollup@4.20.0:
dependencies:
'@types/estree': 1.0.5
optionalDependencies:
'@rollup/rollup-android-arm-eabi': 4.18.1
'@rollup/rollup-android-arm64': 4.18.1
'@rollup/rollup-darwin-arm64': 4.18.1
'@rollup/rollup-darwin-x64': 4.18.1
'@rollup/rollup-linux-arm-gnueabihf': 4.18.1
'@rollup/rollup-linux-arm-musleabihf': 4.18.1
'@rollup/rollup-linux-arm64-gnu': 4.18.1
'@rollup/rollup-linux-arm64-musl': 4.18.1
'@rollup/rollup-linux-powerpc64le-gnu': 4.18.1
'@rollup/rollup-linux-riscv64-gnu': 4.18.1
'@rollup/rollup-linux-s390x-gnu': 4.18.1
'@rollup/rollup-linux-x64-gnu': 4.18.1
'@rollup/rollup-linux-x64-musl': 4.18.1
'@rollup/rollup-win32-arm64-msvc': 4.18.1
'@rollup/rollup-win32-ia32-msvc': 4.18.1
'@rollup/rollup-win32-x64-msvc': 4.18.1
'@rollup/rollup-android-arm-eabi': 4.20.0
'@rollup/rollup-android-arm64': 4.20.0
'@rollup/rollup-darwin-arm64': 4.20.0
'@rollup/rollup-darwin-x64': 4.20.0
'@rollup/rollup-linux-arm-gnueabihf': 4.20.0
'@rollup/rollup-linux-arm-musleabihf': 4.20.0
'@rollup/rollup-linux-arm64-gnu': 4.20.0
'@rollup/rollup-linux-arm64-musl': 4.20.0
'@rollup/rollup-linux-powerpc64le-gnu': 4.20.0
'@rollup/rollup-linux-riscv64-gnu': 4.20.0
'@rollup/rollup-linux-s390x-gnu': 4.20.0
'@rollup/rollup-linux-x64-gnu': 4.20.0
'@rollup/rollup-linux-x64-musl': 4.20.0
'@rollup/rollup-win32-arm64-msvc': 4.20.0
'@rollup/rollup-win32-ia32-msvc': 4.20.0
'@rollup/rollup-win32-x64-msvc': 4.20.0
fsevents: 2.3.3
supports-preserve-symlinks-flag@1.0.0: {}
tslib@2.6.3: {}
typescript@5.5.3: {}
typescript@5.5.4: {}