Compare commits
302 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| dde7a4dff1 | |||
| 40601bd05b | |||
| 72d5ed908e | |||
| 72673a9d52 | |||
| 327ccdcf38 | |||
| 8c2f96d1aa | |||
| 34ba0bc95b | |||
| ed162c2e66 | |||
| 40b5fe9a54 | |||
| 5a98fac395 | |||
| 0bab14cd72 | |||
| b407cfd9d4 | |||
| 25dcdc652a | |||
| 950cb04534 | |||
| c07d1286ef | |||
| 8ddd153022 | |||
| 870353c499 | |||
| ecebbecd3b | |||
| f39fbb2ce2 | |||
| ec56c0bc45 | |||
| 20a6025075 | |||
| 707963c0d9 | |||
| 3c7837692e | |||
| f890812577 | |||
| 47f3efe71b | |||
| 6d88b10b14 | |||
| d34a51739f | |||
| a6773aa549 | |||
| 0314c66635 | |||
| 3fb172b4d2 | |||
| 96fc19b803 | |||
| 9f7ba8ab8f | |||
| e592e9f29a | |||
| 4608bca998 | |||
| b5dfc7374c | |||
| b469f8197a | |||
| 0a38a8ef4a | |||
| e75be7801f | |||
| 6c49bb1865 | |||
| f9c24bc205 | |||
| d7c3179c6e | |||
| b0fd37949a | |||
| 29994b663a | |||
| fc397c35c5 | |||
| 0f2b214918 | |||
| fec885c427 | |||
| 5a2fd4465c | |||
| 83d1ecc4da | |||
| 7c6daf7c56 | |||
| 28fe6257be | |||
| 99430983bc | |||
| d758a4958f | |||
| 95b12dda5a | |||
| 2675cf2d00 | |||
| 72be46e8fa | |||
| c5580feb64 | |||
| 7e3819be86 | |||
| f0302f2be7 | |||
| b5f60f843d | |||
| 6bdfb8b01f | |||
| ef1d81a2a1 | |||
| 739b4ee106 | |||
| 6a038e8a88 | |||
| 72ea8a9f76 | |||
| 44d93648ee | |||
| 75f7865769 | |||
| 01e3ad99ca | |||
| 3c0d85c9db | |||
| b38991a14e | |||
| 465269566b | |||
| f103fc13d9 | |||
| e5917fad4e | |||
| de8c89eb03 | |||
| c142db301a | |||
| 8dc8c7d9e2 | |||
| 2b909e04ea | |||
| e130c3f2e4 | |||
| 3ad754879f | |||
| fd2b3768e1 | |||
| 67cff12c76 | |||
| c5ea7848b3 | |||
| 34365a096e | |||
| d880dfbbca | |||
| b46a200f8d | |||
| 81490d0662 | |||
| 3d1e841cc5 | |||
| f52936a103 | |||
| 23f69ce6a4 | |||
| f84ae228fc | |||
| 74c716ccaa | |||
| 445b02b2ca | |||
| bb17ffa9fc | |||
| 389ea709ce | |||
| c2f535ead4 | |||
| 0318f55322 | |||
| 1f4340e82f | |||
| ed08707c98 | |||
| 7397abcb94 | |||
| 98d321f8ac | |||
| e78b0ef869 | |||
| 8d654330ac | |||
| 00d61333d3 | |||
| 03b55b61e7 | |||
| 745e44cc87 | |||
| 24213a874a | |||
| 155f8a2ba2 | |||
| 568dca6f9c | |||
| 673c34cf5a | |||
| 2050ed78d0 | |||
| 2632c44195 | |||
| 5449eabf2a | |||
| dd5b00faf4 | |||
| 0caec3e4da | |||
| e48e62cac0 | |||
| 06ebda2e2f | |||
| 53c449b9fb | |||
| 51e0fac72c | |||
| 32b1fe0893 | |||
| 2af3b82e32 | |||
| eca1231831 | |||
| e833c2a28b | |||
| 8b89a037e8 | |||
| 1e821a03fe | |||
| 66051967fe | |||
| a63778854f | |||
| 4aea0821dd | |||
| 08546925cc | |||
| d0f26d9303 | |||
| 2a5d5ea4df | |||
| b69b122c8d | |||
| 55a39491cb | |||
| 1194ee1c2d | |||
| c23b544c34 | |||
| 9d76b86f49 | |||
| bb0ccca3e5 | |||
| 306817ae9a | |||
| d2ec60e108 | |||
| e016aeddeb | |||
| a4419a31fd | |||
| 34e4e907a9 | |||
| 2f4a097787 | |||
| f3de00be37 | |||
| 4cf61f0d4a | |||
| 4e5915f98e | |||
| 870eca9e9f | |||
| 25ed41caf5 | |||
| 4bb72b5606 | |||
| c4d8ea4fec | |||
| 8588c9201a | |||
| dd2236c697 | |||
| bc7c4d8cd0 | |||
| aed54f7318 | |||
| 86600c6315 | |||
| 3f47f37470 | |||
| 1324e6163e | |||
| 89093167c6 | |||
| 15ad92aef2 | |||
| 6cdea38284 | |||
| 9d455e22fa | |||
| 4fc3ff8ce8 | |||
| 88e6de9d7e | |||
| e948dbfcc1 | |||
| 8aca5851f2 | |||
| 18da94bf33 | |||
| 1ac2e1c8e3 | |||
| a78b759741 | |||
| b5c3726e67 | |||
| efee3707da | |||
| bbd3453f36 | |||
| 0bf42c53cc | |||
| 2134bc9139 | |||
| 4df8d7e976 | |||
| 70708b34cc | |||
| 949003ee1b | |||
| db9df1df94 | |||
| 4dca25db86 | |||
| d87a440c04 | |||
| 55efd62798 | |||
| 70a41275c1 | |||
| dd941681ce | |||
| 9824d0adaa | |||
| d2291628e0 | |||
| 7ab8cad1af | |||
| 2c017e0fc5 | |||
| d9453589ac | |||
| e344372616 | |||
| 63821e56bc | |||
| 1be64223c8 | |||
| a08a8e7f4c | |||
| b31996230d | |||
| 1e836501a8 | |||
| d4e59ffc40 | |||
| 37ceb77bf6 | |||
| ba3da97ad4 | |||
| 984ed8f6cf | |||
| c7895963e4 | |||
| a0ece6ad4d | |||
| d0a3a40a0f | |||
| ff5ee8a05e | |||
| a50bcf3087 | |||
| e0b364d3e2 | |||
| 2496cf51c3 | |||
| 7b4a01e7fb | |||
| 3f9a1d8f2e | |||
| 0b927bcc91 | |||
| 92397bf7b6 | |||
| d1e2e1db2b | |||
| 783ba50c9e | |||
| aca9a0e35b | |||
| fb8d262554 | |||
| bd60cfc2a0 | |||
| 06afd221d5 | |||
| 0171fb35a4 | |||
| 99c47813c3 | |||
| 82f5dfd569 | |||
| 6d7edcd486 | |||
| 9f273dc887 | |||
| ac9cfa5040 | |||
| 1b03223537 | |||
| 0467b0a3dc | |||
| ba75167238 | |||
| 51e7daa26f | |||
| 2ff653cc6f | |||
| cfe4d080d5 | |||
| 9b28ecde8e | |||
| 096ed39d23 | |||
| 6ea3adcef8 | |||
| 4342be29d7 | |||
| 1609c97574 | |||
| f07b3ee9c6 | |||
| 2058dbc470 | |||
| 6964fb71fc | |||
| a8bb4ee7e5 | |||
| 3fcd74ce4e | |||
| 2b7ff0efc5 | |||
| 5833541a6e | |||
| 54c6418f97 | |||
| fc9aac42b4 | |||
| 89b43684d8 | |||
| 31b26222d3 | |||
| e4df03053e | |||
| 833e7eca22 | |||
| b7d85ad2ff | |||
| 8793560e12 | |||
| 58e0e48d59 | |||
| ad4cbbea6d | |||
| db660ee3b1 | |||
| ae54a872ce | |||
| 2aa686f7ad | |||
| ce10bf5e60 | |||
| 28ae9c447a | |||
| ff6da9bbec | |||
| 198c239399 | |||
| 0fbbea963f | |||
| 51165c54f5 | |||
| f14875aa3f | |||
| 6391dceb62 | |||
| 29806b899a | |||
| d63a3c01e4 | |||
| b6fb7ac962 | |||
| 1d22fdc972 | |||
| d135dd5a6f | |||
| 7cae63cb17 | |||
| 232165eff3 | |||
| 2bc4dd8c53 | |||
| cca105e91d | |||
| 3e52490d1b | |||
| d1293276ce | |||
| 4a5e426730 | |||
| fdc2755291 | |||
| b4fbcd8d80 | |||
| 2415cb211e | |||
| 5e51784803 | |||
| 5f0d71b0fe | |||
| 71d41f0a70 | |||
| 78f2804bad | |||
| 0da09ec605 | |||
| 02b5b5f3c7 | |||
| 4a0adaa3f8 | |||
| d5bc15cf7a | |||
| 4fea3a60d6 | |||
| 7a2bc52ae0 | |||
| debc165326 | |||
| d5eef25ad1 | |||
| 5451b52daa | |||
| fc9812dd54 | |||
| 8858492fb4 | |||
| 5987528f59 | |||
| a03fc04b1b | |||
| 858ade2eee | |||
| 4938e3ed2b | |||
| 537f6ecf78 | |||
| 24143cbf1c | |||
| 513e4cacc9 | |||
| 7cfa850d4c | |||
| 571d4a6e8f | |||
| 0f39bfcefa | |||
| 34f832bbad | |||
| 8aa57ebc22 | |||
| fede35cca4 | |||
| b2100b78d3 | |||
| 70ad81f7bd |
@@ -6,72 +6,84 @@ rustflags = ["-C", "linker-flavor=ld.lld"]
|
|||||||
linker = "aarch64-linux-gnu-gcc"
|
linker = "aarch64-linux-gnu-gcc"
|
||||||
|
|
||||||
[target.aarch64-unknown-linux-musl]
|
[target.aarch64-unknown-linux-musl]
|
||||||
linker = "aarch64-linux-musl-gcc"
|
linker = "aarch64-unknown-linux-musl-gcc"
|
||||||
rustflags = ["-C", "target-feature=+crt-static"]
|
rustflags = ["-C", "target-feature=+crt-static"]
|
||||||
|
|
||||||
[target.'cfg(all(windows, target_env = "msvc"))']
|
[target.'cfg(all(windows, target_env = "msvc"))']
|
||||||
rustflags = ["-C", "target-feature=+crt-static"]
|
rustflags = ["-C", "target-feature=+crt-static"]
|
||||||
|
|
||||||
[target.mipsel-unknown-linux-musl]
|
[target.mipsel-unknown-linux-musl]
|
||||||
linker = "mipsel-linux-muslsf-gcc"
|
linker = "mipsel-unknown-linux-muslsf-gcc"
|
||||||
rustflags = [
|
rustflags = [
|
||||||
"-C",
|
"-C",
|
||||||
"target-feature=+crt-static",
|
"target-feature=+crt-static",
|
||||||
"-L",
|
"-L",
|
||||||
"./musl_gcc/mipsel-linux-muslsf-cross/mipsel-linux-muslsf/lib",
|
"./musl_gcc/mipsel-unknown-linux-muslsf/mipsel-unknown-linux-muslsf/lib",
|
||||||
"-L",
|
"-L",
|
||||||
"./musl_gcc/mipsel-linux-muslsf-cross/lib/gcc/mipsel-linux-muslsf/11.2.1",
|
"./musl_gcc/mipsel-unknown-linux-muslsf/mipsel-unknown-linux-muslsf/sysroot/usr/lib",
|
||||||
|
"-L",
|
||||||
|
"./musl_gcc/mipsel-unknown-linux-muslsf/lib/gcc/mipsel-unknown-linux-muslsf/15.1.0",
|
||||||
"-l",
|
"-l",
|
||||||
"atomic",
|
"atomic",
|
||||||
"-l",
|
"-l",
|
||||||
"ctz",
|
"ctz",
|
||||||
|
"-l",
|
||||||
|
"gcc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[target.mips-unknown-linux-musl]
|
[target.mips-unknown-linux-musl]
|
||||||
linker = "mips-linux-muslsf-gcc"
|
linker = "mips-unknown-linux-muslsf-gcc"
|
||||||
rustflags = [
|
rustflags = [
|
||||||
"-C",
|
"-C",
|
||||||
"target-feature=+crt-static",
|
"target-feature=+crt-static",
|
||||||
"-L",
|
"-L",
|
||||||
"./musl_gcc/mips-linux-muslsf-cross/mips-linux-muslsf/lib",
|
"./musl_gcc/mips-unknown-linux-muslsf/mips-unknown-linux-muslsf/lib",
|
||||||
"-L",
|
"-L",
|
||||||
"./musl_gcc/mips-linux-muslsf-cross/lib/gcc/mips-linux-muslsf/11.2.1",
|
"./musl_gcc/mips-unknown-linux-muslsf/mips-unknown-linux-muslsf/sysroot/usr/lib",
|
||||||
|
"-L",
|
||||||
|
"./musl_gcc/mips-unknown-linux-muslsf/lib/gcc/mips-unknown-linux-muslsf/15.1.0",
|
||||||
"-l",
|
"-l",
|
||||||
"atomic",
|
"atomic",
|
||||||
"-l",
|
"-l",
|
||||||
"ctz",
|
"ctz",
|
||||||
|
"-l",
|
||||||
|
"gcc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[target.armv7-unknown-linux-musleabihf]
|
[target.armv7-unknown-linux-musleabihf]
|
||||||
linker = "armv7l-linux-musleabihf-gcc"
|
linker = "armv7-unknown-linux-musleabihf-gcc"
|
||||||
rustflags = ["-C", "target-feature=+crt-static"]
|
rustflags = ["-C", "target-feature=+crt-static"]
|
||||||
|
|
||||||
[target.armv7-unknown-linux-musleabi]
|
[target.armv7-unknown-linux-musleabi]
|
||||||
linker = "armv7m-linux-musleabi-gcc"
|
linker = "armv7-unknown-linux-musleabi-gcc"
|
||||||
rustflags = ["-C", "target-feature=+crt-static"]
|
rustflags = ["-C", "target-feature=+crt-static"]
|
||||||
|
|
||||||
[target.arm-unknown-linux-musleabihf]
|
[target.arm-unknown-linux-musleabihf]
|
||||||
linker = "arm-linux-musleabihf-gcc"
|
linker = "arm-unknown-linux-musleabihf-gcc"
|
||||||
rustflags = [
|
rustflags = [
|
||||||
"-C",
|
"-C",
|
||||||
"target-feature=+crt-static",
|
"target-feature=+crt-static",
|
||||||
"-L",
|
"-L",
|
||||||
"./musl_gcc/arm-linux-musleabihf-cross/arm-linux-musleabihf/lib",
|
"./musl_gcc/arm-unknown-linux-musleabihf/arm-unknown-linux-musleabihf/lib",
|
||||||
"-L",
|
"-L",
|
||||||
"./musl_gcc/arm-linux-musleabihf-cross/lib/gcc/arm-linux-musleabihf/11.2.1",
|
"./musl_gcc/arm-unknown-linux-musleabihf/lib/gcc/arm-unknown-linux-musleabihf/15.1.0",
|
||||||
"-l",
|
"-l",
|
||||||
"atomic",
|
"atomic",
|
||||||
|
"-l",
|
||||||
|
"gcc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[target.arm-unknown-linux-musleabi]
|
[target.arm-unknown-linux-musleabi]
|
||||||
linker = "arm-linux-musleabi-gcc"
|
linker = "arm-unknown-linux-musleabi-gcc"
|
||||||
rustflags = [
|
rustflags = [
|
||||||
"-C",
|
"-C",
|
||||||
"target-feature=+crt-static",
|
"target-feature=+crt-static",
|
||||||
"-L",
|
"-L",
|
||||||
"./musl_gcc/arm-linux-musleabi-cross/arm-linux-musleabi/lib",
|
"./musl_gcc/arm-unknown-linux-musleabi/arm-unknown-linux-musleabi/lib",
|
||||||
"-L",
|
"-L",
|
||||||
"./musl_gcc/arm-linux-musleabi-cross/lib/gcc/arm-linux-musleabi/11.2.1",
|
"./musl_gcc/arm-unknown-linux-musleabi/lib/gcc/arm-unknown-linux-musleabi/15.1.0",
|
||||||
"-l",
|
"-l",
|
||||||
"atomic",
|
"atomic",
|
||||||
|
"-l",
|
||||||
|
"gcc",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
# Copyright 2024-present Easytier Programme within The Commons Conservancy
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
name: 🐞 问题报告 / Bug Report
|
||||||
|
title: '[bug] '
|
||||||
|
description: 报告一个问题 / Report a bug
|
||||||
|
labels: ['type: bug', 'status: needs triage']
|
||||||
|
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
## 在提交问题之前 / First of all
|
||||||
|
1. 请先搜索有关此问题的 [现有问题](https://github.com/EasyTier/EasyTier/issues?q=is%3Aissue)。
|
||||||
|
1. Please search for [existing issues](https://github.com/EasyTier/EasyTier/issues?q=is%3Aissue) about this problem first.
|
||||||
|
2. 请确保所使用的 Easytier 版本都是最新的。
|
||||||
|
2. Make sure that all Easytier versions are up-to-date.
|
||||||
|
3. 请确保这是 EasyTier 的问题,而不是你正在使用的其他内容引起的问题。
|
||||||
|
3. Make sure it's an issue with EasyTier and not something else you are using.
|
||||||
|
4. 请记得遵守我们的社区准则并保持友好态度。
|
||||||
|
4. Remember to follow our community guidelines and be friendly.
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: description
|
||||||
|
attributes:
|
||||||
|
label: 描述问题 / Describe the bug
|
||||||
|
description: 对 bug 的明确描述。如果条件允许,请包括屏幕截图。 / A clear description of what the bug is. Include screenshots if applicable.
|
||||||
|
placeholder: 问题描述 / Bug description
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: reproduction
|
||||||
|
attributes:
|
||||||
|
label: 重现步骤 / Reproduction
|
||||||
|
description: 能够重现行为的步骤或指向能够复现的存储库链接。 / A link to a reproduction repo or steps to reproduce the behaviour.
|
||||||
|
placeholder: |
|
||||||
|
请提供一个最小化的复现示例或复现步骤,请参考这个指南 https://stackoverflow.com/help/minimal-reproducible-example
|
||||||
|
Please provide a minimal reproduction or steps to reproduce, see this guide https://stackoverflow.com/help/minimal-reproducible-example
|
||||||
|
为什么需要重现(问题)?请参阅这篇文章 https://antfu.me/posts/why-reproductions-are-required
|
||||||
|
Why reproduction is required? see this article https://antfu.me/posts/why-reproductions-are-required
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: expected-behavior
|
||||||
|
attributes:
|
||||||
|
label: 预期结果 / Expected behavior
|
||||||
|
description: 清楚地描述您期望发生的事情。 / A clear description of what you expected to happen.
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: context
|
||||||
|
attributes:
|
||||||
|
label: 额外上下文 / Additional context
|
||||||
|
description: 在这里添加关于问题的任何其他上下文。 / Add any other context about the problem here.
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
# Copyright 2024-present Easytier Programme within The Commons Conservancy
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
name: 💡 新功能请求 / Feature Request
|
||||||
|
title: '[feat] '
|
||||||
|
description: 提出一个想法 / Suggest an idea
|
||||||
|
labels: ['type: feature request']
|
||||||
|
|
||||||
|
body:
|
||||||
|
- type: textarea
|
||||||
|
id: problem
|
||||||
|
attributes:
|
||||||
|
label: 描述问题 / Describe the problem
|
||||||
|
description: 明确描述此功能将解决的问题 / A clear description of the problem this feature would solve
|
||||||
|
placeholder: "我总是在...感觉困惑 / I'm always frustrated when..."
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: solution
|
||||||
|
attributes:
|
||||||
|
label: "描述您想要的解决方案 / Describe the solution you'd like"
|
||||||
|
description: 明确说明您希望做出的改变 / A clear description of what change you would like
|
||||||
|
placeholder: '我希望... / I would like to...'
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: alternatives
|
||||||
|
attributes:
|
||||||
|
label: 替代方案 / Alternatives considered
|
||||||
|
description: "您考虑过的任何替代解决方案 / Any alternative solutions you've considered"
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: context
|
||||||
|
attributes:
|
||||||
|
label: 额外上下文 / Additional context
|
||||||
|
description: 在此处添加有关问题的任何其他上下文。 / Add any other context about the problem here.
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
RUN apk add --no-cache tzdata tini
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=builder --chmod=755 /tmp/output/* /usr/local/bin
|
||||||
|
|
||||||
|
# users can use "-e TZ=xxx" to adjust it
|
||||||
|
ENV TZ Asia/Shanghai
|
||||||
|
|
||||||
|
# tcp
|
||||||
|
EXPOSE 11010/tcp
|
||||||
|
# udp
|
||||||
|
EXPOSE 11010/udp
|
||||||
|
# wg
|
||||||
|
EXPOSE 11011/udp
|
||||||
|
# ws
|
||||||
|
EXPOSE 11011/tcp
|
||||||
|
# wss
|
||||||
|
EXPOSE 11012/tcp
|
||||||
|
|
||||||
|
ENTRYPOINT ["/sbin/tini", "--", "easytier-core"]
|
||||||
@@ -2,7 +2,7 @@ name: EasyTier Core
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: ["develop", "main"]
|
branches: ["develop", "main", "releases/**"]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: ["develop", "main"]
|
branches: ["develop", "main"]
|
||||||
|
|
||||||
@@ -20,51 +20,19 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
# Map a step output to a job output
|
# Map a step output to a job output
|
||||||
outputs:
|
outputs:
|
||||||
should_skip: ${{ steps.skip_check.outputs.should_skip }}
|
# do not skip push on branch starts with releases/
|
||||||
|
should_skip: ${{ steps.skip_check.outputs.should_skip == 'true' && !startsWith(github.ref_name, 'releases/') }}
|
||||||
steps:
|
steps:
|
||||||
- id: skip_check
|
- id: skip_check
|
||||||
uses: fkirc/skip-duplicate-actions@v5
|
uses: fkirc/skip-duplicate-actions@v5
|
||||||
with:
|
with:
|
||||||
# All of these options are optional, so you can remove them if you are happy with the defaults
|
# All of these options are optional, so you can remove them if you are happy with the defaults
|
||||||
concurrent_skipping: 'never'
|
concurrent_skipping: 'same_content_newer'
|
||||||
skip_after_successful_duplicate: 'true'
|
skip_after_successful_duplicate: 'true'
|
||||||
paths: '["Cargo.toml", "Cargo.lock", "easytier/**", ".github/workflows/core.yml"]'
|
cancel_others: 'true'
|
||||||
build:
|
paths: '["Cargo.toml", "Cargo.lock", "easytier/**", ".github/workflows/core.yml", ".github/workflows/install_rust.sh"]'
|
||||||
strategy:
|
build_web:
|
||||||
fail-fast: false
|
runs-on: ubuntu-latest
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- TARGET: aarch64-unknown-linux-musl
|
|
||||||
OS: ubuntu-latest
|
|
||||||
- TARGET: x86_64-unknown-linux-musl
|
|
||||||
OS: ubuntu-latest
|
|
||||||
- TARGET: mips-unknown-linux-musl
|
|
||||||
OS: ubuntu-latest
|
|
||||||
- TARGET: mipsel-unknown-linux-musl
|
|
||||||
OS: ubuntu-latest
|
|
||||||
|
|
||||||
- TARGET: x86_64-apple-darwin
|
|
||||||
OS: macos-latest
|
|
||||||
- TARGET: aarch64-apple-darwin
|
|
||||||
OS: macos-latest
|
|
||||||
|
|
||||||
- TARGET: x86_64-pc-windows-msvc
|
|
||||||
OS: windows-latest
|
|
||||||
|
|
||||||
- TARGET: armv7-unknown-linux-musleabihf # raspberry pi 2-3-4, not tested
|
|
||||||
OS: ubuntu-latest
|
|
||||||
- TARGET: armv7-unknown-linux-musleabi # raspberry pi 2-3-4, not tested
|
|
||||||
OS: ubuntu-latest
|
|
||||||
- TARGET: arm-unknown-linux-musleabihf # raspberry pi 0-1, not tested
|
|
||||||
OS: ubuntu-latest
|
|
||||||
- TARGET: arm-unknown-linux-musleabi # raspberry pi 0-1, not tested
|
|
||||||
OS: ubuntu-latest
|
|
||||||
runs-on: ${{ matrix.OS }}
|
|
||||||
env:
|
|
||||||
NAME: easytier
|
|
||||||
TARGET: ${{ matrix.TARGET }}
|
|
||||||
OS: ${{ matrix.OS }}
|
|
||||||
OSS_BUCKET: ${{ secrets.ALIYUN_OSS_BUCKET }}
|
|
||||||
needs: pre_job
|
needs: pre_job
|
||||||
if: needs.pre_job.outputs.should_skip != 'true'
|
if: needs.pre_job.outputs.should_skip != 'true'
|
||||||
steps:
|
steps:
|
||||||
@@ -74,7 +42,113 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
node-version: 21
|
node-version: 21
|
||||||
|
|
||||||
|
- name: Install pnpm
|
||||||
|
uses: pnpm/action-setup@v3
|
||||||
|
with:
|
||||||
|
version: 9
|
||||||
|
run_install: false
|
||||||
|
|
||||||
|
- name: Get pnpm store directory
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Setup pnpm cache
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: ${{ env.STORE_PATH }}
|
||||||
|
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-pnpm-store-
|
||||||
|
|
||||||
|
- name: Install frontend dependencies
|
||||||
|
run: |
|
||||||
|
pnpm -r install
|
||||||
|
pnpm -r --filter "./easytier-web/*" build
|
||||||
|
|
||||||
|
- name: Archive artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: easytier-web-dashboard
|
||||||
|
path: |
|
||||||
|
easytier-web/frontend/dist/*
|
||||||
|
build:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- TARGET: aarch64-unknown-linux-musl
|
||||||
|
OS: ubuntu-22.04
|
||||||
|
ARTIFACT_NAME: linux-aarch64
|
||||||
|
- TARGET: x86_64-unknown-linux-musl
|
||||||
|
OS: ubuntu-22.04
|
||||||
|
ARTIFACT_NAME: linux-x86_64
|
||||||
|
- TARGET: mips-unknown-linux-musl
|
||||||
|
OS: ubuntu-22.04
|
||||||
|
ARTIFACT_NAME: linux-mips
|
||||||
|
- TARGET: mipsel-unknown-linux-musl
|
||||||
|
OS: ubuntu-22.04
|
||||||
|
ARTIFACT_NAME: linux-mipsel
|
||||||
|
- TARGET: armv7-unknown-linux-musleabihf # raspberry pi 2-3-4, not tested
|
||||||
|
OS: ubuntu-22.04
|
||||||
|
ARTIFACT_NAME: linux-armv7hf
|
||||||
|
- TARGET: armv7-unknown-linux-musleabi # raspberry pi 2-3-4, not tested
|
||||||
|
OS: ubuntu-22.04
|
||||||
|
ARTIFACT_NAME: linux-armv7
|
||||||
|
- TARGET: arm-unknown-linux-musleabihf # raspberry pi 0-1, not tested
|
||||||
|
OS: ubuntu-22.04
|
||||||
|
ARTIFACT_NAME: linux-armhf
|
||||||
|
- TARGET: arm-unknown-linux-musleabi # raspberry pi 0-1, not tested
|
||||||
|
OS: ubuntu-22.04
|
||||||
|
ARTIFACT_NAME: linux-arm
|
||||||
|
|
||||||
|
- TARGET: x86_64-apple-darwin
|
||||||
|
OS: macos-latest
|
||||||
|
ARTIFACT_NAME: macos-x86_64
|
||||||
|
- TARGET: aarch64-apple-darwin
|
||||||
|
OS: macos-latest
|
||||||
|
ARTIFACT_NAME: macos-aarch64
|
||||||
|
|
||||||
|
- TARGET: x86_64-pc-windows-msvc
|
||||||
|
OS: windows-latest
|
||||||
|
ARTIFACT_NAME: windows-x86_64
|
||||||
|
- TARGET: aarch64-pc-windows-msvc
|
||||||
|
OS: windows-latest
|
||||||
|
ARTIFACT_NAME: windows-arm64
|
||||||
|
- TARGET: i686-pc-windows-msvc
|
||||||
|
OS: windows-latest
|
||||||
|
ARTIFACT_NAME: windows-i686
|
||||||
|
|
||||||
|
- TARGET: x86_64-unknown-freebsd
|
||||||
|
OS: ubuntu-22.04
|
||||||
|
ARTIFACT_NAME: freebsd-13.2-x86_64
|
||||||
|
BSD_VERSION: 13.2
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.OS }}
|
||||||
|
env:
|
||||||
|
NAME: easytier
|
||||||
|
TARGET: ${{ matrix.TARGET }}
|
||||||
|
OS: ${{ matrix.OS }}
|
||||||
|
OSS_BUCKET: ${{ secrets.ALIYUN_OSS_BUCKET }}
|
||||||
|
needs:
|
||||||
|
- pre_job
|
||||||
|
- build_web
|
||||||
|
if: needs.pre_job.outputs.should_skip != 'true'
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set current ref as env variable
|
||||||
|
run: |
|
||||||
|
echo "GIT_DESC=$(git log -1 --format=%cd.%h --date=format:%Y-%m-%d_%H:%M:%S)" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Download web artifact
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: easytier-web-dashboard
|
||||||
|
path: easytier-web/frontend/dist/
|
||||||
|
|
||||||
- name: Cargo cache
|
- name: Cargo cache
|
||||||
|
if: ${{ ! endsWith(matrix.TARGET, 'freebsd') }}
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
@@ -82,9 +156,6 @@ jobs:
|
|||||||
./target
|
./target
|
||||||
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
|
||||||
- name: Install rust target
|
|
||||||
run: bash ./.github/workflows/install_rust.sh
|
|
||||||
|
|
||||||
- name: Setup protoc
|
- name: Setup protoc
|
||||||
uses: arduino/setup-protoc@v2
|
uses: arduino/setup-protoc@v2
|
||||||
with:
|
with:
|
||||||
@@ -92,28 +163,79 @@ jobs:
|
|||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Build Core & Cli
|
- name: Build Core & Cli
|
||||||
|
if: ${{ ! endsWith(matrix.TARGET, 'freebsd') }}
|
||||||
run: |
|
run: |
|
||||||
if [[ $OS =~ ^ubuntu.*$ && $TARGET =~ ^mips.*$ ]]; then
|
bash ./.github/workflows/install_rust.sh
|
||||||
cargo +nightly build -r --verbose --target $TARGET -Z build-std=std,panic_abort --no-default-features --features mips
|
|
||||||
else
|
# we set the sysroot when sysroot is a dir
|
||||||
cargo build --release --verbose --target $TARGET
|
# this dir is a soft link generated by install_rust.sh
|
||||||
|
# kcp-sys need this to gen ffi bindings. without this clang may fail to find some libc headers such as bits/libc-header-start.h
|
||||||
|
if [[ -d "./musl_gcc/sysroot" ]]; then
|
||||||
|
export BINDGEN_EXTRA_CLANG_ARGS=--sysroot=$(readlink -f ./musl_gcc/sysroot)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Install UPX
|
if [[ $OS =~ ^ubuntu.*$ && $TARGET =~ ^mips.*$ ]]; then
|
||||||
if: ${{ matrix.OS != 'macos-latest' }}
|
cargo +nightly build -r --target $TARGET -Z build-std=std,panic_abort --package=easytier
|
||||||
uses: crazy-max/ghaction-upx@v3
|
else
|
||||||
|
if [[ $OS =~ ^windows.*$ ]]; then
|
||||||
|
SUFFIX=.exe
|
||||||
|
fi
|
||||||
|
cargo build --release --target $TARGET --package=easytier-web --features=embed
|
||||||
|
mv ./target/$TARGET/release/easytier-web"$SUFFIX" ./target/$TARGET/release/easytier-web-embed"$SUFFIX"
|
||||||
|
cargo build --release --target $TARGET
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Copied and slightly modified from @lmq8267 (https://github.com/lmq8267)
|
||||||
|
- name: Build Core & Cli (X86_64 FreeBSD)
|
||||||
|
uses: vmactions/freebsd-vm@v1
|
||||||
|
if: ${{ endsWith(matrix.TARGET, 'freebsd') }}
|
||||||
|
env:
|
||||||
|
TARGET: ${{ matrix.TARGET }}
|
||||||
with:
|
with:
|
||||||
version: latest
|
envs: TARGET
|
||||||
install-only: true
|
release: ${{ matrix.BSD_VERSION }}
|
||||||
|
arch: x86_64
|
||||||
|
usesh: true
|
||||||
|
mem: 6144
|
||||||
|
cpu: 4
|
||||||
|
run: |
|
||||||
|
uname -a
|
||||||
|
echo $SHELL
|
||||||
|
pwd
|
||||||
|
ls -lah
|
||||||
|
whoami
|
||||||
|
env | sort
|
||||||
|
|
||||||
|
pkg install -y git protobuf llvm-devel sudo curl
|
||||||
|
curl --proto 'https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||||
|
. $HOME/.cargo/env
|
||||||
|
|
||||||
|
rustup set auto-self-update disable
|
||||||
|
|
||||||
|
rustup install 1.86
|
||||||
|
rustup default 1.86
|
||||||
|
|
||||||
|
export CC=clang
|
||||||
|
export CXX=clang++
|
||||||
|
export CARGO_TERM_COLOR=always
|
||||||
|
|
||||||
|
cargo build --release --verbose --target $TARGET --package=easytier-web --features=embed
|
||||||
|
mv ./target/$TARGET/release/easytier-web ./target/$TARGET/release/easytier-web-embed
|
||||||
|
cargo build --release --verbose --target $TARGET
|
||||||
|
|
||||||
- name: Compress
|
- name: Compress
|
||||||
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/*.dll ./artifacts/objects/
|
||||||
cp easytier/third_party/wintun.dll ./artifacts/objects/
|
elif [[ $OS =~ ^windows.*$ && $TARGET =~ ^i686.*$ ]]; then
|
||||||
|
SUFFIX=.exe
|
||||||
|
cp easytier/third_party/i686/*.dll ./artifacts/objects/
|
||||||
|
elif [[ $OS =~ ^windows.*$ && $TARGET =~ ^aarch64.*$ ]]; then
|
||||||
|
SUFFIX=.exe
|
||||||
|
cp easytier/third_party/arm64/*.dll ./artifacts/objects/
|
||||||
fi
|
fi
|
||||||
if [[ $GITHUB_REF_TYPE =~ ^tag$ ]]; then
|
if [[ $GITHUB_REF_TYPE =~ ^tag$ ]]; then
|
||||||
TAG=$GITHUB_REF_NAME
|
TAG=$GITHUB_REF_NAME
|
||||||
@@ -121,43 +243,77 @@ jobs:
|
|||||||
TAG=$GITHUB_SHA
|
TAG=$GITHUB_SHA
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ ! $OS =~ ^macos.*$ ]]; then
|
if [[ $OS =~ ^ubuntu.*$ && ! $TARGET =~ ^.*freebsd$ ]]; then
|
||||||
upx --lzma --best ./target/$TARGET/release/easytier-core"$SUFFIX"
|
UPX_VERSION=4.2.4
|
||||||
upx --lzma --best ./target/$TARGET/release/easytier-cli"$SUFFIX"
|
curl -L https://github.com/upx/upx/releases/download/v${UPX_VERSION}/upx-${UPX_VERSION}-amd64_linux.tar.xz -s | tar xJvf -
|
||||||
|
cp upx-${UPX_VERSION}-amd64_linux/upx .
|
||||||
|
./upx --lzma --best ./target/$TARGET/release/easytier-core"$SUFFIX"
|
||||||
|
./upx --lzma --best ./target/$TARGET/release/easytier-cli"$SUFFIX"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
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/
|
||||||
|
mv ./target/$TARGET/release/easytier-web-embed"$SUFFIX" ./artifacts/objects/
|
||||||
|
fi
|
||||||
|
|
||||||
tar -cvf ./artifacts/$NAME-$TARGET-$TAG.tar -C ./artifacts/objects/ .
|
mv ./artifacts/objects/* ./artifacts/
|
||||||
rm -rf ./artifacts/objects/
|
rm -rf ./artifacts/objects/
|
||||||
|
|
||||||
- name: Archive artifact
|
- name: Archive artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: easytier-${{ matrix.OS }}-${{ matrix.TARGET }}
|
name: easytier-${{ matrix.ARTIFACT_NAME }}
|
||||||
path: |
|
path: |
|
||||||
./artifacts/*
|
./artifacts/*
|
||||||
|
|
||||||
- name: Upload OSS
|
|
||||||
if: ${{ env.OSS_BUCKET != '' }}
|
|
||||||
uses: Menci/upload-to-oss@main
|
|
||||||
with:
|
|
||||||
access-key-id: ${{ secrets.ALIYUN_OSS_ACCESS_ID }}
|
|
||||||
access-key-secret: ${{ secrets.ALIYUN_OSS_ACCESS_KEY }}
|
|
||||||
endpoint: ${{ secrets.ALIYUN_OSS_ENDPOINT }}
|
|
||||||
bucket: ${{ secrets.ALIYUN_OSS_BUCKET }}
|
|
||||||
local-path: ./artifacts/
|
|
||||||
remote-path: /easytier-releases/${{ github.sha }}/
|
|
||||||
no-delete-remote-files: true
|
|
||||||
retry: 5
|
|
||||||
core-result:
|
core-result:
|
||||||
if: needs.pre_job.outputs.should_skip != 'true' && always()
|
if: needs.pre_job.outputs.should_skip != 'true' && always()
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs:
|
needs:
|
||||||
- pre_job
|
- pre_job
|
||||||
|
- build_web
|
||||||
- build
|
- build
|
||||||
steps:
|
steps:
|
||||||
- name: Mark result as failed
|
- name: Mark result as failed
|
||||||
if: needs.build.result != 'success'
|
if: needs.build.result != 'success'
|
||||||
run: exit 1
|
run: exit 1
|
||||||
|
|
||||||
|
magisk_build:
|
||||||
|
needs:
|
||||||
|
- pre_job
|
||||||
|
- build_web
|
||||||
|
- build
|
||||||
|
if: needs.pre_job.outputs.should_skip != 'true' && always()
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout Code
|
||||||
|
uses: actions/checkout@v4 # 必须先检出代码才能获取模块配置
|
||||||
|
|
||||||
|
# 下载二进制文件到独立目录
|
||||||
|
- name: Download Linux aarch64 binaries
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: easytier-linux-aarch64
|
||||||
|
path: ./downloaded-binaries/ # 独立目录避免冲突
|
||||||
|
|
||||||
|
# 将二进制文件复制到 Magisk 模块目录
|
||||||
|
- name: Prepare binaries
|
||||||
|
run: |
|
||||||
|
mkdir -p ./easytier-contrib/easytier-magisk/
|
||||||
|
cp ./downloaded-binaries/easytier-core ./easytier-contrib/easytier-magisk/
|
||||||
|
cp ./downloaded-binaries/easytier-cli ./easytier-contrib/easytier-magisk/
|
||||||
|
cp ./downloaded-binaries/easytier-web ./easytier-contrib/easytier-magisk/
|
||||||
|
|
||||||
|
|
||||||
|
# 上传生成的模块
|
||||||
|
- name: Upload Magisk Module
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: Easytier-Magisk
|
||||||
|
path: |
|
||||||
|
./easytier-contrib/easytier-magisk
|
||||||
|
!./easytier-contrib/easytier-magisk/build.sh
|
||||||
|
!./easytier-contrib/easytier-magisk/magisk_update.json
|
||||||
|
if-no-files-found: error
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
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: 'v2.3.2'
|
||||||
|
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: login github container registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- name: Download artifact
|
||||||
|
id: download-artifact
|
||||||
|
uses: dawidd6/action-download-artifact@v6
|
||||||
|
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' || '' }},
|
||||||
|
ghcr.io/easytier/easytier:${{ inputs.image_tag }}${{ inputs.mark_latest && ',easytier/easytier:latest' || '' }},
|
||||||
@@ -2,7 +2,7 @@ name: EasyTier GUI
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: ["develop", "main"]
|
branches: ["develop", "main", "releases/**"]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: ["develop", "main"]
|
branches: ["develop", "main"]
|
||||||
|
|
||||||
@@ -20,37 +20,54 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
# Map a step output to a job output
|
# Map a step output to a job output
|
||||||
outputs:
|
outputs:
|
||||||
should_skip: ${{ steps.skip_check.outputs.should_skip }}
|
should_skip: ${{ steps.skip_check.outputs.should_skip == 'true' && !startsWith(github.ref_name, 'releases/') }}
|
||||||
steps:
|
steps:
|
||||||
- id: skip_check
|
- id: skip_check
|
||||||
uses: fkirc/skip-duplicate-actions@v5
|
uses: fkirc/skip-duplicate-actions@v5
|
||||||
with:
|
with:
|
||||||
# All of these options are optional, so you can remove them if you are happy with the defaults
|
# All of these options are optional, so you can remove them if you are happy with the defaults
|
||||||
concurrent_skipping: 'never'
|
concurrent_skipping: 'same_content_newer'
|
||||||
skip_after_successful_duplicate: 'true'
|
skip_after_successful_duplicate: 'true'
|
||||||
paths: '["Cargo.toml", "Cargo.lock", "easytier/**", "easytier-gui/**", ".github/workflows/gui.yml"]'
|
cancel_others: 'true'
|
||||||
|
paths: '["Cargo.toml", "Cargo.lock", "easytier/**", "easytier-gui/**", ".github/workflows/gui.yml", ".github/workflows/install_rust.sh"]'
|
||||||
build-gui:
|
build-gui:
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
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
|
||||||
- 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
|
||||||
|
|
||||||
- TARGET: x86_64-apple-darwin
|
- TARGET: x86_64-apple-darwin
|
||||||
OS: macos-latest
|
OS: macos-latest
|
||||||
GUI_TARGET: x86_64-apple-darwin
|
GUI_TARGET: x86_64-apple-darwin
|
||||||
|
ARTIFACT_NAME: macos-x86_64
|
||||||
- TARGET: aarch64-apple-darwin
|
- TARGET: aarch64-apple-darwin
|
||||||
OS: macos-latest
|
OS: macos-latest
|
||||||
GUI_TARGET: aarch64-apple-darwin
|
GUI_TARGET: aarch64-apple-darwin
|
||||||
|
ARTIFACT_NAME: macos-aarch64
|
||||||
|
|
||||||
- TARGET: x86_64-pc-windows-msvc
|
- TARGET: x86_64-pc-windows-msvc
|
||||||
OS: windows-latest
|
OS: windows-latest
|
||||||
GUI_TARGET: x86_64-pc-windows-msvc
|
GUI_TARGET: x86_64-pc-windows-msvc
|
||||||
|
ARTIFACT_NAME: windows-x86_64
|
||||||
|
|
||||||
|
- TARGET: aarch64-pc-windows-msvc
|
||||||
|
OS: windows-latest
|
||||||
|
GUI_TARGET: aarch64-pc-windows-msvc
|
||||||
|
ARTIFACT_NAME: windows-arm64
|
||||||
|
|
||||||
|
- TARGET: i686-pc-windows-msvc
|
||||||
|
OS: windows-latest
|
||||||
|
GUI_TARGET: i686-pc-windows-msvc
|
||||||
|
ARTIFACT_NAME: windows-i686
|
||||||
|
|
||||||
runs-on: ${{ matrix.OS }}
|
runs-on: ${{ matrix.OS }}
|
||||||
env:
|
env:
|
||||||
NAME: easytier
|
NAME: easytier
|
||||||
@@ -61,52 +78,20 @@ jobs:
|
|||||||
needs: pre_job
|
needs: pre_job
|
||||||
if: needs.pre_job.outputs.should_skip != 'true'
|
if: needs.pre_job.outputs.should_skip != 'true'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- name: Install GUI dependencies (x86 only)
|
||||||
|
if: ${{ matrix.TARGET == 'x86_64-unknown-linux-musl' }}
|
||||||
- uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: 21
|
|
||||||
|
|
||||||
- name: Install pnpm
|
|
||||||
uses: pnpm/action-setup@v3
|
|
||||||
with:
|
|
||||||
version: 9
|
|
||||||
run_install: false
|
|
||||||
|
|
||||||
- name: Get pnpm store directory
|
|
||||||
shell: bash
|
|
||||||
run: |
|
run: |
|
||||||
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
sudo apt update
|
||||||
|
sudo apt install -qq libwebkit2gtk-4.1-dev \
|
||||||
- name: Setup pnpm cache
|
build-essential \
|
||||||
uses: actions/cache@v4
|
curl \
|
||||||
with:
|
wget \
|
||||||
path: ${{ env.STORE_PATH }}
|
file \
|
||||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
libgtk-3-dev \
|
||||||
restore-keys: |
|
librsvg2-dev \
|
||||||
${{ runner.os }}-pnpm-store-
|
libxdo-dev \
|
||||||
|
libssl-dev \
|
||||||
- name: Install frontend dependencies
|
patchelf
|
||||||
run: |
|
|
||||||
cd easytier-gui
|
|
||||||
pnpm install
|
|
||||||
|
|
||||||
- name: Cargo cache
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.cargo
|
|
||||||
./target
|
|
||||||
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
|
||||||
|
|
||||||
- name: Install rust target
|
|
||||||
run: bash ./.github/workflows/install_rust.sh
|
|
||||||
|
|
||||||
- name: Setup protoc
|
|
||||||
uses: arduino/setup-protoc@v2
|
|
||||||
with:
|
|
||||||
# GitHub repo token to use to avoid rate limiter
|
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Install GUI cross compile (aarch64 only)
|
- name: Install GUI cross compile (aarch64 only)
|
||||||
if: ${{ matrix.TARGET == 'aarch64-unknown-linux-musl' }}
|
if: ${{ matrix.TARGET == 'aarch64-unknown-linux-musl' }}
|
||||||
@@ -135,10 +120,75 @@ jobs:
|
|||||||
echo "deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy-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 update
|
||||||
sudo apt install libwebkit2gtk-4.0-dev:arm64
|
sudo apt install aptitude
|
||||||
sudo apt install libssl-dev:arm64
|
sudo aptitude install -y libgstreamer1.0-0:arm64 gstreamer1.0-plugins-base:arm64 gstreamer1.0-plugins-good:arm64 \
|
||||||
|
libgstreamer-gl1.0-0:arm64 libgstreamer-plugins-base1.0-0:arm64 libgstreamer-plugins-good1.0-0:arm64 libwebkit2gtk-4.1-0:arm64 \
|
||||||
|
libwebkit2gtk-4.1-dev:arm64 libssl-dev:arm64 gcc-aarch64-linux-gnu
|
||||||
echo "PKG_CONFIG_SYSROOT_DIR=/usr/aarch64-linux-gnu/" >> "$GITHUB_ENV"
|
echo "PKG_CONFIG_SYSROOT_DIR=/usr/aarch64-linux-gnu/" >> "$GITHUB_ENV"
|
||||||
|
echo "PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig/" >> "$GITHUB_ENV"
|
||||||
|
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set current ref as env variable
|
||||||
|
run: |
|
||||||
|
echo "GIT_DESC=$(git log -1 --format=%cd.%h --date=format:%Y-%m-%d_%H:%M:%S)" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 21
|
||||||
|
|
||||||
|
- name: Install pnpm
|
||||||
|
uses: pnpm/action-setup@v3
|
||||||
|
with:
|
||||||
|
version: 9
|
||||||
|
run_install: false
|
||||||
|
|
||||||
|
- name: Get pnpm store directory
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Setup pnpm cache
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: ${{ env.STORE_PATH }}
|
||||||
|
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-pnpm-store-
|
||||||
|
|
||||||
|
- name: Install frontend dependencies
|
||||||
|
run: |
|
||||||
|
pnpm -r install
|
||||||
|
pnpm -r build
|
||||||
|
|
||||||
|
- name: Cargo cache
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cargo
|
||||||
|
./target
|
||||||
|
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
|
||||||
|
- name: Install rust target
|
||||||
|
run: bash ./.github/workflows/install_rust.sh
|
||||||
|
|
||||||
|
- name: Setup protoc
|
||||||
|
uses: arduino/setup-protoc@v2
|
||||||
|
with:
|
||||||
|
# GitHub repo token to use to avoid rate limiter
|
||||||
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- 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/
|
||||||
|
elif [[ $GUI_TARGET =~ ^i686.*$ ]]; then
|
||||||
|
cp ./easytier/third_party/i686/*.dll ./easytier-gui/src-tauri/
|
||||||
|
else
|
||||||
|
cp ./easytier/third_party/*.dll ./easytier-gui/src-tauri/
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Build GUI
|
- name: Build GUI
|
||||||
if: ${{ matrix.GUI_TARGET != '' }}
|
if: ${{ matrix.GUI_TARGET != '' }}
|
||||||
@@ -146,7 +196,7 @@ jobs:
|
|||||||
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: |
|
||||||
@@ -170,28 +220,16 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
tar -cvf ./artifacts/$NAME-$TARGET-$TAG.tar -C ./artifacts/objects/ .
|
mv ./artifacts/objects/* ./artifacts/
|
||||||
rm -rf ./artifacts/objects/
|
rm -rf ./artifacts/objects/
|
||||||
|
|
||||||
- name: Archive artifact
|
- name: Archive artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: easytier-gui-${{ matrix.OS }}-${{ matrix.TARGET }}
|
name: easytier-gui-${{ matrix.ARTIFACT_NAME }}
|
||||||
path: |
|
path: |
|
||||||
./artifacts/*
|
./artifacts/*
|
||||||
|
|
||||||
- name: Upload OSS
|
|
||||||
if: ${{ env.OSS_BUCKET != '' }}
|
|
||||||
uses: Menci/upload-to-oss@main
|
|
||||||
with:
|
|
||||||
access-key-id: ${{ secrets.ALIYUN_OSS_ACCESS_ID }}
|
|
||||||
access-key-secret: ${{ secrets.ALIYUN_OSS_ACCESS_KEY }}
|
|
||||||
endpoint: ${{ secrets.ALIYUN_OSS_ENDPOINT }}
|
|
||||||
bucket: ${{ secrets.ALIYUN_OSS_BUCKET }}
|
|
||||||
local-path: ./artifacts/
|
|
||||||
remote-path: /easytier-releases/${{ github.sha }}/gui
|
|
||||||
no-delete-remote-files: true
|
|
||||||
retry: 5
|
|
||||||
gui-result:
|
gui-result:
|
||||||
if: needs.pre_job.outputs.should_skip != 'true' && always()
|
if: needs.pre_job.outputs.should_skip != 'true' && always()
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
@@ -8,61 +8,33 @@
|
|||||||
# dependencies are only needed on ubuntu as that's the only place where
|
# dependencies are only needed on ubuntu as that's the only place where
|
||||||
# we make cross-compilation
|
# we make cross-compilation
|
||||||
if [[ $OS =~ ^ubuntu.*$ ]]; then
|
if [[ $OS =~ ^ubuntu.*$ ]]; then
|
||||||
sudo apt-get update && sudo apt-get install -qq crossbuild-essential-arm64 crossbuild-essential-armhf musl-tools
|
sudo apt-get update && sudo apt-get install -qq musl-tools libappindicator3-dev llvm clang
|
||||||
# for easytier-gui
|
# https://github.com/cross-tools/musl-cross/releases
|
||||||
if [[ $GUI_TARGET != '' ]]; then
|
# if "musl" is a substring of TARGET, we assume that we are using musl
|
||||||
sudo apt install libwebkit2gtk-4.0-dev \
|
MUSL_TARGET=$TARGET
|
||||||
build-essential \
|
# if target is mips or mipsel, we should use soft-float version of musl
|
||||||
curl \
|
if [[ $TARGET =~ ^mips.*$ || $TARGET =~ ^mipsel.*$ ]]; then
|
||||||
wget \
|
MUSL_TARGET=${TARGET}sf
|
||||||
file \
|
|
||||||
libssl-dev \
|
|
||||||
libgtk-3-dev \
|
|
||||||
libayatana-appindicator3-dev \
|
|
||||||
librsvg2-dev \
|
|
||||||
patchelf
|
|
||||||
fi
|
fi
|
||||||
# curl -s musl.cc | grep mipsel
|
if [[ $MUSL_TARGET =~ musl ]]; then
|
||||||
case $TARGET in
|
|
||||||
mipsel-unknown-linux-musl)
|
|
||||||
MUSL_URI=mipsel-linux-muslsf
|
|
||||||
;;
|
|
||||||
mips-unknown-linux-musl)
|
|
||||||
MUSL_URI=mips-linux-muslsf
|
|
||||||
;;
|
|
||||||
aarch64-unknown-linux-musl)
|
|
||||||
MUSL_URI=aarch64-linux-musl
|
|
||||||
;;
|
|
||||||
armv7-unknown-linux-musleabihf)
|
|
||||||
MUSL_URI=armv7l-linux-musleabihf
|
|
||||||
;;
|
|
||||||
armv7-unknown-linux-musleabi)
|
|
||||||
MUSL_URI=armv7m-linux-musleabi
|
|
||||||
;;
|
|
||||||
arm-unknown-linux-musleabihf)
|
|
||||||
MUSL_URI=arm-linux-musleabihf
|
|
||||||
;;
|
|
||||||
arm-unknown-linux-musleabi)
|
|
||||||
MUSL_URI=arm-linux-musleabi
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
if [ -n "$MUSL_URI" ]; then
|
|
||||||
mkdir -p ./musl_gcc
|
mkdir -p ./musl_gcc
|
||||||
wget -c https://musl.cc/${MUSL_URI}-cross.tgz -P ./musl_gcc/
|
wget --inet4-only -c https://github.com/cross-tools/musl-cross/releases/download/20250520/${MUSL_TARGET}.tar.xz -P ./musl_gcc/
|
||||||
tar zxf ./musl_gcc/${MUSL_URI}-cross.tgz -C ./musl_gcc/
|
tar xf ./musl_gcc/${MUSL_TARGET}.tar.xz -C ./musl_gcc/
|
||||||
sudo ln -s $(pwd)/musl_gcc/${MUSL_URI}-cross/bin/*gcc /usr/bin/
|
sudo ln -sf $(pwd)/musl_gcc/${MUSL_TARGET}/bin/*gcc /usr/bin/
|
||||||
|
sudo ln -sf $(pwd)/musl_gcc/${MUSL_TARGET}/include/ /usr/include/musl-cross
|
||||||
|
sudo ln -sf $(pwd)/musl_gcc/${MUSL_TARGET}/${MUSL_TARGET}/sysroot/ ./musl_gcc/sysroot
|
||||||
|
sudo chmod -R a+rwx ./musl_gcc
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# see https://github.com/rust-lang/rustup/issues/3709
|
# see https://github.com/rust-lang/rustup/issues/3709
|
||||||
rustup set auto-self-update disable
|
rustup set auto-self-update disable
|
||||||
rustup install 1.75
|
rustup install 1.86
|
||||||
rustup default 1.75
|
rustup default 1.86
|
||||||
|
|
||||||
# mips/mipsel cannot add target from rustup, need compile by ourselves
|
# mips/mipsel cannot add target from rustup, need compile by ourselves
|
||||||
if [[ $OS =~ ^ubuntu.*$ && $TARGET =~ ^mips.*$ ]]; then
|
if [[ $OS =~ ^ubuntu.*$ && $TARGET =~ ^mips.*$ ]]; then
|
||||||
cd "$PWD/musl_gcc/${MUSL_URI}-cross/lib/gcc/${MUSL_URI}/11.2.1" || exit 255
|
cd "$PWD/musl_gcc/${MUSL_TARGET}/lib/gcc/${MUSL_TARGET}/15.1.0" || exit 255
|
||||||
# for panic-abort
|
# for panic-abort
|
||||||
cp libgcc_eh.a libunwind.a
|
cp libgcc_eh.a libunwind.a
|
||||||
|
|
||||||
@@ -72,7 +44,13 @@ if [[ $OS =~ ^ubuntu.*$ && $TARGET =~ ^mips.*$ ]]; then
|
|||||||
|
|
||||||
rustup toolchain install nightly-x86_64-unknown-linux-gnu
|
rustup toolchain install nightly-x86_64-unknown-linux-gnu
|
||||||
rustup component add rust-src --toolchain 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
|
else
|
||||||
rustup target add $TARGET
|
rustup target add $TARGET
|
||||||
if [[ $GUI_TARGET != '' ]]; then
|
if [[ $GUI_TARGET != '' ]]; then
|
||||||
|
|||||||
@@ -0,0 +1,158 @@
|
|||||||
|
name: EasyTier Mobile
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: ["develop", "main", "releases/**"]
|
||||||
|
pull_request:
|
||||||
|
branches: ["develop", "main"]
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
# necessary for windows
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
pre_job:
|
||||||
|
# continue-on-error: true # Uncomment once integration is finished
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
# Map a step output to a job output
|
||||||
|
outputs:
|
||||||
|
should_skip: ${{ steps.skip_check.outputs.should_skip == 'true' && !startsWith(github.ref_name, 'releases/') }}
|
||||||
|
steps:
|
||||||
|
- id: skip_check
|
||||||
|
uses: fkirc/skip-duplicate-actions@v5
|
||||||
|
with:
|
||||||
|
# All of these options are optional, so you can remove them if you are happy with the defaults
|
||||||
|
concurrent_skipping: 'same_content_newer'
|
||||||
|
skip_after_successful_duplicate: 'true'
|
||||||
|
cancel_others: 'true'
|
||||||
|
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
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- TARGET: android
|
||||||
|
OS: ubuntu-22.04
|
||||||
|
ARTIFACT_NAME: android
|
||||||
|
runs-on: ${{ matrix.OS }}
|
||||||
|
env:
|
||||||
|
NAME: easytier
|
||||||
|
TARGET: ${{ matrix.TARGET }}
|
||||||
|
OS: ${{ matrix.OS }}
|
||||||
|
OSS_BUCKET: ${{ secrets.ALIYUN_OSS_BUCKET }}
|
||||||
|
needs: pre_job
|
||||||
|
if: needs.pre_job.outputs.should_skip != 'true'
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set current ref as env variable
|
||||||
|
run: |
|
||||||
|
echo "GIT_DESC=$(git log -1 --format=%cd.%h --date=format:%Y-%m-%d_%H:%M:%S)" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
distribution: 'oracle'
|
||||||
|
java-version: '20'
|
||||||
|
|
||||||
|
- name: Setup Android SDK
|
||||||
|
uses: android-actions/setup-android@v3
|
||||||
|
with:
|
||||||
|
cmdline-tools-version: 11076708
|
||||||
|
packages: 'build-tools;34.0.0 ndk;26.0.10792818 tools platform-tools platforms;android-34 '
|
||||||
|
|
||||||
|
- name: Setup Android Environment
|
||||||
|
run: |
|
||||||
|
echo "$ANDROID_HOME/platform-tools" >> $GITHUB_PATH
|
||||||
|
echo "$ANDROID_HOME/ndk/26.0.10792818/toolchains/llvm/prebuilt/linux-x86_64/bin" >> $GITHUB_PATH
|
||||||
|
echo "NDK_HOME=$ANDROID_HOME/ndk/26.0.10792818/" > $GITHUB_ENV
|
||||||
|
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 21
|
||||||
|
|
||||||
|
- name: Install pnpm
|
||||||
|
uses: pnpm/action-setup@v3
|
||||||
|
with:
|
||||||
|
version: 9
|
||||||
|
run_install: false
|
||||||
|
|
||||||
|
- name: Get pnpm store directory
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Setup pnpm cache
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: ${{ env.STORE_PATH }}
|
||||||
|
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-pnpm-store-
|
||||||
|
|
||||||
|
- name: Install frontend dependencies
|
||||||
|
run: |
|
||||||
|
pnpm -r install
|
||||||
|
pnpm -r build
|
||||||
|
|
||||||
|
- name: Cargo cache
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cargo
|
||||||
|
./target
|
||||||
|
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
|
||||||
|
- name: Install rust target
|
||||||
|
run: |
|
||||||
|
bash ./.github/workflows/install_rust.sh
|
||||||
|
rustup target add aarch64-linux-android
|
||||||
|
rustup target add armv7-linux-androideabi
|
||||||
|
rustup target add i686-linux-android
|
||||||
|
rustup target add x86_64-linux-android
|
||||||
|
|
||||||
|
- name: Setup protoc
|
||||||
|
uses: arduino/setup-protoc@v2
|
||||||
|
with:
|
||||||
|
# GitHub repo token to use to avoid rate limiter
|
||||||
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Build Android
|
||||||
|
run: |
|
||||||
|
cd easytier-gui
|
||||||
|
pnpm tauri android build
|
||||||
|
|
||||||
|
- name: Compress
|
||||||
|
run: |
|
||||||
|
mkdir -p ./artifacts/objects/
|
||||||
|
mv easytier-gui/src-tauri/gen/android/app/build/outputs/apk/universal/release/app-universal-release.apk ./artifacts/objects/
|
||||||
|
|
||||||
|
if [[ $GITHUB_REF_TYPE =~ ^tag$ ]]; then
|
||||||
|
TAG=$GITHUB_REF_NAME
|
||||||
|
else
|
||||||
|
TAG=$GITHUB_SHA
|
||||||
|
fi
|
||||||
|
|
||||||
|
mv ./artifacts/objects/* ./artifacts
|
||||||
|
rm -rf ./artifacts/objects/
|
||||||
|
|
||||||
|
- name: Archive artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: easytier-gui-${{ matrix.ARTIFACT_NAME }}
|
||||||
|
path: |
|
||||||
|
./artifacts/*
|
||||||
|
|
||||||
|
mobile-result:
|
||||||
|
if: needs.pre_job.outputs.should_skip != 'true' && always()
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs:
|
||||||
|
- pre_job
|
||||||
|
- build-mobile
|
||||||
|
steps:
|
||||||
|
- name: Mark result as failed
|
||||||
|
if: needs.build-mobile.result != 'success'
|
||||||
|
run: exit 1
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
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: 'v2.3.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_nozip
|
||||||
|
|
||||||
|
- name: Download Mobile 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_nozip
|
||||||
|
|
||||||
|
- name: Zip release assets
|
||||||
|
env:
|
||||||
|
VERSION: ${{ inputs.version }}
|
||||||
|
run: |
|
||||||
|
mkdir zipped_assets
|
||||||
|
|
||||||
|
find release_assets_nozip -type f -exec mv {} zipped_assets \;
|
||||||
|
ls -l -R ./zipped_assets
|
||||||
|
|
||||||
|
cd release_assets
|
||||||
|
ls -l -R ./
|
||||||
|
chmod -R 755 .
|
||||||
|
for x in `ls`; do
|
||||||
|
if [ "$x" = "Easytier-Magisk" ]; then
|
||||||
|
# for Easytier-Magisk, make sure files are in the root of the zip
|
||||||
|
cd $x;
|
||||||
|
zip -r ../../zipped_assets/$x-${VERSION}.zip .;
|
||||||
|
cd ..;
|
||||||
|
else
|
||||||
|
zip -r ../zipped_assets/$x-${VERSION}.zip $x;
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
- name: Release
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
with:
|
||||||
|
name: ${{ inputs.version }}
|
||||||
|
draft: true
|
||||||
|
files: |
|
||||||
|
./zipped_assets/*
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
tag_name: ${{ inputs.version }}
|
||||||
@@ -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:
|
||||||
@@ -47,11 +47,40 @@ jobs:
|
|||||||
|
|
||||||
- name: Setup system for test
|
- name: Setup system for test
|
||||||
run: |
|
run: |
|
||||||
|
sudo modprobe br_netfilter
|
||||||
sudo sysctl net.bridge.bridge-nf-call-iptables=0
|
sudo sysctl net.bridge.bridge-nf-call-iptables=0
|
||||||
sudo sysctl net.bridge.bridge-nf-call-ip6tables=0
|
sudo sysctl net.bridge.bridge-nf-call-ip6tables=0
|
||||||
sudo sysctl net.ipv6.conf.lo.disable_ipv6=0
|
sudo sysctl net.ipv6.conf.lo.disable_ipv6=0
|
||||||
sudo ip addr add 2001:db8::2/64 dev lo
|
sudo ip addr add 2001:db8::2/64 dev lo
|
||||||
|
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 21
|
||||||
|
|
||||||
|
- name: Install pnpm
|
||||||
|
uses: pnpm/action-setup@v3
|
||||||
|
with:
|
||||||
|
version: 9
|
||||||
|
run_install: false
|
||||||
|
|
||||||
|
- name: Get pnpm store directory
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Setup pnpm cache
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: ${{ env.STORE_PATH }}
|
||||||
|
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-pnpm-store-
|
||||||
|
|
||||||
|
- name: Install frontend dependencies
|
||||||
|
run: |
|
||||||
|
pnpm -r install
|
||||||
|
pnpm -r --filter "./easytier-web/*" build
|
||||||
|
|
||||||
- name: Cargo cache
|
- name: Cargo cache
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
@@ -62,6 +91,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: |
|
run: |
|
||||||
sudo -E env "PATH=$PATH" cargo test --no-default-features --features=full --verbose
|
sudo prlimit --pid $$ --nofile=1048576:1048576
|
||||||
|
sudo -E env "PATH=$PATH" cargo test --no-default-features --features=full --verbose -- --test-threads=1
|
||||||
sudo chown -R $USER:$USER ./target
|
sudo chown -R $USER:$USER ./target
|
||||||
sudo chown -R $USER:$USER ~/.cargo
|
sudo chown -R $USER:$USER ~/.cargo
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -1,7 +1,13 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
members = ["easytier", "easytier-gui/src-tauri"]
|
members = [
|
||||||
default-members = ["easytier"]
|
"easytier",
|
||||||
|
"easytier-gui/src-tauri",
|
||||||
|
"easytier-rpc-build",
|
||||||
|
"easytier-web",
|
||||||
|
"easytier-contrib/easytier-ffi",
|
||||||
|
]
|
||||||
|
default-members = ["easytier", "easytier-web"]
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
panic = "unwind"
|
panic = "unwind"
|
||||||
@@ -10,4 +16,5 @@ panic = "unwind"
|
|||||||
panic = "abort"
|
panic = "abort"
|
||||||
lto = true
|
lto = true
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
|
opt-level = 3
|
||||||
strip = true
|
strip = true
|
||||||
|
|||||||
@@ -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.experimental.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"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,73 +1,165 @@
|
|||||||
Apache License
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
Version 2.0, January 2004
|
Version 3, 29 June 2007
|
||||||
http://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
|
This version of the GNU Lesser General Public License incorporates
|
||||||
|
the terms and conditions of version 3 of the GNU General Public
|
||||||
|
License, supplemented by the additional permissions listed below.
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
|
0. Additional Definitions.
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
|
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||||
|
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||||
|
General Public License.
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
|
"The Library" refers to a covered work governed by this License,
|
||||||
|
other than an Application or a Combined Work as defined below.
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
|
An "Application" is any work that makes use of an interface provided
|
||||||
|
by the Library, but which is not otherwise based on the Library.
|
||||||
|
Defining a subclass of a class defined by the Library is deemed a mode
|
||||||
|
of using an interface provided by the Library.
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
|
A "Combined Work" is a work produced by combining or linking an
|
||||||
|
Application with the Library. The particular version of the Library
|
||||||
|
with which the Combined Work was made is also called the "Linked
|
||||||
|
Version".
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
|
The "Minimal Corresponding Source" for a Combined Work means the
|
||||||
|
Corresponding Source for the Combined Work, excluding any source code
|
||||||
|
for portions of the Combined Work that, considered in isolation, are
|
||||||
|
based on the Application, and not on the Linked Version.
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
|
The "Corresponding Application Code" for a Combined Work means the
|
||||||
|
object code and/or source code for the Application, including any data
|
||||||
|
and utility programs needed for reproducing the Combined Work from the
|
||||||
|
Application, but excluding the System Libraries of the Combined Work.
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
|
1. Exception to Section 3 of the GNU GPL.
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
|
You may convey a covered work under sections 3 and 4 of this License
|
||||||
|
without being bound by section 3 of the GNU GPL.
|
||||||
|
|
||||||
2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
|
2. Conveying Modified Versions.
|
||||||
|
|
||||||
3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
|
If you modify a copy of the Library, and, in your modifications, a
|
||||||
|
facility refers to a function or data to be supplied by an Application
|
||||||
|
that uses the facility (other than as an argument passed when the
|
||||||
|
facility is invoked), then you may convey a copy of the modified
|
||||||
|
version:
|
||||||
|
|
||||||
4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
|
a) under this License, provided that you make a good faith effort to
|
||||||
|
ensure that, in the event an Application does not supply the
|
||||||
|
function or data, the facility still operates, and performs
|
||||||
|
whatever part of its purpose remains meaningful, or
|
||||||
|
|
||||||
(a) You must give any other recipients of the Work or Derivative Works a copy of this License; and
|
b) under the GNU GPL, with none of the additional permissions of
|
||||||
|
this License applicable to that copy.
|
||||||
|
|
||||||
(b) You must cause any modified files to carry prominent notices stating that You changed the files; and
|
3. Object Code Incorporating Material from Library Header Files.
|
||||||
|
|
||||||
(c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
|
The object code form of an Application may incorporate material from
|
||||||
|
a header file that is part of the Library. You may convey such object
|
||||||
|
code under terms of your choice, provided that, if the incorporated
|
||||||
|
material is not limited to numerical parameters, data structure
|
||||||
|
layouts and accessors, or small macros, inline functions and templates
|
||||||
|
(ten or fewer lines in length), you do both of the following:
|
||||||
|
|
||||||
(d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
|
a) Give prominent notice with each copy of the object code that the
|
||||||
|
Library is used in it and that the Library and its use are
|
||||||
|
covered by this License.
|
||||||
|
|
||||||
You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
|
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||||
|
document.
|
||||||
|
|
||||||
5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
|
4. Combined Works.
|
||||||
|
|
||||||
6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
|
You may convey a Combined Work under terms of your choice that,
|
||||||
|
taken together, effectively do not restrict modification of the
|
||||||
|
portions of the Library contained in the Combined Work and reverse
|
||||||
|
engineering for debugging such modifications, if you also do each of
|
||||||
|
the following:
|
||||||
|
|
||||||
7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
|
a) Give prominent notice with each copy of the Combined Work that
|
||||||
|
the Library is used in it and that the Library and its use are
|
||||||
|
covered by this License.
|
||||||
|
|
||||||
8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
|
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||||
|
document.
|
||||||
|
|
||||||
9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
|
c) For a Combined Work that displays copyright notices during
|
||||||
|
execution, include the copyright notice for the Library among
|
||||||
|
these notices, as well as a reference directing the user to the
|
||||||
|
copies of the GNU GPL and this license document.
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
d) Do one of the following:
|
||||||
|
|
||||||
APPENDIX: How to apply the Apache License to your work.
|
0) Convey the Minimal Corresponding Source under the terms of this
|
||||||
|
License, and the Corresponding Application Code in a form
|
||||||
|
suitable for, and under terms that permit, the user to
|
||||||
|
recombine or relink the Application with a modified version of
|
||||||
|
the Linked Version to produce a modified Combined Work, in the
|
||||||
|
manner specified by section 6 of the GNU GPL for conveying
|
||||||
|
Corresponding Source.
|
||||||
|
|
||||||
To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives.
|
1) Use a suitable shared library mechanism for linking with the
|
||||||
|
Library. A suitable mechanism is one that (a) uses at run time
|
||||||
|
a copy of the Library already present on the user's computer
|
||||||
|
system, and (b) will operate properly with a modified version
|
||||||
|
of the Library that is interface-compatible with the Linked
|
||||||
|
Version.
|
||||||
|
|
||||||
Copyright 2023 sunsijie
|
e) Provide Installation Information, but only if you would otherwise
|
||||||
|
be required to provide such information under section 6 of the
|
||||||
|
GNU GPL, and only to the extent that such information is
|
||||||
|
necessary to install and execute a modified version of the
|
||||||
|
Combined Work produced by recombining or relinking the
|
||||||
|
Application with a modified version of the Linked Version. (If
|
||||||
|
you use option 4d0, the Installation Information must accompany
|
||||||
|
the Minimal Corresponding Source and Corresponding Application
|
||||||
|
Code. If you use option 4d1, you must provide the Installation
|
||||||
|
Information in the manner specified by section 6 of the GNU GPL
|
||||||
|
for conveying Corresponding Source.)
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
5. Combined Libraries.
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
You may place library facilities that are a work based on the
|
||||||
|
Library side by side in a single library together with other library
|
||||||
|
facilities that are not Applications and are not covered by this
|
||||||
|
License, and convey such a combined library under terms of your
|
||||||
|
choice, if you do both of the following:
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
a) Accompany the combined library with a copy of the same work based
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
on the Library, uncombined with any other library facilities,
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
conveyed under the terms of this License.
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
b) Give prominent notice with the combined library that part of it
|
||||||
|
is a work based on the Library, and explaining where to find the
|
||||||
|
accompanying uncombined form of the same work.
|
||||||
|
|
||||||
|
6. Revised Versions of the GNU Lesser General Public License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions
|
||||||
|
of the GNU Lesser General Public License from time to time. Such new
|
||||||
|
versions will be similar in spirit to the present version, but may
|
||||||
|
differ in detail to address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Library as you received it specifies that a certain numbered version
|
||||||
|
of the GNU Lesser General Public License "or any later version"
|
||||||
|
applies to it, you have the option of following the terms and
|
||||||
|
conditions either of that published version or of any later version
|
||||||
|
published by the Free Software Foundation. If the Library as you
|
||||||
|
received it does not specify a version number of the GNU Lesser
|
||||||
|
General Public License, you may choose any version of the GNU Lesser
|
||||||
|
General Public License ever published by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Library as you received it specifies that a proxy can decide
|
||||||
|
whether future versions of the GNU Lesser General Public License shall
|
||||||
|
apply, that proxy's public statement of acceptance of any version is
|
||||||
|
permanent authorization for you to choose that version for the
|
||||||
|
Library.
|
||||||
|
|||||||
@@ -1,28 +1,31 @@
|
|||||||
# EasyTier
|
# EasyTier
|
||||||
|
|
||||||
[](https://github.com/KKRainbow/EasyTier/blob/main/LICENSE)
|
[](https://github.com/EasyTier/EasyTier/releases)
|
||||||
[](https://github.com/KKRainbow/EasyTier/commits/main)
|
[](https://github.com/EasyTier/EasyTier/blob/main/LICENSE)
|
||||||
[](https://github.com/KKRainbow/EasyTier/issues)
|
[](https://github.com/EasyTier/EasyTier/commits/main)
|
||||||
[](https://github.com/EasyTier/EasyTier/actions/workflows/core.yml)
|
[](https://github.com/EasyTier/EasyTier/issues)
|
||||||
[](https://github.com/EasyTier/EasyTier/actions/workflows/gui.yml)
|
[](https://github.com/EasyTier/EasyTier/actions/workflows/core.yml)
|
||||||
|
[](https://github.com/EasyTier/EasyTier/actions/workflows/gui.yml)
|
||||||
|
[](https://github.com/EasyTier/EasyTier/actions/workflows/test.yml)
|
||||||
|
[](https://deepwiki.com/EasyTier/EasyTier)
|
||||||
|
|
||||||
[简体中文](/README_CN.md) | [English](/README.md)
|
[简体中文](/README_CN.md) | [English](/README.md)
|
||||||
|
|
||||||
**Please visit the [EasyTier Official Website](https://www.easytier.top/en/) to view the full documentation.**
|
**Please visit the [EasyTier Official Website](https://easytier.cn/en/) to view the full documentation.**
|
||||||
|
|
||||||
EasyTier is a simple, safe and decentralized VPN networking solution implemented with the Rust language and Tokio framework.
|
EasyTier is a simple, safe and decentralized VPN networking solution implemented with the Rust language and Tokio framework.
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="assets/image-5.png" width="300">
|
<img src="assets/image-5.png" width="300">
|
||||||
<img src="assets/image-4.png" width="300">
|
<img src="assets/image-4.png" width="300">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- **Decentralized**: No need to rely on centralized services, nodes are equal and independent.
|
- **Decentralized**: No need to rely on centralized services, nodes are equal and independent.
|
||||||
- **Safe**: Use WireGuard protocol to encrypt data.
|
- **Safe**: Use WireGuard protocol to encrypt data.
|
||||||
- **High Performance**: Full-link zero-copy, with performance comparable to mainstream networking software.
|
- **High Performance**: Full-link zero-copy, with performance comparable to mainstream networking software.
|
||||||
- **Cross-platform**: Supports MacOS/Linux/Windows, will support IOS and Android in the future. The executable file is statically linked, making deployment simple.
|
- **Cross-platform**: Supports MacOS/Linux/Windows/Android, will support IOS in the future. The executable file is statically linked, making deployment simple.
|
||||||
- **Networking without public IP**: Supports networking using shared public nodes, refer to [Configuration Guide](#Networking-without-public-IP)
|
- **Networking without public IP**: Supports networking using shared public nodes, refer to [Configuration Guide](#Networking-without-public-IP)
|
||||||
- **NAT traversal**: Supports UDP-based NAT traversal, able to establish stable connections even in complex network environments.
|
- **NAT traversal**: Supports UDP-based NAT traversal, able to establish stable connections even in complex network environments.
|
||||||
- **Subnet Proxy (Point-to-Network)**: Nodes can expose accessible network segments as proxies to the VPN subnet, allowing other nodes to access these subnets through the node.
|
- **Subnet Proxy (Point-to-Network)**: Nodes can expose accessible network segments as proxies to the VPN subnet, allowing other nodes to access these subnets through the node.
|
||||||
@@ -31,160 +34,226 @@
|
|||||||
- **High Availability**: Supports multi-path and switches to healthy paths when high packet loss or network errors are detected.
|
- **High Availability**: Supports multi-path and switches to healthy paths when high packet loss or network errors are detected.
|
||||||
- **IPv6 Support**: Supports networking using IPv6.
|
- **IPv6 Support**: Supports networking using IPv6.
|
||||||
- **Multiple Protocol Types**: Supports communication between nodes using protocols such as WebSocket and QUIC.
|
- **Multiple Protocol Types**: Supports communication between nodes using protocols such as WebSocket and QUIC.
|
||||||
|
- **Web Management Interface**: Provides a [web-based management](https://easytier.cn/web) interface for easy configuration and monitoring.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
1. **Download the precompiled binary file**
|
||||||
|
|
||||||
|
Visit the [GitHub Release page](https://github.com/EasyTier/EasyTier/releases) to download the binary file suitable for your operating system. Release includes both command-line programs and GUI programs in the compressed package.
|
||||||
|
|
||||||
|
2. **Install via crates.io**
|
||||||
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
1. **Download the precompiled binary file**
|
|
||||||
|
|
||||||
Visit the [GitHub Release page](https://github.com/KKRainbow/EasyTier/releases) to download the binary file suitable for your operating system. Release includes both command-line programs and GUI programs in the compressed package.
|
|
||||||
|
|
||||||
2. **Install via crates.io**
|
|
||||||
```sh
|
```sh
|
||||||
cargo install easytier
|
cargo install easytier
|
||||||
```
|
```
|
||||||
|
|
||||||
3. **Install from source code**
|
|
||||||
```sh
|
|
||||||
cargo install --git https://github.com/KKRainbow/EasyTier.git
|
|
||||||
```
|
|
||||||
|
|
||||||
## 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.
|
3. **Install from source code**
|
||||||
|
|
||||||
Make sure EasyTier is installed according to the [Installation Guide](#Installation), and both easytier-core and easytier-cli commands are available.
|
```sh
|
||||||
|
cargo install --git https://github.com/EasyTier/EasyTier.git easytier
|
||||||
### Two-node Networking
|
```
|
||||||
|
|
||||||
Assuming the network topology of the two nodes is as follows
|
4. **Install by Docker Compose**
|
||||||
|
|
||||||
```mermaid
|
Please visit the [EasyTier Official Website](https://easytier.cn/en/) to view the full documentation.
|
||||||
flowchart LR
|
|
||||||
|
5. **Install by script (For Linux Only)**
|
||||||
subgraph Node A IP 22.1.1.1
|
|
||||||
nodea[EasyTier\n10.144.144.1]
|
```sh
|
||||||
end
|
wget -O /tmp/easytier.sh "https://raw.githubusercontent.com/EasyTier/EasyTier/main/script/install.sh" && bash /tmp/easytier.sh install
|
||||||
|
```
|
||||||
subgraph Node B
|
|
||||||
nodeb[EasyTier\n10.144.144.2]
|
The script supports the following commands and options:
|
||||||
end
|
|
||||||
|
Commands:
|
||||||
nodea <-----> nodeb
|
- `install`: Install EasyTier
|
||||||
|
- `uninstall`: Uninstall EasyTier
|
||||||
```
|
- `update`: Update EasyTier to the latest version
|
||||||
|
- `help`: Show help message
|
||||||
1. Execute on Node A:
|
|
||||||
|
Options:
|
||||||
|
- `--skip-folder-verify`: Skip folder verification during installation
|
||||||
|
- `--skip-folder-fix`: Skip automatic folder path fixing
|
||||||
|
- `--no-gh-proxy`: Disable GitHub proxy
|
||||||
|
- `--gh-proxy`: Set custom GitHub proxy URL (default: https://ghfast.top/)
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
```sh
|
||||||
|
# Show help
|
||||||
|
bash /tmp/easytier.sh help
|
||||||
|
|
||||||
|
# Install with options
|
||||||
|
bash /tmp/easytier.sh install --skip-folder-verify
|
||||||
|
bash /tmp/easytier.sh install --no-gh-proxy
|
||||||
|
bash /tmp/easytier.sh install --gh-proxy https://your-proxy.com/
|
||||||
|
|
||||||
|
# Update EasyTier
|
||||||
|
bash /tmp/easytier.sh update
|
||||||
|
|
||||||
|
# Uninstall EasyTier
|
||||||
|
bash /tmp/easytier.sh uninstall
|
||||||
|
```
|
||||||
|
|
||||||
|
6. **Install by Homebrew (For MacOS Only)**
|
||||||
|
|
||||||
|
```sh
|
||||||
|
brew tap brewforge/chinese
|
||||||
|
brew install --cask easytier-gui
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
Make sure EasyTier is installed according to the [Installation Guide](#Installation), and both easytier-core and easytier-cli commands are available.
|
||||||
|
|
||||||
|
### Two-node Networking
|
||||||
|
|
||||||
|
Assuming the network topology of the two nodes is as follows
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
|
||||||
|
subgraph Node A IP 22.1.1.1
|
||||||
|
nodea[EasyTier\n10.144.144.1]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph Node B
|
||||||
|
nodeb[EasyTier\n10.144.144.2]
|
||||||
|
end
|
||||||
|
|
||||||
|
nodea <-----> nodeb
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Execute on Node A:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
sudo easytier-core --ipv4 10.144.144.1
|
sudo easytier-core --ipv4 10.144.144.1
|
||||||
```
|
```
|
||||||
|
|
||||||
Successful execution of the command will print the following.
|
Successful execution of the command will print the following.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
2. Execute on Node B
|
2. Execute on Node B
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
sudo easytier-core --ipv4 10.144.144.2 --peers udp://22.1.1.1:11010
|
sudo easytier-core --ipv4 10.144.144.2 --peers udp://22.1.1.1:11010
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Test Connectivity
|
3. Test Connectivity
|
||||||
|
|
||||||
The two nodes should connect successfully and be able to communicate within the virtual subnet
|
The two nodes should connect successfully and be able to communicate within the virtual subnet
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
ping 10.144.144.2
|
ping 10.144.144.2
|
||||||
```
|
```
|
||||||
|
|
||||||
Use easytier-cli to view node information in the subnet
|
Use easytier-cli to view node information in the subnet
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
easytier-cli peer
|
easytier-cli peer
|
||||||
```
|
```
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
easytier-cli route
|
easytier-cli route
|
||||||
```
|
```
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
|
```sh
|
||||||
|
easytier-cli node
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Multi-node Networking
|
### Multi-node Networking
|
||||||
|
|
||||||
Based on the two-node networking example just now, if more nodes need to join the virtual network, you can use the following command.
|
Based on the two-node networking example just now, if more nodes need to join the virtual network, you can use the following command.
|
||||||
|
|
||||||
```
|
```sh
|
||||||
sudo easytier-core --ipv4 10.144.144.2 --peers udp://22.1.1.1:11010
|
sudo easytier-core --ipv4 10.144.144.2 --peers udp://22.1.1.1:11010
|
||||||
```
|
```
|
||||||
|
|
||||||
The `--peers` parameter can fill in the listening address of any node already in the virtual network.
|
The `--peers` parameter can fill in the listening address of any node already in the virtual network.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Subnet Proxy (Point-to-Network) Configuration
|
### Subnet Proxy (Point-to-Network) Configuration
|
||||||
|
|
||||||
Assuming the network topology is as follows, Node B wants to share its accessible subnet 10.1.1.0/24 with other nodes.
|
Assuming the network topology is as follows, Node B wants to share its accessible subnet 10.1.1.0/24 with other nodes.
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart LR
|
flowchart LR
|
||||||
|
|
||||||
subgraph Node A IP 22.1.1.1
|
subgraph Node A IP 22.1.1.1
|
||||||
nodea[EasyTier\n10.144.144.1]
|
nodea[EasyTier\n10.144.144.1]
|
||||||
end
|
end
|
||||||
|
|
||||||
subgraph Node B
|
subgraph Node B
|
||||||
nodeb[EasyTier\n10.144.144.2]
|
nodeb[EasyTier\n10.144.144.2]
|
||||||
end
|
end
|
||||||
|
|
||||||
id1[[10.1.1.0/24]]
|
id1[[10.1.1.0/24]]
|
||||||
|
|
||||||
nodea <--> nodeb <-.-> id1
|
nodea <--> nodeb <-.-> id1
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Then the startup parameters for Node B's easytier are (new -n parameter)
|
Then the startup parameters for Node B's easytier are (new -n parameter)
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
sudo easytier-core --ipv4 10.144.144.2 -n 10.1.1.0/24
|
sudo easytier-core --ipv4 10.144.144.2 -n 10.1.1.0/24
|
||||||
```
|
```
|
||||||
|
|
||||||
Subnet proxy information will automatically sync to each node in the virtual network, and each node will automatically configure the corresponding route. Node A can check whether the subnet proxy is effective through the following command.
|
Subnet proxy information will automatically sync to each node in the virtual network, and each node will automatically configure the corresponding route. Node A can check whether the subnet proxy is effective through the following command.
|
||||||
|
|
||||||
1. Check whether the routing information has been synchronized, the proxy_cidrs column shows the proxied subnets.
|
1. Check whether the routing information has been synchronized, the proxy_cidrs column shows the proxied subnets.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
easytier-cli route
|
easytier-cli route
|
||||||
```
|
```
|
||||||

|
|
||||||
|

|
||||||
|
|
||||||
2. Test whether Node A can access nodes under the proxied subnet
|
2. Test whether Node A can access nodes under the proxied subnet
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
ping 10.1.1.2
|
ping 10.1.1.2
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Networking without Public IP
|
|
||||||
|
|
||||||
EasyTier supports networking using shared public nodes. The currently deployed shared public node is ``tcp://easytier.public.kkrainbow.top:11010``.
|
|
||||||
|
|
||||||
When using shared nodes, each node entering the network needs to provide the same ``--network-name`` and ``--network-secret`` parameters as the unique identifier of the network.
|
|
||||||
|
|
||||||
Taking two nodes as an example, Node A executes:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
sudo easytier-core -i 10.144.144.1 --network-name abc --network-secret abc -e tcp://easytier.public.kkrainbow.top:11010
|
|
||||||
```
|
|
||||||
|
|
||||||
Node B executes
|
|
||||||
|
|
||||||
```sh
|
|
||||||
sudo easytier-core --ipv4 10.144.144.2 --network-name abc --network-secret abc -e tcp://easytier.public.kkrainbow.top:11010
|
|
||||||
```
|
|
||||||
|
|
||||||
After the command is successfully executed, Node A can access Node B through the virtual IP 10.144.144.2.
|
|
||||||
|
|
||||||
### Use EasyTier with WireGuard Client
|
|
||||||
|
|
||||||
EasyTier can be used as a WireGuard server to allow any device with WireGuard client installed to access the EasyTier network. For platforms currently unsupported by EasyTier (such as iOS, Android, etc.), this method can be used to connect to the EasyTier network.
|
---
|
||||||
|
|
||||||
|
### Networking without Public IP
|
||||||
|
|
||||||
|
EasyTier supports networking using shared public nodes. The currently deployed shared public node is ``tcp://public.easytier.cn:11010``.
|
||||||
|
|
||||||
|
When using shared nodes, each node entering the network needs to provide the same ``--network-name`` and ``--network-secret`` parameters as the unique identifier of the network.
|
||||||
|
|
||||||
|
Taking two nodes as an example, Node A executes:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo easytier-core -i 10.144.144.1 --network-name abc --network-secret abc -p tcp://public.easytier.cn:11010
|
||||||
|
```
|
||||||
|
|
||||||
|
Node B executes
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo easytier-core --ipv4 10.144.144.2 --network-name abc --network-secret abc -p tcp://public.easytier.cn:11010
|
||||||
|
```
|
||||||
|
|
||||||
|
After the command is successfully executed, Node A can access Node B through the virtual IP 10.144.144.2.
|
||||||
|
|
||||||
|
### Use EasyTier with WireGuard Client
|
||||||
|
|
||||||
|
EasyTier can be used as a WireGuard server to allow any device with WireGuard client installed to access the EasyTier network. For platforms currently unsupported by EasyTier (such as iOS, Android, etc.), this method can be used to connect to the EasyTier network.
|
||||||
|
|
||||||
Assuming the network topology is as follows:
|
Assuming the network topology is as follows:
|
||||||
|
|
||||||
@@ -210,14 +279,14 @@ To enable an iPhone to access the EasyTier network through Node A, the following
|
|||||||
|
|
||||||
Include the --vpn-portal parameter in the easytier-core command on Node A to specify the port that the WireGuard service listens on and the subnet used by the WireGuard network.
|
Include the --vpn-portal parameter in the easytier-core command on Node A to specify the port that the WireGuard service listens on and the subnet used by the WireGuard network.
|
||||||
|
|
||||||
```
|
```sh
|
||||||
# The following parameters mean: listen on port 0.0.0.0:11013, and use the 10.14.14.0/24 subnet for WireGuard
|
# The following parameters mean: listen on port 0.0.0.0:11013, and use the 10.14.14.0/24 subnet for WireGuard
|
||||||
sudo easytier-core --ipv4 10.144.144.1 --vpn-portal wg://0.0.0.0:11013/10.14.14.0/24
|
sudo easytier-core --ipv4 10.144.144.1 --vpn-portal wg://0.0.0.0:11013/10.14.14.0/24
|
||||||
```
|
```
|
||||||
|
|
||||||
After successfully starting easytier-core, use easytier-cli to obtain the WireGuard client configuration.
|
After successfully starting easytier-core, use easytier-cli to obtain the WireGuard client configuration.
|
||||||
|
|
||||||
```
|
```sh
|
||||||
$> easytier-cli vpn-portal
|
$> easytier-cli vpn-portal
|
||||||
portal_name: wireguard
|
portal_name: wireguard
|
||||||
|
|
||||||
@@ -240,37 +309,52 @@ connected_clients:
|
|||||||
```
|
```
|
||||||
|
|
||||||
Before using the Client Config, you need to modify the Interface Address and Peer Endpoint to the client's IP and the IP of the EasyTier node, respectively. Import the configuration file into the WireGuard client to access the EasyTier network.
|
Before using the Client Config, you need to modify the Interface Address and Peer Endpoint to the client's IP and the IP of the EasyTier node, respectively. Import the configuration file into the WireGuard client to access the EasyTier network.
|
||||||
|
|
||||||
### Configurations
|
### Self-Hosted Public Server
|
||||||
|
|
||||||
You can use ``easytier-core --help`` to view all configuration items
|
Every virtual network (with same network name and secret) can act as a public server cluster. Nodes of other network can connect to arbitrary nodes in public server cluster to discover each other without public IP.
|
||||||
|
|
||||||
|
Run you own public server cluster is exactly same as running an virtual network, except that you can skip config the ipv4 addr.
|
||||||
# Roadmap
|
|
||||||
|
You can also join the official public server cluster with following command:
|
||||||
- [ ] Improve documentation and user guides.
|
|
||||||
- [ ] Support features such as encryption, TCP hole punching, etc.
|
```
|
||||||
- [ ] Support Android, IOS and other mobile platforms.
|
sudo easytier-core --network-name easytier --network-secret easytier -p tcp://public.easytier.cn:11010
|
||||||
- [ ] Support Web configuration management.
|
```
|
||||||
|
|
||||||
# Community and Contribution
|
|
||||||
|
### Configurations
|
||||||
We welcome and encourage community contributions! If you want to get involved, please submit a [GitHub PR](https://github.com/KKRainbow/EasyTier/pulls). Detailed contribution guidelines can be found in [CONTRIBUTING.md](https://github.com/KKRainbow/EasyTier/blob/main/CONTRIBUTING.md).
|
|
||||||
|
You can use ``easytier-core --help`` to view all configuration items
|
||||||
# Related Projects and Resources
|
|
||||||
|
## Roadmap
|
||||||
- [ZeroTier](https://www.zerotier.com/): A global virtual network for connecting devices.
|
|
||||||
- [TailScale](https://tailscale.com/): A VPN solution aimed at simplifying network configuration.
|
- [ ] Support features such TCP hole punching, KCP, FEC etc.
|
||||||
- [vpncloud](https://github.com/dswd/vpncloud): A P2P Mesh VPN
|
- [ ] Support iOS.
|
||||||
- [Candy](https://github.com/lanthora/candy): A reliable, low-latency, and anti-censorship virtual private network
|
|
||||||
|
## Community and Contribution
|
||||||
# License
|
|
||||||
|
We welcome and encourage community contributions! If you want to get involved, please submit a [GitHub PR](https://github.com/EasyTier/EasyTier/pulls). Detailed contribution guidelines can be found in [CONTRIBUTING.md](https://github.com/EasyTier/EasyTier/blob/main/CONTRIBUTING.md).
|
||||||
EasyTier is released under the [Apache License 2.0](https://github.com/KKRainbow/EasyTier/blob/main/LICENSE).
|
|
||||||
|
## Related Projects and Resources
|
||||||
# Contact
|
|
||||||
|
- [ZeroTier](https://www.zerotier.com/): A global virtual network for connecting devices.
|
||||||
- Ask questions or report problems: [GitHub Issues](https://github.com/KKRainbow/EasyTier/issues)
|
- [TailScale](https://tailscale.com/): A VPN solution aimed at simplifying network configuration.
|
||||||
- Discussion and exchange: [GitHub Discussions](https://github.com/KKRainbow/EasyTier/discussions)
|
- [vpncloud](https://github.com/dswd/vpncloud): A P2P Mesh VPN
|
||||||
- Telegram:https://t.me/easytier
|
- [Candy](https://github.com/lanthora/candy): A reliable, low-latency, and anti-censorship virtual private network
|
||||||
- QQ Group: 949700262
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
EasyTier is released under the [Apache License 2.0](https://github.com/EasyTier/EasyTier/blob/main/LICENSE).
|
||||||
|
|
||||||
|
## Contact
|
||||||
|
|
||||||
|
- Ask questions or report problems: [GitHub Issues](https://github.com/EasyTier/EasyTier/issues)
|
||||||
|
- Discussion and exchange: [GitHub Discussions](https://github.com/EasyTier/EasyTier/discussions)
|
||||||
|
- Telegram:https://t.me/easytier
|
||||||
|
- QQ Group: 949700262
|
||||||
|
|
||||||
|
## Sponsor
|
||||||
|
|
||||||
|
<img src="assets/image-8.png" width="300">
|
||||||
|
<img src="assets/image-9.png" width="300">
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
# EasyTier
|
# EasyTier
|
||||||
|
|
||||||
[](https://github.com/KKRainbow/EasyTier/blob/main/LICENSE)
|
[](https://github.com/EasyTier/EasyTier/blob/main/LICENSE)
|
||||||
[](https://github.com/KKRainbow/EasyTier/commits/main)
|
[](https://github.com/EasyTier/EasyTier/commits/main)
|
||||||
[](https://github.com/KKRainbow/EasyTier/issues)
|
[](https://github.com/EasyTier/EasyTier/issues)
|
||||||
[](https://github.com/EasyTier/EasyTier/actions/workflows/core.yml)
|
[](https://github.com/EasyTier/EasyTier/actions/workflows/core.yml)
|
||||||
[](https://github.com/EasyTier/EasyTier/actions/workflows/gui.yml)
|
[](https://github.com/EasyTier/EasyTier/actions/workflows/gui.yml)
|
||||||
|
|
||||||
[简体中文](/README_CN.md) | [English](/README.md)
|
[简体中文](/README_CN.md) | [English](/README.md)
|
||||||
|
|
||||||
**请访问 [EasyTier 官网](https://www.easytier.top/) 以查看完整的文档。**
|
**请访问 [EasyTier 官网](https://easytier.cn/) 以查看完整的文档。**
|
||||||
|
|
||||||
一个简单、安全、去中心化的内网穿透 VPN 组网方案,使用 Rust 语言和 Tokio 框架实现。
|
一个简单、安全、去中心化的内网穿透 VPN 组网方案,使用 Rust 语言和 Tokio 框架实现。
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
- **去中心化**:无需依赖中心化服务,节点平等且独立。
|
- **去中心化**:无需依赖中心化服务,节点平等且独立。
|
||||||
- **安全**:支持利用 WireGuard 加密通信,也支持 AES-GCM 加密保护中转流量。
|
- **安全**:支持利用 WireGuard 加密通信,也支持 AES-GCM 加密保护中转流量。
|
||||||
- **高性能**:全链路零拷贝,性能与主流组网软件相当。
|
- **高性能**:全链路零拷贝,性能与主流组网软件相当。
|
||||||
- **跨平台**:支持 MacOS/Linux/Windows,未来将支持 IOS 和 Android。可执行文件静态链接,部署简单。
|
- **跨平台**:支持 MacOS/Linux/Windows/Android,未来将支持 IOS。可执行文件静态链接,部署简单。
|
||||||
- **无公网 IP 组网**:支持利用共享的公网节点组网,可参考 [配置指南](#无公网IP组网)
|
- **无公网 IP 组网**:支持利用共享的公网节点组网,可参考 [配置指南](#无公网IP组网)
|
||||||
- **NAT 穿透**:支持基于 UDP 的 NAT 穿透,即使在复杂的网络环境下也能建立稳定的连接。
|
- **NAT 穿透**:支持基于 UDP 的 NAT 穿透,即使在复杂的网络环境下也能建立稳定的连接。
|
||||||
- **子网代理(点对网)**:节点可以将可访问的网段作为代理暴露给 VPN 子网,允许其他节点通过该节点访问这些子网。
|
- **子网代理(点对网)**:节点可以将可访问的网段作为代理暴露给 VPN 子网,允许其他节点通过该节点访问这些子网。
|
||||||
@@ -31,23 +31,73 @@
|
|||||||
- **高可用性**:支持多路径和在检测到高丢包率或网络错误时切换到健康路径。
|
- **高可用性**:支持多路径和在检测到高丢包率或网络错误时切换到健康路径。
|
||||||
- **IPV6 支持**:支持利用 IPV6 组网。
|
- **IPV6 支持**:支持利用 IPV6 组网。
|
||||||
- **多协议类型**: 支持使用 WebSocket、QUIC 等协议进行节点间通信。
|
- **多协议类型**: 支持使用 WebSocket、QUIC 等协议进行节点间通信。
|
||||||
|
- **Web 管理界面**:支持通过 [Web 界面](https://easytier.cn)管理节点。
|
||||||
|
|
||||||
## 安装
|
## 安装
|
||||||
|
|
||||||
1. **下载预编译的二进制文件**
|
1. **下载预编译的二进制文件**
|
||||||
|
|
||||||
访问 [GitHub Release 页面](https://github.com/KKRainbow/EasyTier/releases) 下载适用于您操作系统的二进制文件。Release 压缩包中同时包含命令行程序和图形界面程序。
|
访问 [GitHub Release 页面](https://github.com/EasyTier/EasyTier/releases) 下载适用于您操作系统的二进制文件。Release 压缩包中同时包含命令行程序和图形界面程序。
|
||||||
|
|
||||||
2. **通过 crates.io 安装**
|
2. **通过 crates.io 安装**
|
||||||
```sh
|
|
||||||
cargo install easytier
|
|
||||||
```
|
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cargo install easytier
|
||||||
|
```
|
||||||
|
|
||||||
3. **通过源码安装**
|
3. **通过源码安装**
|
||||||
```sh
|
|
||||||
cargo install --git https://github.com/KKRainbow/EasyTier.git
|
```sh
|
||||||
```
|
cargo install --git https://github.com/EasyTier/EasyTier.git easytier
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **通过Docker Compose安装**
|
||||||
|
|
||||||
|
请访问 [EasyTier 官网](https://easytier.cn/) 以查看完整的文档。
|
||||||
|
|
||||||
|
5. **使用一键脚本安装 (仅适用于 Linux)**
|
||||||
|
|
||||||
|
```sh
|
||||||
|
wget -O /tmp/easytier.sh "https://raw.githubusercontent.com/EasyTier/EasyTier/main/script/install.sh" && bash /tmp/easytier.sh install
|
||||||
|
```
|
||||||
|
|
||||||
|
脚本支持以下命令和选项:
|
||||||
|
|
||||||
|
命令:
|
||||||
|
- `install`: 安装 EasyTier
|
||||||
|
- `uninstall`: 卸载 EasyTier
|
||||||
|
- `update`: 更新 EasyTier 到最新版本
|
||||||
|
- `help`: 显示帮助信息
|
||||||
|
|
||||||
|
选项:
|
||||||
|
- `--skip-folder-verify`: 跳过安装过程中的文件夹验证
|
||||||
|
- `--skip-folder-fix`: 跳过自动修复文件夹路径
|
||||||
|
- `--no-gh-proxy`: 禁用 GitHub 代理
|
||||||
|
- `--gh-proxy`: 设置自定义 GitHub 代理 URL (默认值: https://ghfast.top/)
|
||||||
|
|
||||||
|
示例:
|
||||||
|
```sh
|
||||||
|
# 查看帮助
|
||||||
|
bash /tmp/easytier.sh help
|
||||||
|
|
||||||
|
# 安装(带选项)
|
||||||
|
bash /tmp/easytier.sh install --skip-folder-verify
|
||||||
|
bash /tmp/easytier.sh install --no-gh-proxy
|
||||||
|
bash /tmp/easytier.sh install --gh-proxy https://your-proxy.com/
|
||||||
|
|
||||||
|
# 更新 EasyTier
|
||||||
|
bash /tmp/easytier.sh update
|
||||||
|
|
||||||
|
# 卸载 EasyTier
|
||||||
|
bash /tmp/easytier.sh uninstall
|
||||||
|
```
|
||||||
|
|
||||||
|
6. **使用 Homebrew 安装 (仅适用于 MacOS)**
|
||||||
|
|
||||||
|
```sh
|
||||||
|
brew tap brewforge/chinese
|
||||||
|
brew install --cask easytier-gui
|
||||||
|
```
|
||||||
|
|
||||||
## 快速开始
|
## 快速开始
|
||||||
|
|
||||||
@@ -75,34 +125,48 @@ nodea <-----> nodeb
|
|||||||
```
|
```
|
||||||
|
|
||||||
1. 在节点 A 上执行:
|
1. 在节点 A 上执行:
|
||||||
```sh
|
|
||||||
sudo easytier-core --ipv4 10.144.144.1
|
|
||||||
```
|
|
||||||
命令执行成功会有如下打印。
|
|
||||||
|
|
||||||

|
```sh
|
||||||
|
sudo easytier-core --ipv4 10.144.144.1
|
||||||
|
```
|
||||||
|
|
||||||
|
命令执行成功会有如下打印。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
2. 在节点 B 执行
|
2. 在节点 B 执行
|
||||||
```sh
|
|
||||||
sudo easytier-core --ipv4 10.144.144.2 --peers udp://22.1.1.1:11010
|
```sh
|
||||||
```
|
sudo easytier-core --ipv4 10.144.144.2 --peers udp://22.1.1.1:11010
|
||||||
|
```
|
||||||
|
|
||||||
3. 测试联通性
|
3. 测试联通性
|
||||||
|
|
||||||
两个节点应成功连接并能够在虚拟子网内通信
|
两个节点应成功连接并能够在虚拟子网内通信
|
||||||
```sh
|
|
||||||
ping 10.144.144.2
|
|
||||||
```
|
|
||||||
|
|
||||||
使用 easytier-cli 查看子网中的节点信息
|
```sh
|
||||||
```sh
|
ping 10.144.144.2
|
||||||
easytier-cli peer
|
```
|
||||||
```
|
|
||||||

|
使用 easytier-cli 查看子网中的节点信息
|
||||||
```sh
|
|
||||||
easytier-cli route
|
```sh
|
||||||
```
|
easytier-cli peer
|
||||||

|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
```sh
|
||||||
|
easytier-cli route
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
```sh
|
||||||
|
easytier-cli node
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -110,11 +174,11 @@ nodea <-----> nodeb
|
|||||||
|
|
||||||
基于刚才的双节点组网例子,如果有更多的节点需要加入虚拟网络,可以使用如下命令。
|
基于刚才的双节点组网例子,如果有更多的节点需要加入虚拟网络,可以使用如下命令。
|
||||||
|
|
||||||
```
|
```sh
|
||||||
sudo easytier-core --ipv4 10.144.144.2 --peers udp://22.1.1.1:11010
|
sudo easytier-core --ipv4 10.144.144.2 --peers udp://22.1.1.1:11010
|
||||||
```
|
```
|
||||||
|
|
||||||
其中 `--peers ` 参数可以填写任意一个已经在虚拟网络中的节点的监听地址。
|
其中 `--peers` 参数可以填写任意一个已经在虚拟网络中的节点的监听地址。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -149,35 +213,36 @@ sudo easytier-core --ipv4 10.144.144.2 -n 10.1.1.0/24
|
|||||||
|
|
||||||
1. 检查路由信息是否已经同步,proxy_cidrs 列展示了被代理的子网。
|
1. 检查路由信息是否已经同步,proxy_cidrs 列展示了被代理的子网。
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
easytier-cli route
|
easytier-cli route
|
||||||
```
|
```
|
||||||

|
|
||||||
|

|
||||||
|
|
||||||
2. 测试节点 A 是否可访问被代理子网下的节点
|
2. 测试节点 A 是否可访问被代理子网下的节点
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
ping 10.1.1.2
|
ping 10.1.1.2
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 无公网IP组网
|
### 无公网IP组网
|
||||||
|
|
||||||
EasyTier 支持共享公网节点进行组网。目前已部署共享的公网节点 ``tcp://easytier.public.kkrainbow.top:11010``。
|
EasyTier 支持共享公网节点进行组网。目前已部署共享的公网节点 ``tcp://public.easytier.cn:11010``。
|
||||||
|
|
||||||
使用共享节点时,需要每个入网节点提供相同的 ``--network-name`` 和 ``--network-secret`` 参数,作为网络的唯一标识。
|
使用共享节点时,需要每个入网节点提供相同的 ``--network-name`` 和 ``--network-secret`` 参数,作为网络的唯一标识。
|
||||||
|
|
||||||
以双节点为例,节点 A 执行:
|
以双节点为例,节点 A 执行:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
sudo easytier-core -i 10.144.144.1 --network-name abc --network-secret abc -e tcp://easytier.public.kkrainbow.top:11010
|
sudo easytier-core -i 10.144.144.1 --network-name abc --network-secret abc -p tcp://public.easytier.cn:11010
|
||||||
```
|
```
|
||||||
|
|
||||||
节点 B 执行
|
节点 B 执行
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
sudo easytier-core --ipv4 10.144.144.2 --network-name abc --network-secret abc -e tcp://easytier.public.kkrainbow.top:11010
|
sudo easytier-core --ipv4 10.144.144.2 --network-name abc --network-secret abc -p tcp://public.easytier.cn:11010
|
||||||
```
|
```
|
||||||
|
|
||||||
命令执行成功后,节点 A 即可通过虚拟 IP 10.144.144.2 访问节点 B。
|
命令执行成功后,节点 A 即可通过虚拟 IP 10.144.144.2 访问节点 B。
|
||||||
@@ -212,14 +277,14 @@ ios <-.-> nodea <--> nodeb <-.-> id1
|
|||||||
|
|
||||||
在节点 A 的 easytier-core 命令中,加入 --vpn-portal 参数,指定 WireGuard 服务监听的端口,以及 WireGuard 网络使用的网段。
|
在节点 A 的 easytier-core 命令中,加入 --vpn-portal 参数,指定 WireGuard 服务监听的端口,以及 WireGuard 网络使用的网段。
|
||||||
|
|
||||||
```
|
```sh
|
||||||
# 以下参数的含义为: 监听 0.0.0.0:11013 端口,WireGuard 使用 10.14.14.0/24 网段
|
# 以下参数的含义为: 监听 0.0.0.0:11013 端口,WireGuard 使用 10.14.14.0/24 网段
|
||||||
sudo easytier-core --ipv4 10.144.144.1 --vpn-portal wg://0.0.0.0:11013/10.14.14.0/24
|
sudo easytier-core --ipv4 10.144.144.1 --vpn-portal wg://0.0.0.0:11013/10.14.14.0/24
|
||||||
```
|
```
|
||||||
|
|
||||||
easytier-core 启动成功后,使用 easytier-cli 获取 WireGuard Client 的配置。
|
easytier-core 启动成功后,使用 easytier-cli 获取 WireGuard Client 的配置。
|
||||||
|
|
||||||
```
|
```sh
|
||||||
$> easytier-cli vpn-portal
|
$> easytier-cli vpn-portal
|
||||||
portal_name: wireguard
|
portal_name: wireguard
|
||||||
|
|
||||||
@@ -245,36 +310,51 @@ connected_clients:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### 自建公共中转服务器
|
||||||
|
|
||||||
|
每个虚拟网络(通过相同的网络名称和密钥建链)都可以充当公共服务器集群。其他网络的节点可以连接到公共服务器集群中的任意节点,无需公共 IP 即可发现彼此。
|
||||||
|
|
||||||
|
运行自建的公共服务器集群与运行虚拟网络完全相同,不过可以跳过配置 ipv4 地址。
|
||||||
|
|
||||||
|
也可以使用以下命令加入官方公共服务器集群,后续将实现公共服务器集群的节点间负载均衡:
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo easytier-core --network-name easytier --network-secret easytier -p tcp://public.easytier.cn:11010
|
||||||
|
```
|
||||||
|
|
||||||
### 其他配置
|
### 其他配置
|
||||||
|
|
||||||
可使用 ``easytier-core --help`` 查看全部配置项
|
可使用 ``easytier-core --help`` 查看全部配置项
|
||||||
|
|
||||||
|
## 路线图
|
||||||
# 路线图
|
|
||||||
|
|
||||||
- [ ] 完善文档和用户指南。
|
- [ ] 完善文档和用户指南。
|
||||||
- [ ] 支持 TCP 打洞等特性。
|
- [ ] 支持 TCP 打洞、KCP、FEC 等特性。
|
||||||
- [ ] 支持 Android、IOS 等移动平台。
|
- [ ] 支持 iOS。
|
||||||
- [ ] 支持 Web 配置管理。
|
|
||||||
|
|
||||||
# 社区和贡献
|
## 社区和贡献
|
||||||
|
|
||||||
我们欢迎并鼓励社区贡献!如果你想参与进来,请提交 [GitHub PR](https://github.com/KKRainbow/EasyTier/pulls)。详细的贡献指南可以在 [CONTRIBUTING.md](https://github.com/KKRainbow/EasyTier/blob/main/CONTRIBUTING.md) 中找到。
|
我们欢迎并鼓励社区贡献!如果你想参与进来,请提交 [GitHub PR](https://github.com/EasyTier/EasyTier/pulls)。详细的贡献指南可以在 [CONTRIBUTING.md](https://github.com/EasyTier/EasyTier/blob/main/CONTRIBUTING.md) 中找到。
|
||||||
|
|
||||||
# 相关项目和资源
|
## 相关项目和资源
|
||||||
|
|
||||||
- [ZeroTier](https://www.zerotier.com/): 一个全球虚拟网络,用于连接设备。
|
- [ZeroTier](https://www.zerotier.com/): 一个全球虚拟网络,用于连接设备。
|
||||||
- [TailScale](https://tailscale.com/): 一个旨在简化网络配置的 VPN 解决方案。
|
- [TailScale](https://tailscale.com/): 一个旨在简化网络配置的 VPN 解决方案。
|
||||||
- [vpncloud](https://github.com/dswd/vpncloud): 一个 P2P Mesh VPN
|
- [vpncloud](https://github.com/dswd/vpncloud): 一个 P2P Mesh VPN
|
||||||
- [Candy](https://github.com/lanthora/candy): 可靠、低延迟、抗审查的虚拟专用网络
|
- [Candy](https://github.com/lanthora/candy): 可靠、低延迟、抗审查的虚拟专用网络
|
||||||
|
|
||||||
# 许可证
|
## 许可证
|
||||||
|
|
||||||
EasyTier 根据 [Apache License 2.0](https://github.com/KKRainbow/EasyTier/blob/main/LICENSE) 许可证发布。
|
EasyTier 根据 [Apache License 2.0](https://github.com/EasyTier/EasyTier/blob/main/LICENSE) 许可证发布。
|
||||||
|
|
||||||
# 联系方式
|
## 联系方式
|
||||||
|
|
||||||
- 提问或报告问题:[GitHub Issues](https://github.com/KKRainbow/EasyTier/issues)
|
- 提问或报告问题:[GitHub Issues](https://github.com/EasyTier/EasyTier/issues)
|
||||||
- 讨论和交流:[GitHub Discussions](https://github.com/KKRainbow/EasyTier/discussions)
|
- 讨论和交流:[GitHub Discussions](https://github.com/EasyTier/EasyTier/discussions)
|
||||||
- QQ 群: 949700262
|
- QQ 群: 949700262
|
||||||
- Telegram:https://t.me/easytier
|
- Telegram:https://t.me/easytier
|
||||||
|
|
||||||
|
## 赞助
|
||||||
|
|
||||||
|
<img src="assets/image-8.png" width="300">
|
||||||
|
<img src="assets/image-9.png" width="300">
|
||||||
|
|||||||
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 106 KiB |
|
After Width: | Height: | Size: 218 KiB |
@@ -0,0 +1,17 @@
|
|||||||
|
[package]
|
||||||
|
name = "easytier-ffi"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
easytier = { path = "../../easytier" }
|
||||||
|
|
||||||
|
once_cell = "1.18.0"
|
||||||
|
dashmap = "6.0"
|
||||||
|
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
serde_json = "1"
|
||||||
|
uuid = "1.17.0"
|
||||||
@@ -0,0 +1,159 @@
|
|||||||
|
public class EasyTierFFI
|
||||||
|
{
|
||||||
|
// 导入 DLL 函数
|
||||||
|
private const string DllName = "easytier_ffi.dll";
|
||||||
|
|
||||||
|
[DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
private static extern int parse_config([MarshalAs(UnmanagedType.LPStr)] string cfgStr);
|
||||||
|
|
||||||
|
[DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
private static extern int run_network_instance([MarshalAs(UnmanagedType.LPStr)] string cfgStr);
|
||||||
|
|
||||||
|
[DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
private static extern int retain_network_instance(IntPtr instNames, int length);
|
||||||
|
|
||||||
|
[DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
private static extern int collect_network_infos(IntPtr infos, int maxLength);
|
||||||
|
|
||||||
|
[DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
private static extern void get_error_msg(out IntPtr errorMsg);
|
||||||
|
|
||||||
|
[DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
private static extern void free_string(IntPtr str);
|
||||||
|
|
||||||
|
// 定义 KeyValuePair 结构体
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct KeyValuePair
|
||||||
|
{
|
||||||
|
public IntPtr Key;
|
||||||
|
public IntPtr Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析配置
|
||||||
|
public static void ParseConfig(string config)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(config))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Configuration string cannot be null or empty.");
|
||||||
|
}
|
||||||
|
|
||||||
|
int result = parse_config(config);
|
||||||
|
if (result < 0)
|
||||||
|
{
|
||||||
|
throw new Exception(GetErrorMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启动网络实例
|
||||||
|
public static void RunNetworkInstance(string config)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(config))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Configuration string cannot be null or empty.");
|
||||||
|
}
|
||||||
|
|
||||||
|
int result = run_network_instance(config);
|
||||||
|
if (result < 0)
|
||||||
|
{
|
||||||
|
throw new Exception(GetErrorMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保留网络实例
|
||||||
|
public static void RetainNetworkInstances(string[] instanceNames)
|
||||||
|
{
|
||||||
|
IntPtr[] namePointers = null;
|
||||||
|
IntPtr namesPtr = IntPtr.Zero;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (instanceNames != null && instanceNames.Length > 0)
|
||||||
|
{
|
||||||
|
namePointers = new IntPtr[instanceNames.Length];
|
||||||
|
for (int i = 0; i < instanceNames.Length; i++)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(instanceNames[i]))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Instance name cannot be null or empty.");
|
||||||
|
}
|
||||||
|
namePointers[i] = Marshal.StringToHGlobalAnsi(instanceNames[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
namesPtr = Marshal.AllocHGlobal(Marshal.SizeOf<IntPtr>() * namePointers.Length);
|
||||||
|
Marshal.Copy(namePointers, 0, namesPtr, namePointers.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
int result = retain_network_instance(namesPtr, instanceNames?.Length ?? 0);
|
||||||
|
if (result < 0)
|
||||||
|
{
|
||||||
|
throw new Exception(GetErrorMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (namePointers != null)
|
||||||
|
{
|
||||||
|
foreach (var ptr in namePointers)
|
||||||
|
{
|
||||||
|
if (ptr != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
Marshal.FreeHGlobal(ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (namesPtr != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
Marshal.FreeHGlobal(namesPtr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 收集网络信息
|
||||||
|
public static KeyValuePair<string, string>[] CollectNetworkInfos(int maxLength)
|
||||||
|
{
|
||||||
|
IntPtr buffer = Marshal.AllocHGlobal(Marshal.SizeOf<KeyValuePair>() * maxLength);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int count = collect_network_infos(buffer, maxLength);
|
||||||
|
if (count < 0)
|
||||||
|
{
|
||||||
|
throw new Exception(GetErrorMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = new KeyValuePair<string, string>[count];
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
var kv = Marshal.PtrToStructure<KeyValuePair>(buffer + i * Marshal.SizeOf<KeyValuePair>());
|
||||||
|
string key = Marshal.PtrToStringAnsi(kv.Key);
|
||||||
|
string value = Marshal.PtrToStringAnsi(kv.Value);
|
||||||
|
|
||||||
|
// 释放由 FFI 分配的字符串内存
|
||||||
|
free_string(kv.Key);
|
||||||
|
free_string(kv.Value);
|
||||||
|
|
||||||
|
result[i] = new KeyValuePair<string, string>(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Marshal.FreeHGlobal(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取错误信息
|
||||||
|
private static string GetErrorMessage()
|
||||||
|
{
|
||||||
|
get_error_msg(out IntPtr errorMsgPtr);
|
||||||
|
if (errorMsgPtr == IntPtr.Zero)
|
||||||
|
{
|
||||||
|
return "Unknown error";
|
||||||
|
}
|
||||||
|
|
||||||
|
string errorMsg = Marshal.PtrToStringAnsi(errorMsgPtr);
|
||||||
|
free_string(errorMsgPtr); // 释放错误信息字符串
|
||||||
|
return errorMsg;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,224 @@
|
|||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
use dashmap::DashMap;
|
||||||
|
use easytier::{
|
||||||
|
common::config::{ConfigLoader as _, TomlConfigLoader},
|
||||||
|
instance_manager::NetworkInstanceManager,
|
||||||
|
launcher::ConfigSource,
|
||||||
|
};
|
||||||
|
|
||||||
|
static INSTANCE_NAME_ID_MAP: once_cell::sync::Lazy<DashMap<String, uuid::Uuid>> =
|
||||||
|
once_cell::sync::Lazy::new(DashMap::new);
|
||||||
|
static INSTANCE_MANAGER: once_cell::sync::Lazy<NetworkInstanceManager> =
|
||||||
|
once_cell::sync::Lazy::new(NetworkInstanceManager::new);
|
||||||
|
|
||||||
|
static ERROR_MSG: once_cell::sync::Lazy<Mutex<Vec<u8>>> =
|
||||||
|
once_cell::sync::Lazy::new(|| Mutex::new(Vec::new()));
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct KeyValuePair {
|
||||||
|
pub key: *const std::ffi::c_char,
|
||||||
|
pub value: *const std::ffi::c_char,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_error_msg(msg: &str) {
|
||||||
|
let bytes = msg.as_bytes();
|
||||||
|
let mut msg_buf = ERROR_MSG.lock().unwrap();
|
||||||
|
let len = bytes.len();
|
||||||
|
msg_buf.resize(len, 0);
|
||||||
|
msg_buf[..len].copy_from_slice(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn get_error_msg(out: *mut *const std::ffi::c_char) {
|
||||||
|
let msg_buf = ERROR_MSG.lock().unwrap();
|
||||||
|
if msg_buf.is_empty() {
|
||||||
|
unsafe {
|
||||||
|
*out = std::ptr::null();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let cstr = std::ffi::CString::new(&msg_buf[..]).unwrap();
|
||||||
|
unsafe {
|
||||||
|
*out = cstr.into_raw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn free_string(s: *const std::ffi::c_char) {
|
||||||
|
if s.is_null() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
let _ = std::ffi::CString::from_raw(s as *mut std::ffi::c_char);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn parse_config(cfg_str: *const std::ffi::c_char) -> std::ffi::c_int {
|
||||||
|
let cfg_str = unsafe {
|
||||||
|
assert!(!cfg_str.is_null());
|
||||||
|
std::ffi::CStr::from_ptr(cfg_str)
|
||||||
|
.to_string_lossy()
|
||||||
|
.into_owned()
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(e) = TomlConfigLoader::new_from_str(&cfg_str) {
|
||||||
|
set_error_msg(&format!("failed to parse config: {:?}", e));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn run_network_instance(cfg_str: *const std::ffi::c_char) -> std::ffi::c_int {
|
||||||
|
let cfg_str = unsafe {
|
||||||
|
assert!(!cfg_str.is_null());
|
||||||
|
std::ffi::CStr::from_ptr(cfg_str)
|
||||||
|
.to_string_lossy()
|
||||||
|
.into_owned()
|
||||||
|
};
|
||||||
|
let cfg = match TomlConfigLoader::new_from_str(&cfg_str) {
|
||||||
|
Ok(cfg) => cfg,
|
||||||
|
Err(e) => {
|
||||||
|
set_error_msg(&format!("failed to parse config: {}", e));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let inst_name = cfg.get_inst_name();
|
||||||
|
|
||||||
|
if INSTANCE_NAME_ID_MAP.contains_key(&inst_name) {
|
||||||
|
set_error_msg("instance already exists");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let instance_id = match INSTANCE_MANAGER.run_network_instance(cfg, ConfigSource::FFI) {
|
||||||
|
Ok(id) => id,
|
||||||
|
Err(e) => {
|
||||||
|
set_error_msg(&format!("failed to start instance: {}", e));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
INSTANCE_NAME_ID_MAP.insert(inst_name, instance_id);
|
||||||
|
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn retain_network_instance(
|
||||||
|
inst_names: *const *const std::ffi::c_char,
|
||||||
|
length: usize,
|
||||||
|
) -> std::ffi::c_int {
|
||||||
|
if length == 0 {
|
||||||
|
if let Err(e) = INSTANCE_MANAGER.retain_network_instance(Vec::new()) {
|
||||||
|
set_error_msg(&format!("failed to retain instances: {}", e));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
INSTANCE_NAME_ID_MAP.clear();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let inst_names = unsafe {
|
||||||
|
assert!(!inst_names.is_null());
|
||||||
|
std::slice::from_raw_parts(inst_names, length)
|
||||||
|
.iter()
|
||||||
|
.map(|&name| {
|
||||||
|
assert!(!name.is_null());
|
||||||
|
std::ffi::CStr::from_ptr(name)
|
||||||
|
.to_string_lossy()
|
||||||
|
.into_owned()
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
};
|
||||||
|
|
||||||
|
let inst_ids: Vec<uuid::Uuid> = inst_names
|
||||||
|
.iter()
|
||||||
|
.filter_map(|name| INSTANCE_NAME_ID_MAP.get(name).map(|id| *id))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if let Err(e) = INSTANCE_MANAGER.retain_network_instance(inst_ids) {
|
||||||
|
set_error_msg(&format!("failed to retain instances: {}", e));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = INSTANCE_NAME_ID_MAP.retain(|k, _| inst_names.contains(k));
|
||||||
|
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn collect_network_infos(
|
||||||
|
infos: *mut KeyValuePair,
|
||||||
|
max_length: usize,
|
||||||
|
) -> std::ffi::c_int {
|
||||||
|
if max_length == 0 {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let infos = unsafe {
|
||||||
|
assert!(!infos.is_null());
|
||||||
|
std::slice::from_raw_parts_mut(infos, max_length)
|
||||||
|
};
|
||||||
|
|
||||||
|
let collected_infos = match INSTANCE_MANAGER.collect_network_infos() {
|
||||||
|
Ok(infos) => infos,
|
||||||
|
Err(e) => {
|
||||||
|
set_error_msg(&format!("failed to collect network infos: {}", e));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut index = 0;
|
||||||
|
for (instance_id, value) in collected_infos.iter() {
|
||||||
|
if index >= max_length {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let Some(key) = INSTANCE_MANAGER.get_network_instance_name(instance_id) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
// convert value to json string
|
||||||
|
let value = match serde_json::to_string(&value) {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(e) => {
|
||||||
|
set_error_msg(&format!("failed to serialize instance info: {}", e));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
infos[index] = KeyValuePair {
|
||||||
|
key: std::ffi::CString::new(key.clone()).unwrap().into_raw(),
|
||||||
|
value: std::ffi::CString::new(value).unwrap().into_raw(),
|
||||||
|
};
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
index as std::ffi::c_int
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_config() {
|
||||||
|
let cfg_str = r#"
|
||||||
|
inst_name = "test"
|
||||||
|
network = "test_network"
|
||||||
|
"#;
|
||||||
|
let cstr = std::ffi::CString::new(cfg_str).unwrap();
|
||||||
|
assert_eq!(parse_config(cstr.as_ptr()), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_run_network_instance() {
|
||||||
|
let cfg_str = r#"
|
||||||
|
inst_name = "test"
|
||||||
|
network = "test_network"
|
||||||
|
"#;
|
||||||
|
let cstr = std::ffi::CString::new(cfg_str).unwrap();
|
||||||
|
assert_eq!(run_network_instance(cstr.as_ptr()), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
#!/sbin/sh
|
||||||
|
|
||||||
|
#################
|
||||||
|
# Initialization
|
||||||
|
#################
|
||||||
|
|
||||||
|
umask 022
|
||||||
|
|
||||||
|
# echo before loading util_functions
|
||||||
|
ui_print() { echo "$1"; }
|
||||||
|
|
||||||
|
require_new_magisk() {
|
||||||
|
ui_print "********************************"
|
||||||
|
ui_print " Please install Magisk v20.4+! "
|
||||||
|
ui_print "********************************"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
#########################
|
||||||
|
# Load util_functions.sh
|
||||||
|
#########################
|
||||||
|
|
||||||
|
OUTFD=$2
|
||||||
|
ZIPFILE=$3
|
||||||
|
|
||||||
|
mount /data 2>/dev/null
|
||||||
|
|
||||||
|
[ -f /data/adb/magisk/util_functions.sh ] || require_new_magisk
|
||||||
|
. /data/adb/magisk/util_functions.sh
|
||||||
|
[ $MAGISK_VER_CODE -lt 20400 ] && require_new_magisk
|
||||||
|
|
||||||
|
install_module
|
||||||
|
exit 0
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
#MAGISK
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
# easytier_magisk版模块
|
||||||
|
magisk安装后重启
|
||||||
|
|
||||||
|
目录位置:/data/adb/modules/easytier_magisk
|
||||||
|
配置文件位置://data/adb/modules/easytier_magisk/config/config.toml
|
||||||
|
修改config.conf即可,修改后配置文件后去magisk app重新开关模块即可生效
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#!/data/adb/magisk/busybox sh
|
||||||
|
MODDIR=${0%/*}
|
||||||
|
|
||||||
|
# 查找 easytier-core 进程的 PID
|
||||||
|
PID=$(pgrep easytier-core)
|
||||||
|
|
||||||
|
# 检查是否找到了进程
|
||||||
|
if [ -z "$PID" ]; then
|
||||||
|
echo "easytier-core 进程未找到"
|
||||||
|
else
|
||||||
|
# 结束进程
|
||||||
|
kill $PID
|
||||||
|
echo "已结束 easytier-core 进程 (PID: $PID)"
|
||||||
|
fi
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
version=$(cat module.prop | grep 'version=' | awk -F '=' '{print $2}' | sed 's/ (.*//')
|
||||||
|
|
||||||
|
version='v'$(grep '^version =' ../../easytier/Cargo.toml | cut -d '"' -f 2)
|
||||||
|
|
||||||
|
if [ -z "$version" ]; then
|
||||||
|
echo "Error: 版本号不存在."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
filename="easytier_magisk_${version}.zip"
|
||||||
|
echo $version
|
||||||
|
|
||||||
|
|
||||||
|
if [ -f "./easytier-core" ] && [ -f "./easytier-cli" ] && [ -f "./easytier-web" ]; then
|
||||||
|
zip -r -o -X "$filename" ./ -x '.git/*' -x '.github/*' -x 'folder/*' -x 'build.sh' -x 'magisk_update.json'
|
||||||
|
else
|
||||||
|
wget -O "easytier_last.zip" https://github.com/EasyTier/EasyTier/releases/download/"$version"/easytier-linux-aarch64-"$version".zip
|
||||||
|
unzip -o easytier_last.zip -d ./
|
||||||
|
mv ./easytier-linux-aarch64/* ./
|
||||||
|
rm -rf ./easytier_last.zip
|
||||||
|
rm -rf ./easytier-linux-aarch64
|
||||||
|
zip -r -o -X "$filename" ./ -x '.git/*' -x '.github/*' -x 'folder/*' -x 'build.sh' -x 'magisk_update.json'
|
||||||
|
fi
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
instance_name = "default"
|
||||||
|
dhcp = false
|
||||||
|
#ipv4="本机ip"
|
||||||
|
listeners = [
|
||||||
|
"tcp://0.0.0.0:11010",
|
||||||
|
"udp://0.0.0.0:11010",
|
||||||
|
"wg://0.0.0.0:11011",
|
||||||
|
"ws://0.0.0.0:11011/",
|
||||||
|
"wss://0.0.0.0:11012/",
|
||||||
|
]
|
||||||
|
mapped_listeners = []
|
||||||
|
exit_nodes = []
|
||||||
|
rpc_portal = "0.0.0.0:15888"
|
||||||
|
|
||||||
|
[network_identity]
|
||||||
|
network_name = "default"
|
||||||
|
network_secret = ""
|
||||||
|
|
||||||
|
[[peer]]
|
||||||
|
#uri = "协议://中转ip:端口"
|
||||||
|
|
||||||
|
[flags]
|
||||||
|
default_protocol = "tcp"
|
||||||
|
dev_name = ""
|
||||||
|
enable_encryption = true
|
||||||
|
enable_ipv6 = true
|
||||||
|
mtu = 1380
|
||||||
|
latency_first = false
|
||||||
|
enable_exit_node = false
|
||||||
|
no_tun = false
|
||||||
|
use_smoltcp = false
|
||||||
|
foreign_network_whitelist = "*"
|
||||||
|
disable_p2p = false
|
||||||
|
relay_all_peer_rpc = false
|
||||||
|
disable_udp_hole_punching = false
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
ui_print '安装完成'
|
||||||
|
ui_print '当前架构为' + $ARCH
|
||||||
|
ui_print '当前系统版本为' + $API
|
||||||
|
ui_print '安装目录为: /data/adb/modules/easytier_magisk'
|
||||||
|
ui_print '配置文件位置: /data/adb/modules/easytier_magisk/config/config.toml'
|
||||||
|
ui_print '修改后配置文件后在magisk app点击操作按钮即可生效'
|
||||||
|
ui_print '记得重启'
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
#!/system/bin/sh
|
||||||
|
|
||||||
|
MODDIR=${0%/*}
|
||||||
|
CONFIG_FILE="${MODDIR}/config/config.toml"
|
||||||
|
LOG_FILE="${MODDIR}/log.log"
|
||||||
|
MODULE_PROP="${MODDIR}/module.prop"
|
||||||
|
EASYTIER="${MODDIR}/easytier-core"
|
||||||
|
|
||||||
|
# 更新module.prop文件中的description
|
||||||
|
update_module_description() {
|
||||||
|
local status_message=$1
|
||||||
|
sed -i "/^description=/c\description=[状态]${status_message}" ${MODULE_PROP}
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ ! -e /dev/net/tun ]; then
|
||||||
|
if [ ! -d /dev/net ]; then
|
||||||
|
mkdir -p /dev/net
|
||||||
|
fi
|
||||||
|
|
||||||
|
ln -s /dev/tun /dev/net/tun
|
||||||
|
fi
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
if ls $MODDIR | grep -q "disable"; then
|
||||||
|
update_module_description "关闭中"
|
||||||
|
if pgrep -f 'easytier-core' >/dev/null; then
|
||||||
|
echo "开关控制$(date "+%Y-%m-%d %H:%M:%S") 进程已存在,正在关闭 ..."
|
||||||
|
pkill easytier-core # 关闭进程
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if ! pgrep -f 'easytier-core' >/dev/null; then
|
||||||
|
if [ ! -f "$CONFIG_FILE" ]; then
|
||||||
|
update_module_description "config.toml不存在"
|
||||||
|
sleep 3s
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
TZ=Asia/Shanghai ${EASYTIER} -c ${CONFIG_FILE} > ${LOG_FILE} &
|
||||||
|
sleep 5s # 等待easytier-core启动完成
|
||||||
|
update_module_description "已开启(不一定运行成功)"
|
||||||
|
ip rule add from all lookup main
|
||||||
|
else
|
||||||
|
echo "开关控制$(date "+%Y-%m-%d %H:%M:%S") 进程已存在"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep 3s # 暂停3秒后再次执行循环
|
||||||
|
done
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"version": "v1.0",
|
||||||
|
"versionCode": 1,
|
||||||
|
"zipUrl": "",
|
||||||
|
"changelog": ""
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
id=easytier_magisk
|
||||||
|
name=EasyTier_Magisk
|
||||||
|
version=v2.3.2
|
||||||
|
versionCode=1
|
||||||
|
author=EasyTier
|
||||||
|
description=easytier magisk module @EasyTier(https://github.com/EasyTier/EasyTier)
|
||||||
|
updateJson=https://raw.githubusercontent.com/EasyTier/EasyTier/refs/heads/main/easytier-contrib/easytier-magisk/magisk_update.json
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
#!/data/adb/magisk/busybox sh
|
||||||
|
MODDIR=${0%/*}
|
||||||
|
# MODDIR="$(dirname $(readlink -f "$0"))"
|
||||||
|
chmod 755 ${MODDIR}/*
|
||||||
|
|
||||||
|
# 等待系统启动成功
|
||||||
|
while [ "$(getprop sys.boot_completed)" != "1" ]; do
|
||||||
|
sleep 5s
|
||||||
|
done
|
||||||
|
|
||||||
|
# 防止系统挂起
|
||||||
|
echo "PowerManagerService.noSuspend" > /sys/power/wake_lock
|
||||||
|
|
||||||
|
# 修改模块描述
|
||||||
|
sed -i 's/$(description=)$[^"]*/\1[状态]关闭中/' "$MODDIR/module.prop"
|
||||||
|
|
||||||
|
# 等待 3 秒
|
||||||
|
sleep 3s
|
||||||
|
|
||||||
|
"${MODDIR}/easytier_core.sh" &
|
||||||
|
|
||||||
|
# 检查是否启用模块
|
||||||
|
while [ ! -f ${MODDIR}/disable ]; do
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
|
||||||
|
pkill easytier-core
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
nameserver 114.114.114.114
|
||||||
|
nameserver 223.5.5.5
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
MODDIR=${0%/*}
|
||||||
|
pkill easytier-core # 结束 easytier-core 进程
|
||||||
|
rm -rf $MODDIR/*
|
||||||
@@ -23,3 +23,5 @@ dist-ssr
|
|||||||
*.njsproj
|
*.njsproj
|
||||||
*.sln
|
*.sln
|
||||||
*.sw?
|
*.sw?
|
||||||
|
|
||||||
|
vite.config.ts.timestamp*
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
shamefully-hoist=true
|
|
||||||
strict-peer-dependencies=false
|
|
||||||
@@ -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"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -1,16 +1,54 @@
|
|||||||
# Tauri + Vue 3 + TypeScript
|
# GUI for EasyTier
|
||||||
|
|
||||||
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.
|
this is a GUI implementation for EasyTier, based on Tauri2.
|
||||||
|
|
||||||
## Recommended IDE Setup
|
## Compile
|
||||||
|
|
||||||
- [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) + [Tauri](https://marketplace.visualstudio.com/items?itemName=tauri-apps.tauri-vscode) + [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer)
|
### Install prerequisites
|
||||||
|
|
||||||
## Type Support For `.vue` Imports in TS
|
```
|
||||||
|
apt install npm
|
||||||
|
npm install -g pnpm
|
||||||
|
```
|
||||||
|
|
||||||
Since TypeScript cannot handle type information for `.vue` imports, they are shimmed to be a generic Vue component type by default. In most cases this is fine if you don't really care about component prop types outside of templates. However, if you wish to get actual prop types in `.vue` imports (for example to get props validation when using manual `h(...)` calls), you can enable Volar's Take Over mode by following these steps:
|
### For Desktop (Win/Mac/Linux)
|
||||||
|
|
||||||
1. Run `Extensions: Show Built-in Extensions` from VS Code's command palette, look for `TypeScript and JavaScript Language Features`, then right click and select `Disable (Workspace)`. By default, Take Over mode will enable itself if the default TypeScript extension is disabled.
|
```
|
||||||
2. Reload the VS Code window by running `Developer: Reload Window` from the command palette.
|
cd ../tauri-plugin-vpnservice
|
||||||
|
pnpm install
|
||||||
|
pnpm build
|
||||||
|
|
||||||
You can learn more about Take Over mode [here](https://github.com/johnsoncodehk/volar/discussions/471).
|
cd ../easytier-web/frontend-lib
|
||||||
|
pnpm install
|
||||||
|
pnpm build
|
||||||
|
|
||||||
|
cd ../../easytier-gui
|
||||||
|
pnpm install
|
||||||
|
pnpm tauri build
|
||||||
|
```
|
||||||
|
|
||||||
|
### For Android
|
||||||
|
|
||||||
|
Need to install android SDK / emulator / NDK / Java (easy with android studio)
|
||||||
|
|
||||||
|
```
|
||||||
|
# For ArchLinux
|
||||||
|
sudo pacman -Sy sdkmanager
|
||||||
|
sudo sdkmanager --install platform-tools platforms\;android-34 ndk\;r26 build-tools
|
||||||
|
export PATH=/opt/android-sdk/platform-tools:$PATH
|
||||||
|
export ANDROID_HOME=/opt/android-sdk/
|
||||||
|
export NDK_HOME=/opt/android-sdk/ndk/26.0.10792818/
|
||||||
|
rustup target add aarch64-linux-android
|
||||||
|
|
||||||
|
install java 20
|
||||||
|
```
|
||||||
|
|
||||||
|
Java version depend on gradle version specified in (easytier-gui\src-tauri\gen\android\build.gradle.kts)
|
||||||
|
|
||||||
|
See [Gradle compatibility matrix](https://docs.gradle.org/current/userguide/compatibility.html) for detail .
|
||||||
|
|
||||||
|
```
|
||||||
|
pnpm install
|
||||||
|
pnpm tauri android init
|
||||||
|
pnpm tauri android build
|
||||||
|
```
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ proxy_cidrs: 子网代理CIDR
|
|||||||
enable_vpn_portal: 启用VPN门户
|
enable_vpn_portal: 启用VPN门户
|
||||||
vpn_portal_listen_port: 监听端口
|
vpn_portal_listen_port: 监听端口
|
||||||
vpn_portal_client_network: 客户端子网
|
vpn_portal_client_network: 客户端子网
|
||||||
|
dev_name: TUN接口名称
|
||||||
advanced_settings: 高级设置
|
advanced_settings: 高级设置
|
||||||
basic_settings: 基础设置
|
basic_settings: 基础设置
|
||||||
listener_urls: 监听地址
|
listener_urls: 监听地址
|
||||||
@@ -32,16 +33,30 @@ retain_network_instance: 保留网络实例
|
|||||||
collect_network_infos: 收集网络信息
|
collect_network_infos: 收集网络信息
|
||||||
settings: 设置
|
settings: 设置
|
||||||
exchange_language: Switch to English
|
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: 关闭开机自启
|
disable_auto_launch: 关闭开机自启
|
||||||
enable_auto_launch: 开启开机自启
|
enable_auto_launch: 开启开机自启
|
||||||
exit: 退出
|
exit: 退出
|
||||||
chips_placeholder: 例如: {0}, 按回车添加
|
chips_placeholder: 例如: {0}, 按回车添加
|
||||||
hostname_placeholder: '留空默认为主机名: {0}'
|
hostname_placeholder: '留空默认为主机名: {0}'
|
||||||
|
dev_name_placeholder: 注意:当多个网络同时使用相同的TUN接口名称时,将会在设置TUN的IP时产生冲突,留空以自动生成随机名称
|
||||||
off_text: 点击关闭
|
off_text: 点击关闭
|
||||||
on_text: 点击开启
|
on_text: 点击开启
|
||||||
show_config: 显示配置
|
show_config: 显示配置
|
||||||
|
edit_config: 编辑配置文件
|
||||||
close: 关闭
|
close: 关闭
|
||||||
|
save: 保存
|
||||||
|
config_saved: 配置已保存
|
||||||
|
|
||||||
|
|
||||||
|
use_latency_first: 延迟优先模式
|
||||||
my_node_info: 当前节点信息
|
my_node_info: 当前节点信息
|
||||||
peer_count: 已连接
|
peer_count: 已连接
|
||||||
upload: 上传
|
upload: 上传
|
||||||
@@ -58,8 +73,48 @@ upload_bytes: 上传
|
|||||||
download_bytes: 下载
|
download_bytes: 下载
|
||||||
loss_rate: 丢包率
|
loss_rate: 丢包率
|
||||||
|
|
||||||
|
status:
|
||||||
|
version: 内核版本
|
||||||
|
local: 本机
|
||||||
|
server: 服务器
|
||||||
|
relay: 中继
|
||||||
|
|
||||||
run_network: 运行网络
|
run_network: 运行网络
|
||||||
stop_network: 停止网络
|
stop_network: 停止网络
|
||||||
network_running: 运行中
|
network_running: 运行中
|
||||||
network_stopped: 已停止
|
network_stopped: 已停止
|
||||||
dhcp_experimental_warning: 实验性警告!使用DHCP时如果组网环境中发生IP冲突,将自动更改IP。
|
dhcp_experimental_warning: 实验性警告!使用DHCP时如果组网环境中发生IP冲突,将自动更改IP。
|
||||||
|
|
||||||
|
tray:
|
||||||
|
show: 显示 / 隐藏
|
||||||
|
exit: 退出
|
||||||
|
|
||||||
|
about:
|
||||||
|
title: 关于
|
||||||
|
version: 版本
|
||||||
|
author: 作者
|
||||||
|
homepage: 主页
|
||||||
|
license: 许可证
|
||||||
|
description: 一个简单、安全、去中心化的内网穿透 VPN 组网方案,使用 Rust 语言和 Tokio 框架实现。
|
||||||
|
check_update: 检查更新
|
||||||
|
|
||||||
|
event:
|
||||||
|
Unknown: 未知
|
||||||
|
TunDeviceReady: Tun设备就绪
|
||||||
|
TunDeviceError: Tun设备错误
|
||||||
|
PeerAdded: 对端添加
|
||||||
|
PeerRemoved: 对端移除
|
||||||
|
PeerConnAdded: 对端连接添加
|
||||||
|
PeerConnRemoved: 对端连接移除
|
||||||
|
ListenerAdded: 监听器添加
|
||||||
|
ListenerAddFailed: 监听器添加失败
|
||||||
|
ListenerAcceptFailed: 监听器接受连接失败
|
||||||
|
ConnectionAccepted: 连接已接受
|
||||||
|
ConnectionError: 连接错误
|
||||||
|
Connecting: 正在连接
|
||||||
|
ConnectError: 连接错误
|
||||||
|
VpnPortalClientConnected: VPN门户客户端已连接
|
||||||
|
VpnPortalClientDisconnected: VPN门户客户端已断开连接
|
||||||
|
DhcpIpv4Changed: DHCP IPv4地址更改
|
||||||
|
DhcpIpv4Conflicted: DHCP IPv4地址冲突
|
||||||
|
PortForwardAdded: 端口转发添加
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ proxy_cidrs: Subnet Proxy CIDRs
|
|||||||
enable_vpn_portal: Enable VPN Portal
|
enable_vpn_portal: Enable VPN Portal
|
||||||
vpn_portal_listen_port: VPN Portal Listen Port
|
vpn_portal_listen_port: VPN Portal Listen Port
|
||||||
vpn_portal_client_network: Client Sub Network
|
vpn_portal_client_network: Client Sub Network
|
||||||
|
dev_name: TUN interface name
|
||||||
advanced_settings: Advanced Settings
|
advanced_settings: Advanced Settings
|
||||||
basic_settings: Basic Settings
|
basic_settings: Basic Settings
|
||||||
listener_urls: Listener URLs
|
listener_urls: Listener URLs
|
||||||
@@ -21,7 +22,7 @@ config_network: Config Network
|
|||||||
running: Running
|
running: Running
|
||||||
error_msg: Error Message
|
error_msg: Error Message
|
||||||
detail: Detail
|
detail: Detail
|
||||||
add_new_network: Add New Network
|
add_new_network: New Network
|
||||||
del_cur_network: Delete Current Network
|
del_cur_network: Delete Current Network
|
||||||
select_network: Select Network
|
select_network: Select Network
|
||||||
network_instances: Network Instances
|
network_instances: Network Instances
|
||||||
@@ -32,16 +33,28 @@ retain_network_instance: Retain Network Instance
|
|||||||
collect_network_infos: Collect Network Infos
|
collect_network_infos: Collect Network Infos
|
||||||
settings: Settings
|
settings: Settings
|
||||||
exchange_language: 切换中文
|
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
|
disable_auto_launch: Disable Launch on Reboot
|
||||||
enable_auto_launch: Enable Launch on Reboot
|
enable_auto_launch: Enable Launch on Reboot
|
||||||
exit: Exit
|
exit: Exit
|
||||||
|
use_latency_first: Latency First Mode
|
||||||
chips_placeholder: 'e.g: {0}, press Enter to add'
|
chips_placeholder: 'e.g: {0}, press Enter to add'
|
||||||
hostname_placeholder: 'Leave blank and default to host name: {0}'
|
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
|
off_text: Press to disable
|
||||||
on_text: Press to enable
|
on_text: Press to enable
|
||||||
show_config: Show Config
|
show_config: Show Config
|
||||||
|
edit_config: Edit Config File
|
||||||
close: Close
|
close: Close
|
||||||
|
save: Save
|
||||||
|
config_saved: Configuration saved
|
||||||
my_node_info: My Node Info
|
my_node_info: My Node Info
|
||||||
peer_count: Connected
|
peer_count: Connected
|
||||||
upload: Upload
|
upload: Upload
|
||||||
@@ -58,8 +71,48 @@ upload_bytes: Upload
|
|||||||
download_bytes: Download
|
download_bytes: Download
|
||||||
loss_rate: Loss Rate
|
loss_rate: Loss Rate
|
||||||
|
|
||||||
|
status:
|
||||||
|
version: Version
|
||||||
|
local: Local
|
||||||
|
server: Server
|
||||||
|
relay: Relay
|
||||||
|
|
||||||
run_network: Run Network
|
run_network: Run Network
|
||||||
stop_network: Stop Network
|
stop_network: Stop Network
|
||||||
network_running: running
|
network_running: running
|
||||||
network_stopped: stopped
|
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.
|
dhcp_experimental_warning: Experimental warning! if there is an IP conflict in the network when using DHCP, the IP will be automatically changed.
|
||||||
|
|
||||||
|
tray:
|
||||||
|
show: Show / Hide
|
||||||
|
exit: Exit
|
||||||
|
|
||||||
|
about:
|
||||||
|
title: About
|
||||||
|
version: Version
|
||||||
|
author: Author
|
||||||
|
homepage: Homepage
|
||||||
|
license: License
|
||||||
|
description: 'EasyTier is a simple, safe and decentralized VPN networking solution implemented with the Rust language and Tokio framework.'
|
||||||
|
check_update: Check Update
|
||||||
|
|
||||||
|
event:
|
||||||
|
Unknown: Unknown
|
||||||
|
TunDeviceReady: TunDeviceReady
|
||||||
|
TunDeviceError: TunDeviceError
|
||||||
|
PeerAdded: PeerAdded
|
||||||
|
PeerRemoved: PeerRemoved
|
||||||
|
PeerConnAdded: PeerConnAdded
|
||||||
|
PeerConnRemoved: PeerConnRemoved
|
||||||
|
ListenerAdded: ListenerAdded
|
||||||
|
ListenerAddFailed: ListenerAddFailed
|
||||||
|
ListenerAcceptFailed: ListenerAcceptFailed
|
||||||
|
ConnectionAccepted: ConnectionAccepted
|
||||||
|
ConnectionError: ConnectionError
|
||||||
|
Connecting: Connecting
|
||||||
|
ConnectError: ConnectError
|
||||||
|
VpnPortalClientConnected: VpnPortalClientConnected
|
||||||
|
VpnPortalClientDisconnected: VpnPortalClientDisconnected
|
||||||
|
DhcpIpv4Changed: DhcpIpv4Changed
|
||||||
|
DhcpIpv4Conflicted: DhcpIpv4Conflicted
|
||||||
|
PortForwardAdded: PortForwardAdded
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
{
|
{
|
||||||
"name": "easytier-gui",
|
"name": "easytier-gui",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "0.0.0",
|
"version": "2.3.2",
|
||||||
"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,39 +13,51 @@
|
|||||||
"lint:fix": "eslint . --ignore-pattern src-tauri --fix"
|
"lint:fix": "eslint . --ignore-pattern src-tauri --fix"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tauri-apps/api": "^1.5.5",
|
"@primevue/themes": "4.3.3",
|
||||||
"pinia": "^2.1.7",
|
"@tauri-apps/plugin-autostart": "2.0.0",
|
||||||
"primeflex": "^3.3.1",
|
"@tauri-apps/plugin-clipboard-manager": "2.0.0",
|
||||||
"primeicons": "^7.0.0",
|
"@tauri-apps/plugin-os": "2.0.0",
|
||||||
"primevue": "^3.52.0",
|
"@tauri-apps/plugin-process": "2.0.0",
|
||||||
"vue": "^3.4.27",
|
"@tauri-apps/plugin-shell": "2.0.1",
|
||||||
"vue-i18n": "^9.13.1",
|
"@vueuse/core": "^11.2.0",
|
||||||
"vue-router": "^4.3.2"
|
"aura": "link:@primevue\\themes\\aura",
|
||||||
|
"easytier-frontend-lib": "workspace:*",
|
||||||
|
"ip-num": "1.5.1",
|
||||||
|
"pinia": "^2.2.4",
|
||||||
|
"primevue": "4.3.3",
|
||||||
|
"tauri-plugin-vpnservice-api": "workspace:*",
|
||||||
|
"vue": "^3.5.12",
|
||||||
|
"vue-router": "^4.4.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@antfu/eslint-config": "^2.17.0",
|
"@antfu/eslint-config": "^3.7.3",
|
||||||
"@intlify/unplugin-vue-i18n": "^4.0.0",
|
"@intlify/unplugin-vue-i18n": "^5.2.0",
|
||||||
"@tauri-apps/cli": "^1.5.13",
|
"@primevue/auto-import-resolver": "4.3.3",
|
||||||
"@types/node": "^20.12.11",
|
"@tauri-apps/api": "2.1.0",
|
||||||
"@types/uuid": "^9.0.8",
|
"@tauri-apps/cli": "2.1.0",
|
||||||
"@vitejs/plugin-vue": "^5.0.4",
|
"@types/default-gateway": "^7.2.2",
|
||||||
"@vue-macros/volar": "^0.19.0",
|
"@types/node": "^22.7.4",
|
||||||
"autoprefixer": "^10.4.19",
|
"@types/uuid": "^10.0.0",
|
||||||
"eslint": "^9.2.0",
|
"@vitejs/plugin-vue": "^5.1.4",
|
||||||
"eslint-plugin-format": "^0.1.1",
|
"@vue-macros/volar": "0.30.5",
|
||||||
"postcss": "^8.4.38",
|
"autoprefixer": "^10.4.20",
|
||||||
"tailwindcss": "^3.4.3",
|
"cidr-tools": "^11.0.2",
|
||||||
"typescript": "^5.4.5",
|
"default-gateway": "^7.2.2",
|
||||||
"unplugin-auto-import": "^0.17.6",
|
"eslint": "^9.12.0",
|
||||||
"unplugin-vue-components": "^0.27.0",
|
"eslint-plugin-format": "^0.1.2",
|
||||||
"unplugin-vue-macros": "^2.9.2",
|
"postcss": "^8.4.47",
|
||||||
|
"tailwindcss": "=3.4.17",
|
||||||
|
"typescript": "^5.6.2",
|
||||||
|
"unplugin-auto-import": "^0.18.3",
|
||||||
|
"unplugin-vue-components": "^0.27.4",
|
||||||
|
"unplugin-vue-macros": "^2.13.3",
|
||||||
"unplugin-vue-markdown": "^0.26.2",
|
"unplugin-vue-markdown": "^0.26.2",
|
||||||
"unplugin-vue-router": "^0.8.6",
|
"unplugin-vue-router": "^0.10.8",
|
||||||
"uuid": "^9.0.1",
|
"uuid": "^10.0.0",
|
||||||
"vite": "^5.2.11",
|
"vite": "^5.4.8",
|
||||||
"vite-plugin-vue-devtools": "^7.1.3",
|
"vite-plugin-vue-devtools": "^7.4.6",
|
||||||
"vite-plugin-vue-layouts": "^0.11.0",
|
"vite-plugin-vue-layouts": "^0.11.0",
|
||||||
"vue-i18n": "^9.13.1",
|
"vue-i18n": "^10.0.0",
|
||||||
"vue-tsc": "^2.0.17"
|
"vue-tsc": "^2.1.10"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,21 +1,35 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "easytier-gui"
|
name = "easytier-gui"
|
||||||
version = "0.0.0"
|
version = "2.3.2"
|
||||||
description = "EasyTier GUI"
|
description = "EasyTier GUI"
|
||||||
authors = ["you"]
|
authors = ["you"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "app_lib"
|
||||||
|
crate-type = ["staticlib", "cdylib", "rlib"]
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
tauri-build = { version = "1", features = [] }
|
tauri-build = { version = "2.0.0-rc", features = [] }
|
||||||
|
|
||||||
|
# enable thunk-rs when compiling for x86_64 or i686 windows
|
||||||
|
[target.x86_64-pc-windows-msvc.build-dependencies]
|
||||||
|
thunk-rs = { git = "https://github.com/easytier/thunk.git", default-features = false, features = ["win7"] }
|
||||||
|
|
||||||
|
[target.i686-pc-windows-msvc.build-dependencies]
|
||||||
|
thunk-rs = { git = "https://github.com/easytier/thunk.git", default-features = false, features = ["win7"] }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tauri = { version = "1", features = [
|
# wry 0.47 may crash on android, see https://github.com/EasyTier/EasyTier/issues/527
|
||||||
"process-exit",
|
tauri = { version = "=2.0.6", features = [
|
||||||
"system-tray",
|
"tray-icon",
|
||||||
"shell-open",
|
"image-png",
|
||||||
|
"image-ico",
|
||||||
|
"devtools",
|
||||||
] }
|
] }
|
||||||
|
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
|
|
||||||
@@ -25,14 +39,26 @@ anyhow = "1.0"
|
|||||||
chrono = { version = "0.4.37", features = ["serde"] }
|
chrono = { version = "0.4.37", features = ["serde"] }
|
||||||
|
|
||||||
once_cell = "1.18.0"
|
once_cell = "1.18.0"
|
||||||
dashmap = "5.5.3"
|
dashmap = "6.0"
|
||||||
|
|
||||||
privilege = "0.3"
|
privilege = "0.3"
|
||||||
gethostname = "0.4.3"
|
gethostname = "0.5"
|
||||||
|
|
||||||
auto-launch = "0.5.0"
|
|
||||||
dunce = "1.0.4"
|
dunce = "1.0.4"
|
||||||
|
|
||||||
|
tauri-plugin-shell = "2.0"
|
||||||
|
tauri-plugin-process = "2.0"
|
||||||
|
tauri-plugin-clipboard-manager = "2.0"
|
||||||
|
tauri-plugin-positioner = { version = "2.0", features = ["tray-icon"] }
|
||||||
|
tauri-plugin-vpnservice = { path = "../../tauri-plugin-vpnservice" }
|
||||||
|
tauri-plugin-os = "2.0"
|
||||||
|
tauri-plugin-autostart = "2.0"
|
||||||
|
uuid = "1.17.0"
|
||||||
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
# This feature is used for production builds or when a dev server is not specified, DO NOT REMOVE!!
|
# This feature is used for production builds or when a dev server is not specified, DO NOT REMOVE!!
|
||||||
custom-protocol = ["tauri/custom-protocol"]
|
custom-protocol = ["tauri/custom-protocol"]
|
||||||
|
|
||||||
|
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
|
||||||
|
tauri-plugin-single-instance = "2.2.3"
|
||||||
|
|||||||
@@ -1,34 +1,12 @@
|
|||||||
fn main() {
|
fn main() {
|
||||||
if !cfg!(debug_assertions) && cfg!(target_os = "windows") {
|
// enable thunk-rs when target os is windows and arch is x86_64 or i686
|
||||||
let mut windows = tauri_build::WindowsAttributes::new();
|
#[cfg(target_os = "windows")]
|
||||||
windows = windows.app_manifest(
|
if !std::env::var("TARGET")
|
||||||
r#"
|
.unwrap_or_default()
|
||||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
.contains("aarch64")
|
||||||
<dependency>
|
{
|
||||||
<dependentAssembly>
|
thunk::thunk();
|
||||||
<assemblyIdentity
|
}
|
||||||
type="win32"
|
|
||||||
name="Microsoft.Windows.Common-Controls"
|
tauri_build::build();
|
||||||
version="6.0.0.0"
|
}
|
||||||
processorArchitecture="*"
|
|
||||||
publicKeyToken="6595b64144ccf1df"
|
|
||||||
language="*"
|
|
||||||
/>
|
|
||||||
</dependentAssembly>
|
|
||||||
</dependency>
|
|
||||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
|
|
||||||
<security>
|
|
||||||
<requestedPrivileges>
|
|
||||||
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
|
|
||||||
</requestedPrivileges>
|
|
||||||
</security>
|
|
||||||
</trustInfo>
|
|
||||||
</assembly>
|
|
||||||
"#,
|
|
||||||
);
|
|
||||||
tauri_build::try_build(tauri_build::Attributes::new().windows_attributes(windows))
|
|
||||||
.expect("failed to run build script");
|
|
||||||
} else {
|
|
||||||
tauri_build::build();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
{
|
||||||
|
"$schema": "../gen/schemas/desktop-schema.json",
|
||||||
|
"identifier": "migrated",
|
||||||
|
"description": "permissions that were migrated from v1",
|
||||||
|
"local": true,
|
||||||
|
"windows": [
|
||||||
|
"main"
|
||||||
|
],
|
||||||
|
"permissions": [
|
||||||
|
"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:window:allow-set-title",
|
||||||
|
"core:app:default",
|
||||||
|
"core:resources:default",
|
||||||
|
"core:menu:default",
|
||||||
|
"core:tray:default",
|
||||||
|
"shell:allow-open",
|
||||||
|
"process:allow-exit",
|
||||||
|
"clipboard-manager:allow-read-text",
|
||||||
|
"clipboard-manager:allow-write-text",
|
||||||
|
"shell:default",
|
||||||
|
"process:default",
|
||||||
|
"clipboard-manager: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",
|
||||||
|
"vpnservice:allow-stop-vpn",
|
||||||
|
"vpnservice:allow-registerListener",
|
||||||
|
"os:default",
|
||||||
|
"os:allow-os-type",
|
||||||
|
"os:allow-arch",
|
||||||
|
"os:allow-hostname",
|
||||||
|
"os:allow-platform",
|
||||||
|
"os:allow-locale",
|
||||||
|
"autostart:default",
|
||||||
|
"autostart:allow-disable",
|
||||||
|
"autostart:allow-enable",
|
||||||
|
"autostart:allow-is-enabled"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
# EditorConfig is awesome: https://EditorConfig.org
|
||||||
|
|
||||||
|
# top-most EditorConfig file
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
end_of_line = lf
|
||||||
|
charset = utf-8
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
insert_final_newline = false
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
*.iml
|
||||||
|
.gradle
|
||||||
|
/local.properties
|
||||||
|
/.idea/caches
|
||||||
|
/.idea/libraries
|
||||||
|
/.idea/modules.xml
|
||||||
|
/.idea/workspace.xml
|
||||||
|
/.idea/navEditor.xml
|
||||||
|
/.idea/assetWizardSettings.xml
|
||||||
|
.DS_Store
|
||||||
|
build
|
||||||
|
/captures
|
||||||
|
.externalNativeBuild
|
||||||
|
.cxx
|
||||||
|
local.properties
|
||||||
|
key.properties
|
||||||
|
|
||||||
|
/.tauri
|
||||||
|
/tauri.settings.gradle
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
/src/main/java/com/kkrainbow/easytier/generated
|
||||||
|
/src/main/jniLibs/**/*.so
|
||||||
|
/src/main/assets/tauri.conf.json
|
||||||
|
/tauri.build.gradle.kts
|
||||||
|
/proguard-tauri.pro
|
||||||
|
/tauri.properties
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
import java.util.Properties
|
||||||
|
import java.io.FileInputStream
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id("com.android.application")
|
||||||
|
id("org.jetbrains.kotlin.android")
|
||||||
|
id("rust")
|
||||||
|
}
|
||||||
|
|
||||||
|
val tauriProperties = Properties().apply {
|
||||||
|
val propFile = file("tauri.properties")
|
||||||
|
if (propFile.exists()) {
|
||||||
|
propFile.inputStream().use { load(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdk = 34
|
||||||
|
namespace = "com.kkrainbow.easytier"
|
||||||
|
defaultConfig {
|
||||||
|
manifestPlaceholders["usesCleartextTraffic"] = "false"
|
||||||
|
applicationId = "com.kkrainbow.easytier"
|
||||||
|
minSdk = 24
|
||||||
|
targetSdk = 34
|
||||||
|
versionCode = tauriProperties.getProperty("tauri.android.versionCode", "1").toInt()
|
||||||
|
versionName = tauriProperties.getProperty("tauri.android.versionName", "1.0")
|
||||||
|
}
|
||||||
|
signingConfigs {
|
||||||
|
create("release") {
|
||||||
|
val keystorePropertiesFile = rootProject.file("keystore.properties")
|
||||||
|
val keystoreProperties = Properties()
|
||||||
|
if (keystorePropertiesFile.exists()) {
|
||||||
|
keystoreProperties.load(FileInputStream(keystorePropertiesFile))
|
||||||
|
}
|
||||||
|
|
||||||
|
keyAlias = keystoreProperties["keyAlias"] as String
|
||||||
|
keyPassword = keystoreProperties["keyPassword"] as String
|
||||||
|
storeFile = file(keystoreProperties["storeFile"] as String)
|
||||||
|
storePassword = keystoreProperties["storePassword"] as String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buildTypes {
|
||||||
|
getByName("debug") {
|
||||||
|
manifestPlaceholders["usesCleartextTraffic"] = "true"
|
||||||
|
isDebuggable = true
|
||||||
|
isJniDebuggable = true
|
||||||
|
isMinifyEnabled = false
|
||||||
|
packaging { jniLibs.keepDebugSymbols.add("*/arm64-v8a/*.so")
|
||||||
|
jniLibs.keepDebugSymbols.add("*/armeabi-v7a/*.so")
|
||||||
|
jniLibs.keepDebugSymbols.add("*/x86/*.so")
|
||||||
|
jniLibs.keepDebugSymbols.add("*/x86_64/*.so")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getByName("release") {
|
||||||
|
isMinifyEnabled = true
|
||||||
|
proguardFiles(
|
||||||
|
*fileTree(".") { include("**/*.pro") }
|
||||||
|
.plus(getDefaultProguardFile("proguard-android-optimize.txt"))
|
||||||
|
.toList().toTypedArray()
|
||||||
|
)
|
||||||
|
signingConfig = signingConfigs.getByName("release")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = "1.8"
|
||||||
|
}
|
||||||
|
buildFeatures {
|
||||||
|
buildConfig = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rust {
|
||||||
|
rootDirRel = "../../../"
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation("androidx.webkit:webkit:1.6.1")
|
||||||
|
implementation("androidx.appcompat:appcompat:1.6.1")
|
||||||
|
implementation("com.google.android.material:material:1.8.0")
|
||||||
|
testImplementation("junit:junit:4.13.2")
|
||||||
|
androidTestImplementation("androidx.test.ext:junit:1.1.4")
|
||||||
|
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.0")
|
||||||
|
}
|
||||||
|
|
||||||
|
apply(from = "tauri.build.gradle.kts")
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# You can control the set of applied configuration files using the
|
||||||
|
# proguardFiles setting in build.gradle.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
# If your project uses WebView with JS, uncomment the following
|
||||||
|
# and specify the fully qualified class name to the JavaScript interface
|
||||||
|
# class:
|
||||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||||
|
# public *;
|
||||||
|
#}
|
||||||
|
|
||||||
|
# Uncomment this to preserve the line number information for
|
||||||
|
# debugging stack traces.
|
||||||
|
#-keepattributes SourceFile,LineNumberTable
|
||||||
|
|
||||||
|
# If you keep the line number information, uncomment this to
|
||||||
|
# hide the original source file name.
|
||||||
|
#-renamesourcefileattribute SourceFile
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<application
|
||||||
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:theme="@style/Theme.easytier_gui"
|
||||||
|
android:usesCleartextTraffic="${usesCleartextTraffic}">
|
||||||
|
<activity
|
||||||
|
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode"
|
||||||
|
android:launchMode="singleTask"
|
||||||
|
android:label="@string/main_activity_title"
|
||||||
|
android:name=".MainActivity"
|
||||||
|
android:windowSoftInputMode="adjustResize"
|
||||||
|
android:exported="true">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
|
||||||
|
<provider
|
||||||
|
android:name="androidx.core.content.FileProvider"
|
||||||
|
android:authorities="${applicationId}.fileprovider"
|
||||||
|
android:exported="false"
|
||||||
|
android:grantUriPermissions="true">
|
||||||
|
<meta-data
|
||||||
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
|
android:resource="@xml/file_paths" />
|
||||||
|
</provider>
|
||||||
|
|
||||||
|
<service
|
||||||
|
android:name="com.plugin.vpnservice.TauriVpnService"
|
||||||
|
android:enabled="true"
|
||||||
|
android:exported="false"
|
||||||
|
android:label="@string/main_activity_title"
|
||||||
|
android:permission="android.permission.BIND_VPN_SERVICE"
|
||||||
|
android:foregroundServiceType="specialUse">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.net.VpnService" />
|
||||||
|
</intent-filter>
|
||||||
|
</service>
|
||||||
|
</application>
|
||||||
|
</manifest>
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package com.kkrainbow.easytier
|
||||||
|
|
||||||
|
class MainActivity : TauriActivity()
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:aapt="http://schemas.android.com/aapt"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportWidth="108"
|
||||||
|
android:viewportHeight="108">
|
||||||
|
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
<gradient
|
||||||
|
android:endX="85.84757"
|
||||||
|
android:endY="92.4963"
|
||||||
|
android:startX="42.9492"
|
||||||
|
android:startY="49.59793"
|
||||||
|
android:type="linear">
|
||||||
|
<item
|
||||||
|
android:color="#44000000"
|
||||||
|
android:offset="0.0" />
|
||||||
|
<item
|
||||||
|
android:color="#00000000"
|
||||||
|
android:offset="1.0" />
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFF"
|
||||||
|
android:fillType="nonZero"
|
||||||
|
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:strokeColor="#00000000" />
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,170 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportWidth="108"
|
||||||
|
android:viewportHeight="108">
|
||||||
|
<path
|
||||||
|
android:fillColor="#3DDC84"
|
||||||
|
android:pathData="M0,0h108v108h-108z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M9,0L9,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,0L19,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M29,0L29,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M39,0L39,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M49,0L49,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M59,0L59,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M69,0L69,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M79,0L79,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M89,0L89,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M99,0L99,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,9L108,9"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,19L108,19"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,29L108,29"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,39L108,39"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,49L108,49"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,59L108,59"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,69L108,69"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,79L108,79"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,89L108,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,99L108,99"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,29L89,29"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,39L89,39"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,49L89,49"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,59L89,59"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,69L89,69"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,79L89,79"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M29,19L29,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M39,19L39,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M49,19L49,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M59,19L59,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M69,19L69,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M79,19L79,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:context=".MainActivity">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Hello World!"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
After Width: | Height: | Size: 5.3 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 5.3 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 62 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 85 KiB |
|
After Width: | Height: | Size: 34 KiB |
@@ -0,0 +1,6 @@
|
|||||||
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
<!-- Base application theme. -->
|
||||||
|
<style name="Theme.easytier_gui" parent="Theme.MaterialComponents.DayNight.NoActionBar">
|
||||||
|
<!-- Customize your theme here. -->
|
||||||
|
</style>
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="purple_200">#FFBB86FC</color>
|
||||||
|
<color name="purple_500">#FF6200EE</color>
|
||||||
|
<color name="purple_700">#FF3700B3</color>
|
||||||
|
<color name="teal_200">#FF03DAC5</color>
|
||||||
|
<color name="teal_700">#FF018786</color>
|
||||||
|
<color name="black">#FF000000</color>
|
||||||
|
<color name="white">#FFFFFFFF</color>
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<resources>
|
||||||
|
<string name="app_name">easytier-gui</string>
|
||||||
|
<string name="main_activity_title">easytier-gui</string>
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
<!-- Base application theme. -->
|
||||||
|
<style name="Theme.easytier_gui" parent="Theme.MaterialComponents.DayNight.NoActionBar">
|
||||||
|
<!-- Customize your theme here. -->
|
||||||
|
</style>
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<external-path name="my_images" path="." />
|
||||||
|
<cache-path name="my_cache_images" path="." />
|
||||||
|
</paths>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
classpath("com.android.tools.build:gradle:8.3.2")
|
||||||
|
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.21")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allprojects {
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register("clean").configure {
|
||||||
|
delete("build")
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
plugins {
|
||||||
|
`kotlin-dsl`
|
||||||
|
}
|
||||||
|
|
||||||
|
gradlePlugin {
|
||||||
|
plugins {
|
||||||
|
create("pluginsForCoolKids") {
|
||||||
|
id = "rust"
|
||||||
|
implementationClass = "RustPlugin"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compileOnly(gradleApi())
|
||||||
|
implementation("com.android.tools.build:gradle:8.3.2")
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import java.io.File
|
||||||
|
import org.apache.tools.ant.taskdefs.condition.Os
|
||||||
|
import org.gradle.api.DefaultTask
|
||||||
|
import org.gradle.api.GradleException
|
||||||
|
import org.gradle.api.logging.LogLevel
|
||||||
|
import org.gradle.api.tasks.Input
|
||||||
|
import org.gradle.api.tasks.TaskAction
|
||||||
|
|
||||||
|
open class BuildTask : DefaultTask() {
|
||||||
|
@Input
|
||||||
|
var rootDirRel: String? = null
|
||||||
|
@Input
|
||||||
|
var target: String? = null
|
||||||
|
@Input
|
||||||
|
var release: Boolean? = null
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
fun assemble() {
|
||||||
|
val executable = """pnpm""";
|
||||||
|
try {
|
||||||
|
runTauriCli(executable)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
|
||||||
|
runTauriCli("$executable.cmd")
|
||||||
|
} else {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun runTauriCli(executable: String) {
|
||||||
|
val rootDirRel = rootDirRel ?: throw GradleException("rootDirRel cannot be null")
|
||||||
|
val target = target ?: throw GradleException("target cannot be null")
|
||||||
|
val release = release ?: throw GradleException("release cannot be null")
|
||||||
|
val args = listOf("tauri", "android", "android-studio-script");
|
||||||
|
|
||||||
|
project.exec {
|
||||||
|
workingDir(File(project.projectDir, rootDirRel))
|
||||||
|
executable(executable)
|
||||||
|
args(args)
|
||||||
|
if (project.logger.isEnabled(LogLevel.DEBUG)) {
|
||||||
|
args("-vv")
|
||||||
|
} else if (project.logger.isEnabled(LogLevel.INFO)) {
|
||||||
|
args("-v")
|
||||||
|
}
|
||||||
|
if (release) {
|
||||||
|
args("--release")
|
||||||
|
}
|
||||||
|
args(listOf("--target", target))
|
||||||
|
}.assertNormalExitValue()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
import com.android.build.api.dsl.ApplicationExtension
|
||||||
|
import org.gradle.api.DefaultTask
|
||||||
|
import org.gradle.api.Plugin
|
||||||
|
import org.gradle.api.Project
|
||||||
|
import org.gradle.kotlin.dsl.configure
|
||||||
|
import org.gradle.kotlin.dsl.get
|
||||||
|
|
||||||
|
const val TASK_GROUP = "rust"
|
||||||
|
|
||||||
|
open class Config {
|
||||||
|
lateinit var rootDirRel: String
|
||||||
|
}
|
||||||
|
|
||||||
|
open class RustPlugin : Plugin<Project> {
|
||||||
|
private lateinit var config: Config
|
||||||
|
|
||||||
|
override fun apply(project: Project) = with(project) {
|
||||||
|
config = extensions.create("rust", Config::class.java)
|
||||||
|
|
||||||
|
val defaultAbiList = listOf("arm64-v8a", "armeabi-v7a", "x86", "x86_64");
|
||||||
|
val abiList = (findProperty("abiList") as? String)?.split(',') ?: defaultAbiList
|
||||||
|
|
||||||
|
val defaultArchList = listOf("arm64", "arm", "x86", "x86_64");
|
||||||
|
val archList = (findProperty("archList") as? String)?.split(',') ?: defaultArchList
|
||||||
|
|
||||||
|
val targetsList = (findProperty("targetList") as? String)?.split(',') ?: listOf("aarch64", "armv7", "i686", "x86_64")
|
||||||
|
|
||||||
|
extensions.configure<ApplicationExtension> {
|
||||||
|
@Suppress("UnstableApiUsage")
|
||||||
|
flavorDimensions.add("abi")
|
||||||
|
productFlavors {
|
||||||
|
create("universal") {
|
||||||
|
dimension = "abi"
|
||||||
|
ndk {
|
||||||
|
abiFilters += abiList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defaultArchList.forEachIndexed { index, arch ->
|
||||||
|
create(arch) {
|
||||||
|
dimension = "abi"
|
||||||
|
ndk {
|
||||||
|
abiFilters.add(defaultAbiList[index])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
afterEvaluate {
|
||||||
|
for (profile in listOf("debug", "release")) {
|
||||||
|
val profileCapitalized = profile.replaceFirstChar { it.uppercase() }
|
||||||
|
val buildTask = tasks.maybeCreate(
|
||||||
|
"rustBuildUniversal$profileCapitalized",
|
||||||
|
DefaultTask::class.java
|
||||||
|
).apply {
|
||||||
|
group = TASK_GROUP
|
||||||
|
description = "Build dynamic library in $profile mode for all targets"
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks["mergeUniversal${profileCapitalized}JniLibFolders"].dependsOn(buildTask)
|
||||||
|
|
||||||
|
for (targetPair in targetsList.withIndex()) {
|
||||||
|
val targetName = targetPair.value
|
||||||
|
val targetArch = archList[targetPair.index]
|
||||||
|
val targetArchCapitalized = targetArch.replaceFirstChar { it.uppercase() }
|
||||||
|
val targetBuildTask = project.tasks.maybeCreate(
|
||||||
|
"rustBuild$targetArchCapitalized$profileCapitalized",
|
||||||
|
BuildTask::class.java
|
||||||
|
).apply {
|
||||||
|
group = TASK_GROUP
|
||||||
|
description = "Build dynamic library in $profile mode for $targetArch"
|
||||||
|
rootDirRel = config.rootDirRel
|
||||||
|
target = targetName
|
||||||
|
release = profile == "release"
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTask.dependsOn(targetBuildTask)
|
||||||
|
tasks["merge$targetArchCapitalized${profileCapitalized}JniLibFolders"].dependsOn(
|
||||||
|
targetBuildTask
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# Project-wide Gradle settings.
|
||||||
|
# IDE (e.g. Android Studio) users:
|
||||||
|
# Gradle settings configured through the IDE *will override*
|
||||||
|
# any settings specified in this file.
|
||||||
|
# For more details on how to configure your build environment visit
|
||||||
|
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||||
|
# Specifies the JVM arguments used for the daemon process.
|
||||||
|
# The setting is particularly useful for tweaking memory settings.
|
||||||
|
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
||||||
|
# When configured, Gradle will run in incubating parallel mode.
|
||||||
|
# This option should only be used with decoupled projects. More details, visit
|
||||||
|
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||||
|
# org.gradle.parallel=true
|
||||||
|
# AndroidX package structure to make it clearer which packages are bundled with the
|
||||||
|
# Android operating system, and which are packaged with your app"s APK
|
||||||
|
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||||
|
android.useAndroidX=true
|
||||||
|
# Kotlin code style for this project: "official" or "obsolete":
|
||||||
|
kotlin.code.style=official
|
||||||
|
# Enables namespacing of each library's R class so that its R class includes only the
|
||||||
|
# resources declared in the library itself and none from the library's dependencies,
|
||||||
|
# thereby reducing the size of the R class for that library
|
||||||
|
android.nonTransitiveRClass=true
|
||||||
|
android.nonFinalResIds=false
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#Tue May 10 19:22:52 CST 2022
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
@@ -0,0 +1,185 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright 2015 the original author or authors.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
##
|
||||||
|
## Gradle start up script for UN*X
|
||||||
|
##
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
PRG="$0"
|
||||||
|
# Need this for relative symlinks.
|
||||||
|
while [ -h "$PRG" ] ; do
|
||||||
|
ls=`ls -ld "$PRG"`
|
||||||
|
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||||
|
if expr "$link" : '/.*' > /dev/null; then
|
||||||
|
PRG="$link"
|
||||||
|
else
|
||||||
|
PRG=`dirname "$PRG"`"/$link"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
SAVED="`pwd`"
|
||||||
|
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||||
|
APP_HOME="`pwd -P`"
|
||||||
|
cd "$SAVED" >/dev/null
|
||||||
|
|
||||||
|
APP_NAME="Gradle"
|
||||||
|
APP_BASE_NAME=`basename "$0"`
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD="maximum"
|
||||||
|
|
||||||
|
warn () {
|
||||||
|
echo "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
die () {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
nonstop=false
|
||||||
|
case "`uname`" in
|
||||||
|
CYGWIN* )
|
||||||
|
cygwin=true
|
||||||
|
;;
|
||||||
|
Darwin* )
|
||||||
|
darwin=true
|
||||||
|
;;
|
||||||
|
MINGW* )
|
||||||
|
msys=true
|
||||||
|
;;
|
||||||
|
NONSTOP* )
|
||||||
|
nonstop=true
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
# Determine the Java command to use to start the JVM.
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||||
|
else
|
||||||
|
JAVACMD="$JAVA_HOME/bin/java"
|
||||||
|
fi
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD="java"
|
||||||
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||||
|
MAX_FD_LIMIT=`ulimit -H -n`
|
||||||
|
if [ $? -eq 0 ] ; then
|
||||||
|
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||||
|
MAX_FD="$MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
ulimit -n $MAX_FD
|
||||||
|
if [ $? -ne 0 ] ; then
|
||||||
|
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Darwin, add options to specify how the application appears in the dock
|
||||||
|
if $darwin; then
|
||||||
|
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
|
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||||
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
|
|
||||||
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
|
|
||||||
|
# We build the pattern for arguments to be converted via cygpath
|
||||||
|
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||||
|
SEP=""
|
||||||
|
for dir in $ROOTDIRSRAW ; do
|
||||||
|
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||||
|
SEP="|"
|
||||||
|
done
|
||||||
|
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||||
|
# Add a user-defined pattern to the cygpath arguments
|
||||||
|
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||||
|
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||||
|
fi
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
i=0
|
||||||
|
for arg in "$@" ; do
|
||||||
|
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||||
|
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||||
|
|
||||||
|
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||||
|
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||||
|
else
|
||||||
|
eval `echo args$i`="\"$arg\""
|
||||||
|
fi
|
||||||
|
i=`expr $i + 1`
|
||||||
|
done
|
||||||
|
case $i in
|
||||||
|
0) set -- ;;
|
||||||
|
1) set -- "$args0" ;;
|
||||||
|
2) set -- "$args0" "$args1" ;;
|
||||||
|
3) set -- "$args0" "$args1" "$args2" ;;
|
||||||
|
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||||
|
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||||
|
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||||
|
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||||
|
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||||
|
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Escape application args
|
||||||
|
save () {
|
||||||
|
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||||
|
echo " "
|
||||||
|
}
|
||||||
|
APP_ARGS=`save "$@"`
|
||||||
|
|
||||||
|
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||||
|
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||||
|
|
||||||
|
exec "$JAVACMD" "$@"
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
@rem
|
||||||
|
@rem Copyright 2015 the original author or authors.
|
||||||
|
@rem
|
||||||
|
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@rem you may not use this file except in compliance with the License.
|
||||||
|
@rem You may obtain a copy of the License at
|
||||||
|
@rem
|
||||||
|
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@rem
|
||||||
|
@rem Unless required by applicable law or agreed to in writing, software
|
||||||
|
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@rem See the License for the specific language governing permissions and
|
||||||
|
@rem limitations under the License.
|
||||||
|
@rem
|
||||||
|
|
||||||
|
@if "%DEBUG%" == "" @echo off
|
||||||
|
@rem ##########################################################################
|
||||||
|
@rem
|
||||||
|
@rem Gradle startup script for Windows
|
||||||
|
@rem
|
||||||
|
@rem ##########################################################################
|
||||||
|
|
||||||
|
@rem Set local scope for the variables with windows NT shell
|
||||||
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
|
set DIRNAME=%~dp0
|
||||||
|
if "%DIRNAME%" == "" set DIRNAME=.
|
||||||
|
set APP_BASE_NAME=%~n0
|
||||||
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||||
|
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||||
|
|
||||||
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
|
@rem Find java.exe
|
||||||
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
|
set JAVA_EXE=java.exe
|
||||||
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
|
if "%ERRORLEVEL%" == "0" goto execute
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
|
|
||||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
@rem Execute Gradle
|
||||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||||
|
|
||||||
|
:fail
|
||||||
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
|
rem the _cmd.exe /c_ return code!
|
||||||
|
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||||
|
exit /b 1
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
storePassword=EasyTier
|
||||||
|
keyPassword=EasyTier
|
||||||
|
keyAlias=upload
|
||||||
|
storeFile=upload-keystore.jks
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
include ':app'
|
||||||
|
|
||||||
|
apply from: 'tauri.settings.gradle'
|
||||||
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 59 KiB |