├── 1
├── .github
└── workflows
│ ├── Stop All Workflows.yaml
│ ├── android-build-all.yaml
│ ├── android-build-arm64.yaml
│ ├── android-build-armv7.yaml
│ ├── android-build-x86_64.yaml
│ ├── dart.yml
│ ├── ios-build.yaml
│ ├── linux-build.yaml
│ ├── mac-build copy.yaml
│ ├── windows-build-Setup-cmd.yml
│ ├── windows-build-Setup.yml
│ └── windows-build.yml
├── .gitignore
├── .metadata
├── .sentry-native
├── 96697942-6dc9-4fd5-e899-18c41b0da851.run.lock
├── b221b550-270f-48e3-67d4-65b549f7c944.run.lock
└── d5bced59-91f4-4c15-d704-bb7d3ae30d05.run.lock
├── .vscode
└── launch.json
├── 1.iss
├── README.md
├── analysis_options.yaml
├── android
├── .gitignore
├── app
│ ├── build.gradle.kts
│ └── src
│ │ ├── debug
│ │ └── AndroidManifest.xml
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── kotlin
│ │ │ └── com
│ │ │ │ └── example
│ │ │ │ └── astral
│ │ │ │ └── MainActivity.kt
│ │ └── res
│ │ │ ├── drawable-v21
│ │ │ └── launch_background.xml
│ │ │ ├── drawable
│ │ │ ├── launch_background.xml
│ │ │ └── splash_logo.png
│ │ │ ├── mipmap-hdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-mdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xhdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── values-night
│ │ │ └── styles.xml
│ │ │ └── values
│ │ │ └── styles.xml
│ │ └── profile
│ │ └── AndroidManifest.xml
├── build.gradle.kts
├── gradle.properties
├── gradle
│ └── wrapper
│ │ └── gradle-wrapper.properties
└── settings.gradle.kts
├── assets
└── icon.ico
├── devtools_options.yaml
├── dlls
├── Ak.dll
├── Packet.dll
├── Packet.lib
├── arm64
│ ├── Packet.dll
│ ├── Packet.lib
│ └── wintun.dll
├── i686
│ ├── Packet.dll
│ ├── Packet.lib
│ └── wintun.dll
└── wintun.dll
├── flutter_rust_bridge.yaml
├── fonts
├── MiSans-Demibold.ttf
└── MiSans-Regular.ttf
├── integration_test
└── simple_test.dart
├── lib
├── app.dart
├── fun
│ ├── e_d_room.dart
│ ├── net_astral_udp.dart
│ ├── random_name.dart
│ ├── reg.dart
│ ├── route_fun.dart
│ ├── show_add_room_dialog.dart
│ ├── show_edit_room_dialog.dart
│ ├── show_server_dialog.dart
│ └── up.dart
├── k
│ ├── app_s
│ │ └── aps.dart
│ ├── database
│ │ └── app_data.dart
│ ├── mod
│ │ └── window_manager.dart
│ ├── models
│ │ ├── all_settings.dart
│ │ ├── all_settings.g.dart
│ │ ├── net_config.dart
│ │ ├── net_config.g.dart
│ │ ├── room.dart
│ │ ├── room.g.dart
│ │ ├── rule_group.dart
│ │ ├── rule_group.g.dart
│ │ ├── server_mod.dart
│ │ ├── server_mod.g.dart
│ │ ├── theme_settings.dart
│ │ └── theme_settings.g.dart
│ ├── models_mod
│ │ ├── all_settings_cz.dart
│ │ ├── net_config_cz.dart
│ │ ├── room_cz.dart
│ │ ├── rule_group_cz.dart
│ │ ├── server_cz.dart
│ │ └── theme_settings_cz.dart
│ └── navigtion.dart
├── main.dart
├── screens
│ ├── home_page.dart
│ ├── main_screen.dart
│ ├── room_page.dart
│ ├── server_page.dart
│ ├── settings_page.dart
│ └── user_page.dart
├── src
│ └── rust
│ │ ├── api
│ │ ├── firewall.dart
│ │ ├── hops.dart
│ │ └── simple.dart
│ │ ├── frb_generated.dart
│ │ ├── frb_generated.io.dart
│ │ └── frb_generated.web.dart
└── wid
│ ├── all_user_card.dart
│ ├── bottom_nav.dart
│ ├── canvas_jump.dart
│ ├── home
│ ├── about_home.dart
│ ├── bugcs.dart
│ ├── connect_button.dart
│ ├── contributors.dart
│ ├── servers_home.dart
│ ├── udp_log.dart
│ ├── user_ip.dart
│ └── virtual_ip.dart
│ ├── home_box.dart
│ ├── left_nav.dart
│ ├── mini_user_card.dart
│ ├── room_card.dart
│ ├── server_card.dart
│ ├── status_bar.dart
│ ├── theme_selector.dart
│ └── windows_controls.dart
├── linux
├── .gitignore
├── CMakeLists.txt
├── flutter
│ ├── CMakeLists.txt
│ ├── generated_plugin_registrant.cc
│ ├── generated_plugin_registrant.h
│ └── generated_plugins.cmake
└── runner
│ ├── CMakeLists.txt
│ ├── main.cc
│ ├── my_application.cc
│ └── my_application.h
├── pubspec.lock
├── pubspec.yaml
├── rust
├── .gitignore
├── Cargo.lock
├── Cargo.toml
├── easytier
│ ├── Cargo.toml
│ ├── build.rs
│ ├── locales
│ │ └── app.yml
│ ├── src
│ │ ├── arch
│ │ │ ├── mod.rs
│ │ │ └── windows.rs
│ │ ├── common
│ │ │ ├── compressor.rs
│ │ │ ├── config.rs
│ │ │ ├── constants.rs
│ │ │ ├── defer.rs
│ │ │ ├── dns.rs
│ │ │ ├── error.rs
│ │ │ ├── global_ctx.rs
│ │ │ ├── ifcfg
│ │ │ │ ├── darwin.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── netlink.rs
│ │ │ │ ├── route.rs
│ │ │ │ └── windows.rs
│ │ │ ├── mod.rs
│ │ │ ├── netns.rs
│ │ │ ├── network.rs
│ │ │ ├── scoped_task.rs
│ │ │ ├── stun.rs
│ │ │ └── stun_codec_ext.rs
│ │ ├── connector
│ │ │ ├── direct.rs
│ │ │ ├── dns_connector.rs
│ │ │ ├── http_connector.rs
│ │ │ ├── manual.rs
│ │ │ ├── mod.rs
│ │ │ └── udp_hole_punch
│ │ │ │ ├── both_easy_sym.rs
│ │ │ │ ├── common.rs
│ │ │ │ ├── cone.rs
│ │ │ │ ├── mod.rs
│ │ │ │ └── sym_to_cone.rs
│ │ ├── easytier-cli.rs
│ │ ├── easytier-core.rs
│ │ ├── gateway
│ │ │ ├── fast_socks5
│ │ │ │ ├── LICENSE
│ │ │ │ ├── README.md
│ │ │ │ ├── mod.rs
│ │ │ │ ├── server.rs
│ │ │ │ └── util
│ │ │ │ │ ├── mod.rs
│ │ │ │ │ ├── stream.rs
│ │ │ │ │ └── target_addr.rs
│ │ │ ├── icmp_proxy.rs
│ │ │ ├── ip_reassembler.rs
│ │ │ ├── kcp_proxy.rs
│ │ │ ├── mod.rs
│ │ │ ├── socks5.rs
│ │ │ ├── tcp_proxy.rs
│ │ │ ├── tokio_smoltcp
│ │ │ │ ├── channel_device.rs
│ │ │ │ ├── device.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── reactor.rs
│ │ │ │ ├── socket.rs
│ │ │ │ └── socket_allocator.rs
│ │ │ └── udp_proxy.rs
│ │ ├── instance
│ │ │ ├── dns_server
│ │ │ │ ├── client_instance.rs
│ │ │ │ ├── config.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── runner.rs
│ │ │ │ ├── server.rs
│ │ │ │ ├── server_instance.rs
│ │ │ │ ├── system_config
│ │ │ │ │ ├── darwin.rs
│ │ │ │ │ ├── linux.rs
│ │ │ │ │ ├── mod.rs
│ │ │ │ │ └── windows.rs
│ │ │ │ └── tests.rs
│ │ │ ├── instance.rs
│ │ │ ├── listeners.rs
│ │ │ ├── mod.rs
│ │ │ └── virtual_nic.rs
│ │ ├── launcher.rs
│ │ ├── lib.rs
│ │ ├── peer_center
│ │ │ ├── instance.rs
│ │ │ ├── mod.rs
│ │ │ └── server.rs
│ │ ├── peers
│ │ │ ├── encrypt
│ │ │ │ ├── aes_gcm.rs
│ │ │ │ ├── mod.rs
│ │ │ │ └── ring_aes_gcm.rs
│ │ │ ├── foreign_network_client.rs
│ │ │ ├── foreign_network_manager.rs
│ │ │ ├── mod.rs
│ │ │ ├── peer.rs
│ │ │ ├── peer_conn.rs
│ │ │ ├── peer_conn_ping.rs
│ │ │ ├── peer_manager.rs
│ │ │ ├── peer_map.rs
│ │ │ ├── peer_ospf_route.rs
│ │ │ ├── peer_rpc.rs
│ │ │ ├── peer_rpc_service.rs
│ │ │ ├── peer_task.rs
│ │ │ ├── route_trait.rs
│ │ │ ├── rpc_service.rs
│ │ │ └── tests.rs
│ │ ├── proto
│ │ │ ├── cli.proto
│ │ │ ├── cli.rs
│ │ │ ├── common.proto
│ │ │ ├── common.rs
│ │ │ ├── error.proto
│ │ │ ├── error.rs
│ │ │ ├── magic_dns.proto
│ │ │ ├── magic_dns.rs
│ │ │ ├── mod.rs
│ │ │ ├── peer_rpc.proto
│ │ │ ├── peer_rpc.rs
│ │ │ ├── rpc_impl
│ │ │ │ ├── bidirect.rs
│ │ │ │ ├── client.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── packet.rs
│ │ │ │ ├── server.rs
│ │ │ │ ├── service_registry.rs
│ │ │ │ └── standalone.rs
│ │ │ ├── rpc_types
│ │ │ │ ├── __rt.rs
│ │ │ │ ├── controller.rs
│ │ │ │ ├── descriptor.rs
│ │ │ │ ├── error.rs
│ │ │ │ ├── handler.rs
│ │ │ │ └── mod.rs
│ │ │ ├── tests.proto
│ │ │ ├── tests.rs
│ │ │ ├── web.proto
│ │ │ └── web.rs
│ │ ├── tests
│ │ │ ├── mod.rs
│ │ │ └── three_node.rs
│ │ ├── tunnel
│ │ │ ├── buf.rs
│ │ │ ├── common.rs
│ │ │ ├── filter.rs
│ │ │ ├── insecure_tls.rs
│ │ │ ├── mod.rs
│ │ │ ├── mpsc.rs
│ │ │ ├── packet_def.rs
│ │ │ ├── quic.rs
│ │ │ ├── ring.rs
│ │ │ ├── stats.rs
│ │ │ ├── tcp.rs
│ │ │ ├── udp.rs
│ │ │ ├── websocket.rs
│ │ │ └── wireguard.rs
│ │ ├── utils.rs
│ │ ├── vpn_portal
│ │ │ ├── mod.rs
│ │ │ └── wireguard.rs
│ │ └── web_client
│ │ │ ├── controller.rs
│ │ │ ├── mod.rs
│ │ │ └── session.rs
│ └── third_party
│ │ ├── Packet.dll
│ │ ├── Packet.lib
│ │ ├── arm64
│ │ ├── Packet.dll
│ │ ├── Packet.lib
│ │ └── wintun.dll
│ │ ├── i686
│ │ ├── Packet.dll
│ │ ├── Packet.lib
│ │ └── wintun.dll
│ │ └── wintun.dll
└── src
│ ├── api
│ ├── firewall.rs
│ ├── hops.rs
│ ├── mod.rs
│ └── simple.rs
│ ├── frb_generated.rs
│ └── lib.rs
├── rust_builder
├── .gitignore
├── README.md
├── android
│ ├── .gitignore
│ ├── build.gradle
│ ├── settings.gradle
│ └── src
│ │ └── main
│ │ └── AndroidManifest.xml
├── cargokit
│ ├── .gitignore
│ ├── LICENSE
│ ├── README
│ ├── build_pod.sh
│ ├── build_tool
│ │ ├── README.md
│ │ ├── analysis_options.yaml
│ │ ├── bin
│ │ │ └── build_tool.dart
│ │ ├── lib
│ │ │ ├── build_tool.dart
│ │ │ └── src
│ │ │ │ ├── android_environment.dart
│ │ │ │ ├── artifacts_provider.dart
│ │ │ │ ├── build_cmake.dart
│ │ │ │ ├── build_gradle.dart
│ │ │ │ ├── build_pod.dart
│ │ │ │ ├── build_tool.dart
│ │ │ │ ├── builder.dart
│ │ │ │ ├── cargo.dart
│ │ │ │ ├── crate_hash.dart
│ │ │ │ ├── environment.dart
│ │ │ │ ├── logging.dart
│ │ │ │ ├── options.dart
│ │ │ │ ├── precompile_binaries.dart
│ │ │ │ ├── rustup.dart
│ │ │ │ ├── target.dart
│ │ │ │ ├── util.dart
│ │ │ │ └── verify_binaries.dart
│ │ ├── pubspec.lock
│ │ └── pubspec.yaml
│ ├── cmake
│ │ ├── cargokit.cmake
│ │ └── resolve_symlinks.ps1
│ ├── gradle
│ │ └── plugin.gradle
│ ├── run_build_tool.cmd
│ └── run_build_tool.sh
├── ios
│ ├── Classes
│ │ └── dummy_file.c
│ └── rust_lib_astral.podspec
├── linux
│ └── CMakeLists.txt
├── macos
│ ├── Classes
│ │ └── dummy_file.c
│ └── rust_lib_astral.podspec
├── pubspec.yaml
└── windows
│ ├── .gitignore
│ └── CMakeLists.txt
├── test_driver
└── integration_test.dart
├── vpn_service_plugin
├── .gitignore
├── .metadata
├── CHANGELOG.md
├── LICENSE
├── README.md
├── analysis_options.yaml
├── android
│ ├── .gitignore
│ ├── build.gradle
│ ├── settings.gradle
│ └── src
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ └── kotlin
│ │ │ └── com
│ │ │ └── plugin
│ │ │ └── vpn_service_plugin
│ │ │ ├── TauriVpnService.kt
│ │ │ └── VpnServicePlugin.kt
│ │ └── test
│ │ └── kotlin
│ │ └── com
│ │ └── example
│ │ └── vpn_service_plugin
│ │ └── VpnServicePluginTest.kt
├── example
│ ├── .gitignore
│ ├── README.md
│ ├── analysis_options.yaml
│ ├── android
│ │ ├── .gitignore
│ │ ├── app
│ │ │ ├── build.gradle.kts
│ │ │ └── src
│ │ │ │ ├── debug
│ │ │ │ └── AndroidManifest.xml
│ │ │ │ ├── main
│ │ │ │ ├── AndroidManifest.xml
│ │ │ │ ├── kotlin
│ │ │ │ │ └── com
│ │ │ │ │ │ └── example
│ │ │ │ │ │ └── vpn_service_plugin_example
│ │ │ │ │ │ └── MainActivity.kt
│ │ │ │ └── res
│ │ │ │ │ ├── drawable-v21
│ │ │ │ │ └── launch_background.xml
│ │ │ │ │ ├── drawable
│ │ │ │ │ └── launch_background.xml
│ │ │ │ │ ├── mipmap-hdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ ├── mipmap-mdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ ├── mipmap-xhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ ├── values-night
│ │ │ │ │ └── styles.xml
│ │ │ │ │ └── values
│ │ │ │ │ └── styles.xml
│ │ │ │ └── profile
│ │ │ │ └── AndroidManifest.xml
│ │ ├── build.gradle.kts
│ │ ├── gradle.properties
│ │ ├── gradle
│ │ │ └── wrapper
│ │ │ │ └── gradle-wrapper.properties
│ │ └── settings.gradle.kts
│ ├── integration_test
│ │ └── plugin_integration_test.dart
│ ├── lib
│ │ └── main.dart
│ ├── pubspec.lock
│ ├── pubspec.yaml
│ └── test
│ │ └── widget_test.dart
├── lib
│ ├── vpn_service_plugin.dart
│ ├── vpn_service_plugin_method_channel.dart
│ └── vpn_service_plugin_platform_interface.dart
└── pubspec.yaml
├── web
├── favicon.png
├── icons
│ ├── Icon-192.png
│ ├── Icon-512.png
│ ├── Icon-maskable-192.png
│ └── Icon-maskable-512.png
├── index.html
└── manifest.json
└── windows
├── .gitignore
├── CMakeLists.txt
├── flutter
├── CMakeLists.txt
├── generated_plugin_registrant.cc
├── generated_plugin_registrant.h
└── generated_plugins.cmake
└── runner
├── CMakeLists.txt
├── Runner.rc
├── flutter_window.cpp
├── flutter_window.h
├── main.cpp
├── resource.h
├── resources
└── app_icon.ico
├── runner.exe.manifest
├── utils.cpp
├── utils.h
├── win32_window.cpp
└── win32_window.h
/1:
--------------------------------------------------------------------------------
1 | Theme.of(context).colorScheme.primary // 主要颜色(Primary)
2 | Theme.of(context).colorScheme.onPrimary // 在主要颜色上的颜色(On Primary)
3 | Theme.of(context).colorScheme.primaryContainer // 主要容器颜色(Primary Container)
4 | Theme.of(context).colorScheme.onPrimaryContainer // 在主要容器上的颜色(On Primary Container)
5 |
6 | Theme.of(context).colorScheme.secondary // 次要颜色(Secondary)
7 | Theme.of(context).colorScheme.onSecondary // 在次要颜色上的颜色(On Secondary)
8 | Theme.of(context).colorScheme.secondaryContainer // 次要容器颜色(Secondary Container)
9 | Theme.of(context).colorScheme.onSecondaryContainer // 在次要容器上的颜色(On Secondary Container)
10 |
11 | Theme.of(context).colorScheme.tertiary // 第三颜色(Tertiary)
12 | Theme.of(context).colorScheme.onTertiary // 在第三颜色上的颜色(On Tertiary)
13 | Theme.of(context).colorScheme.tertiaryContainer // 第三容器颜色(Tertiary Container)
14 | Theme.of(context).colorScheme.onTertiaryContainer // 在第三容器上的颜色(On Tertiary Container)
15 |
16 | Theme.of(context).colorScheme.error // 错误颜色(Error)
17 | Theme.of(context).colorScheme.onError // 在错误颜色上的颜色(On Error)
18 | Theme.of(context).colorScheme.errorContainer // 错误容器颜色(Error Container)
19 | Theme.of(context).colorScheme.onErrorContainer // 在错误容器上的颜色(On Error Container)
20 |
21 | Theme.of(context).colorScheme.background // 背景颜色(Background)
22 | Theme.of(context).colorScheme.onBackground // 在背景上的颜色(On Background)
23 |
24 | Theme.of(context).colorScheme.surface // 表面颜色(Surface)
25 | Theme.of(context).colorScheme.onSurface // 在表面上的颜色(On Surface)
26 | Theme.of(context).colorScheme.surfaceVariant // 表面变体颜色(Surface Variant)
27 | Theme.of(context).colorScheme.onSurfaceVariant // 在表面变体上的颜色(On Surface Variant)
28 | Theme.of(context).colorScheme.surfaceTint // 表面色调(Surface Tint)
29 |
30 | Theme.of(context).colorScheme.outline // 轮廓颜色(Outline)
31 | Theme.of(context).colorScheme.shadow // 阴影颜色(Shadow)
32 |
33 | Theme.of(context).colorScheme.inverseSurface // 反转表面颜色(Inverse Surface)
34 | Theme.of(context).colorScheme.onInverseSurface // 在反转表面上的颜色(On Inverse Surface)
35 | Theme.of(context).colorScheme.inversePrimary // 反转主要颜色(Inverse Primary)
--------------------------------------------------------------------------------
/.github/workflows/Stop All Workflows.yaml:
--------------------------------------------------------------------------------
1 | name: Stop All Workflows
2 |
3 | on:
4 | workflow_dispatch: # 手动触发
5 |
6 | permissions:
7 | actions: write # 需要 actions 的写入权限
8 | contents: read # 需要读取仓库内容的权限
9 |
10 | jobs:
11 | stop-workflows:
12 | runs-on: ubuntu-latest
13 | steps:
14 | - name: Checkout code
15 | uses: actions/checkout@v4
16 |
17 | - name: Stop all running workflows
18 | env:
19 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # 使用默认的 GITHUB_TOKEN
20 | run: |
21 | # 获取当前仓库的所有正在运行的工作流
22 | RUNNING_WORKFLOWS=$(curl -s -H "Authorization: Bearer $GITHUB_TOKEN" \
23 | -H "Accept: application/vnd.github.v3+json" \
24 | "https://api.github.com/repos/${{ github.repository }}/actions/runs?status=in_progress" | \
25 | jq -r '.workflow_runs[] | select(.status == "in_progress") | .id')
26 |
27 | # 逐个取消工作流
28 | for RUN_ID in $RUNNING_WORKFLOWS; do
29 | echo "Cancelling workflow run ID: $RUN_ID"
30 | curl -s -X POST -H "Authorization: Bearer $GITHUB_TOKEN" \
31 | -H "Accept: application/vnd.github.v3+json" \
32 | "https://api.github.com/repos/${{ github.repository }}/actions/runs/$RUN_ID/cancel"
33 | done
34 |
--------------------------------------------------------------------------------
/.github/workflows/linux-build.yaml:
--------------------------------------------------------------------------------
1 | name: 🐧 构建 Linux 应用
2 |
3 | on:
4 | workflow_dispatch:
5 | # push:
6 | # tags:
7 | # - 'v*' # 当推送版本标签时触发
8 |
9 | jobs:
10 | build:
11 | runs-on: ubuntu-latest
12 | permissions:
13 | contents: write
14 | packages: write
15 |
16 | steps:
17 | - name: 🛠️ 检出代码仓库
18 | uses: actions/checkout@v4
19 | with:
20 | fetch-depth: 0
21 | lfs: true
22 |
23 | - name: 🦀 设置 Rust 工具链
24 | uses: actions-rs/toolchain@v1
25 | with:
26 | toolchain: stable
27 | override: true
28 |
29 | - name: 📦 安装系统依赖
30 | run: |
31 | sudo apt-get update
32 | sudo apt-get install -y build-essential ninja-build libgtk-3-dev
33 | sudo apt-get install -y clang cmake pkg-config
34 | sudo apt-get install -y liblzma-dev
35 | sudo apt-get install -y libsecret-1-dev libjsoncpp-dev
36 | sudo apt-get install -y protobuf-compiler
37 | # 只安装 ayatana 版本的依赖
38 | sudo apt-get install -y libayatana-appindicator3-dev
39 | # 移除冲突的包
40 | # sudo apt-get install -y libappindicator3-dev
41 |
42 | - name: 🐦 安装 Flutter
43 | run: |
44 | git clone https://github.com/flutter/flutter.git -b stable $HOME/flutter
45 | echo "$HOME/flutter/bin" >> $GITHUB_PATH
46 | export PATH="$HOME/flutter/bin:$PATH"
47 | chmod +x $HOME/flutter/bin/flutter
48 | flutter --version
49 |
50 | - name: 🎯 启用 Linux 桌面支持
51 | run: |
52 | flutter config --enable-linux-desktop
53 | flutter create --platforms=linux .
54 |
55 | - name: 📚 安装 Flutter 依赖
56 | run: |
57 | flutter pub get
58 |
59 | - name: 🛠️ 构建 Linux 应用
60 | run: |
61 | flutter build linux --release
62 |
63 | - name: 📦 打包应用
64 | run: |
65 | mkdir -p release
66 | cd build/linux/x64/release/bundle
67 | tar -czf $GITHUB_WORKSPACE/release/astral-linux-x64.tar.gz *
68 | echo "打包完成,检查文件大小:"
69 | ls -lh $GITHUB_WORKSPACE/release/
70 |
71 | - name: 📤 上传构建产物
72 | uses: actions/upload-artifact@v4
73 | with:
74 | name: linux-release
75 | path: release/astral-linux-x64.tar.gz
76 | retention-days: 7
--------------------------------------------------------------------------------
/.github/workflows/mac-build copy.yaml:
--------------------------------------------------------------------------------
1 | name: 🍎 构建 macOS 应用
2 |
3 | on:
4 | workflow_dispatch:
5 | # push:
6 | # tags:
7 | # - 'v*' # 当推送版本标签时触发
8 |
9 | jobs:
10 | build:
11 | runs-on: self-hosted # 更改为支持 M1 的运行器
12 | permissions:
13 | contents: write
14 | packages: write
15 |
16 | steps:
17 | - name: 🛠️ 检出代码仓库
18 | uses: actions/checkout@v4
19 | with:
20 | fetch-depth: 0
21 | lfs: true
22 |
23 | - name: 🦀 设置 Rust 工具链
24 | uses: actions-rs/toolchain@v1
25 | with:
26 | toolchain: stable
27 | override: true
28 | target: aarch64-apple-darwin # 添加 ARM 架构支持
29 |
30 | - name: 📦 安装系统依赖
31 | run: |
32 | brew update
33 | brew install cmake ninja
34 | brew install protobuf
35 | brew install ruby
36 | brew install cocoapods
37 | export LDFLAGS="-L/opt/homebrew/opt/ruby/lib"
38 | export CPPFLAGS="-I/opt/homebrew/opt/ruby/include"
39 |
40 | - name: 🐦 安装 Flutter
41 | run: |
42 | [ ! -d "$HOME/flutter" ] && git clone https://github.com/flutter/flutter.git -b stable $HOME/flutter
43 | echo "$HOME/flutter/bin" >> $GITHUB_PATH
44 | export PATH="$HOME/flutter/bin:$PATH"
45 | chmod +x $HOME/flutter/bin/flutter
46 | flutter --version
47 |
48 | - name: 🎯 启用 macOS 桌面支持
49 | run: |
50 | flutter config --enable-macos-desktop
51 | flutter create --platforms=macos .
52 |
53 | - name: 📚 安装 Flutter 依赖
54 | run: |
55 | flutter pub get
56 |
57 | - name: 🛠️ 构建 macOS 应用
58 | run: |
59 | flutter build macos --release --dart-define=FLUTTER_APP_ARCH=arm64 # 指定 ARM64 平台
60 |
61 | - name: 📦 打包应用
62 | run: |
63 | mkdir -p release
64 | cd build/macos/Build/Products/Release
65 | # 创建 DMG 文件
66 | hdiutil create -volname "Astral" -srcfolder astral.app -ov -format UDZO $GITHUB_WORKSPACE/release/astral-macos-arm64.dmg
67 | echo "打包完成,检查文件大小:"
68 | ls -lh $GITHUB_WORKSPACE/release/
69 |
70 | - name: 📤 上传构建产物
71 | uses: actions/upload-artifact@v4
72 | with:
73 | name: macos-arm64-release
74 | path: release/astral-macos-arm64.dmg
75 | retention-days: 7
76 |
--------------------------------------------------------------------------------
/.github/workflows/windows-build.yml:
--------------------------------------------------------------------------------
1 | name: 🪟 构建 Windows 应用
2 |
3 | on:
4 | workflow_dispatch:
5 | push: # 每次 push 到仓库时触发
6 | tags:
7 | - 'v*' # 指定触发分支
8 |
9 | jobs:
10 | build:
11 | runs-on: windows-latest
12 |
13 | # 调整 GITHUB_TOKEN 的权限
14 | permissions:
15 | contents: write # 允许写入仓库内容(上传文件)
16 | packages: write
17 |
18 | steps:
19 | - name: 🛠️ 检出代码仓库
20 | uses: actions/checkout@v4
21 |
22 | # 安装Flutter
23 | - name: 🐦 安装Flutter
24 | run: |
25 | echo "正在克隆Flutter稳定分支..."
26 | git clone https://github.com/flutter/flutter.git --branch 3.29.3 $env:GITHUB_WORKSPACE/flutter --depth 1
27 |
28 | echo "正在将Flutter添加到PATH..."
29 | echo "$env:GITHUB_WORKSPACE/flutter/bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
30 |
31 | echo "Flutter版本验证:"
32 | & "$env:GITHUB_WORKSPACE/flutter/bin/flutter.bat" --version
33 | shell: pwsh
34 |
35 | # 安装 Flutter 依赖
36 | - name: 📦 安装 Flutter 依赖
37 | run: |
38 | echo "正在获取 Flutter 依赖..."
39 | flutter pub get
40 | echo "Flutter 依赖安装完成!"
41 | shell: pwsh
42 |
43 | # 编译 Flutter Windows 应用
44 | - name: 🛠️ 编译 Flutter Windows 应用
45 | run: |
46 | echo "正在编译 Flutter Windows Release 版本..."
47 | flutter build windows --release
48 | echo "Flutter Windows Release 编译完成!"
49 | shell: pwsh
50 |
51 |
52 | # 复制 DLL 文件到输出目录
53 | - name: 📂 复制 DLL 文件到 Release 目录
54 | run: |
55 | echo "正在复制 DLL 文件到 Release 目录..."
56 | $dllSourceDir = "$env:GITHUB_WORKSPACE\dlls"
57 | $releaseDir = "$env:GITHUB_WORKSPACE\build\windows\x64\runner\Release"
58 | Copy-Item -Path "$dllSourceDir\*" -Destination $releaseDir -Recurse -Force
59 | echo "DLL 文件已复制到 $releaseDir!"
60 |
61 | # 压缩 Release 目录
62 | - name: 📦 压缩 Release 目录
63 | run: |
64 | echo "正在压缩 Release 目录..."
65 | $releaseDir = "$env:GITHUB_WORKSPACE\build\windows\x64\runner\Release"
66 | $zipFile = "$env:GITHUB_WORKSPACE\astral-windows.zip"
67 | Compress-Archive -Path "$releaseDir\*" -DestinationPath $zipFile -Force
68 | echo "Release 目录已压缩为 $zipFile!"
69 |
70 | # 上传压缩后的 Release 目录作为 Artifact
71 | - name: 📤 上传压缩后的 Release 目录
72 | uses: actions/upload-artifact@v4
73 | with:
74 | name: release
75 | path: ${{ github.workspace }}/astral-windows.zip
76 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .build/
9 | .buildlog/
10 | .history
11 | .svn/
12 | .swiftpm/
13 | migrate_working_dir/
14 |
15 | # IntelliJ related
16 | *.iml
17 | *.ipr
18 | *.iws
19 | .idea/
20 |
21 | # The .vscode folder contains launch configuration and tasks you configure in
22 | # VS Code which you may wish to be included in version control, so this line
23 | # is commented out by default.
24 | #.vscode/
25 |
26 | # Flutter/Dart/Pub related
27 | **/doc/api/
28 | **/ios/Flutter/.last_build_id
29 | .dart_tool/
30 | .flutter-plugins
31 | .flutter-plugins-dependencies
32 | .pub-cache/
33 | .pub/
34 | /build/
35 |
36 | # Symbolication related
37 | app.*.symbols
38 |
39 | # Obfuscation related
40 | app.*.map.json
41 |
42 | # Android Studio will place build artifacts here
43 | /android/app/debug
44 | /android/app/profile
45 | /android/app/release
46 |
47 | # 添加至.gitignore
48 | .env
49 | *.env
50 |
--------------------------------------------------------------------------------
/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: "c23637390482d4cf9598c3ce3f2be31aa7332daf"
8 | channel: "stable"
9 |
10 | project_type: app
11 |
12 | # Tracks metadata for the flutter migrate command
13 | migration:
14 | platforms:
15 | - platform: root
16 | create_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
17 | base_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
18 | - platform: android
19 | create_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
20 | base_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
21 | - platform: linux
22 | create_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
23 | base_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
24 | - platform: web
25 | create_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
26 | base_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
27 | - platform: windows
28 | create_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
29 | base_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
30 |
31 | # User provided section
32 |
33 | # List of Local paths (relative to this file) that should be
34 | # ignored by the migrate tool.
35 | #
36 | # Files that are not part of the templates will be ignored by default.
37 | unmanaged_files:
38 | - 'lib/main.dart'
39 | - 'ios/Runner.xcodeproj/project.pbxproj'
40 |
--------------------------------------------------------------------------------
/.sentry-native/96697942-6dc9-4fd5-e899-18c41b0da851.run.lock:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldoubil/astral/22171ad191cdb0648683de7a019bb7aba085a54c/.sentry-native/96697942-6dc9-4fd5-e899-18c41b0da851.run.lock
--------------------------------------------------------------------------------
/.sentry-native/b221b550-270f-48e3-67d4-65b549f7c944.run.lock:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldoubil/astral/22171ad191cdb0648683de7a019bb7aba085a54c/.sentry-native/b221b550-270f-48e3-67d4-65b549f7c944.run.lock
--------------------------------------------------------------------------------
/.sentry-native/d5bced59-91f4-4c15-d704-bb7d3ae30d05.run.lock:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldoubil/astral/22171ad191cdb0648683de7a019bb7aba085a54c/.sentry-native/d5bced59-91f4-4c15-d704-bb7d3ae30d05.run.lock
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // 使用 IntelliSense 了解相关属性。
3 | // 悬停以查看现有属性的描述。
4 | // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": "Flutter (x86)",
9 | "request": "launch",
10 | "type": "dart",
11 | "flutterMode": "debug",
12 | "args": [
13 | "--no-build",
14 | "--use-application-binary=build/app/outputs/flutter-apk/app-debug.apk"
15 | ]
16 | },
17 | {
18 | "name": "Flutter",
19 | "request": "launch",
20 | "type": "dart",
21 | "flutterMode": "profile"
22 | },
23 | {
24 | "name": "Flutter Run",
25 | "request": "launch",
26 | "type": "dart",
27 | "program": "lib/main.dart",
28 | "args": ["run"]
29 | },
30 | {
31 | "name": "fln2n",
32 | "request": "launch",
33 | "type": "dart"
34 | },
35 | {
36 | "name": "fln2n (profile mode)",
37 | "request": "launch",
38 | "type": "dart",
39 | "flutterMode": "profile"
40 | },
41 | {
42 | "name": "fln2n (release mode)",
43 | "request": "launch",
44 | "type": "dart",
45 | "flutterMode": "release"
46 | },
47 | {
48 | "name": "rust_builder",
49 | "cwd": "rust_builder",
50 | "request": "launch",
51 | "type": "dart"
52 | },
53 | {
54 | "name": "rust_builder (profile mode)",
55 | "cwd": "rust_builder",
56 | "request": "launch",
57 | "type": "dart",
58 | "flutterMode": "profile"
59 | },
60 | {
61 | "name": "rust_builder (release mode)",
62 | "cwd": "rust_builder",
63 | "request": "launch",
64 | "type": "dart",
65 | "flutterMode": "release"
66 | },
67 | {
68 | "name": "build_tool",
69 | "cwd": "rust_builder\\cargokit\\build_tool",
70 | "request": "launch",
71 | "type": "dart"
72 | }
73 | ]
74 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Astral [](https://deepwiki.com/ldoubil/astral)
2 |
3 | ### 重要声明
4 |
5 | ⚠️ 本软件仅供学习和非商业用途使用
6 | - ❌ 禁止商业使用 - 本软件不得用于任何商业目的
7 | - ❌ 禁止修改软件名称 - 不得更改 "Astral" 软件名称试图混淆以及二次售卖
8 | - ✅ 保留原作者信息 - 必须保留所有原作者版权信息
9 | - ✅ 开源协议 - 遵循开源许可证条款
10 |
11 | ## 项目简介
12 |
13 | Astral 是一个基于 EasyTier 的跨平台网络应用,提供简单易用的 P2P 网络连接和 VPN 服务。通过 Flutter 构建的现代化界面,让用户能够轻松创建和管理虚拟网络。
14 |
15 | ## 主要特性
16 |
17 | - 🌐 **P2P 网络连接** - 基于 EasyTier 的去中心化网络架构
18 | - 🔒 **VPN 服务** - 安全的虚拟专用网络连接
19 | - 🖥️ **跨平台支持** - 支持 Windows、macOS、Linux、Android 和 iOS
20 | - 🎨 **现代化界面** - 基于 Flutter 的美观用户界面
21 | - ⚡ **高性能** - Rust 后端确保高效的网络处理
22 | - 🔧 **易于配置** - 简单的房间和服务器管理
23 |
24 | ## 技术栈
25 |
26 | - **前端**: Flutter (Dart)
27 | - **后端**: Rust (EasyTier)
28 | - **网络**: P2P、VPN、WireGuard
29 | - **平台**: Windows、macOS、Linux、Android、iOS
30 |
31 | ## 功能说明
32 |
33 | - 房间管理 - 创建和加入网络房间
34 | - 服务器配置 - 配置和管理网络服务器
35 | - 用户管理 - 查看和管理网络用户
36 | - 网络设置 - 自定义网络参数和配置
37 |
38 | ## 联系方式
39 |
40 | - QQ群:808169040
41 | - 项目文档:
42 |
43 |
44 |
--------------------------------------------------------------------------------
/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | # This file configures the analyzer, which statically analyzes Dart code to
2 | # check for errors, warnings, and lints.
3 | #
4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled
5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
6 | # invoked from the command line by running `flutter analyze`.
7 |
8 | # The following line activates a set of recommended lints for Flutter apps,
9 | # packages, and plugins designed to encourage good coding practices.
10 | analyzer:
11 | errors:
12 | collection_methods_unrelated_type: ignore
13 | file_names: ignore
14 | non_constant_identifier_names: ignore
15 | include: package:flutter_lints/flutter.yaml
16 |
17 | linter:
18 | # The lint rules applied to this project can be customized in the
19 | # section below to disable rules from the `package:flutter_lints/flutter.yaml`
20 | # included above or to enable additional rules. A list of all available lints
21 | # and their documentation is published at https://dart.dev/lints.
22 | #
23 | # Instead of disabling a lint rule for the entire project in the
24 | # section below, it can also be suppressed for a single line of code
25 | # or a specific dart file by using the `// ignore: name_of_lint` and
26 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file
27 | # producing the lint.
28 | rules:
29 | # avoid_print: false # Uncomment to disable the `avoid_print` rule
30 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
31 |
32 | # Additional information about this file can be found at
33 | # https://dart.dev/guides/language/analysis-options
34 |
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /local.properties
7 | GeneratedPluginRegistrant.java
8 | .cxx/
9 |
10 | # Remember to never publicly share your keystore.
11 | # See https://flutter.dev/to/reference-keystore
12 | key.properties
13 | **/*.keystore
14 | **/*.jks
15 |
--------------------------------------------------------------------------------
/android/app/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import java.util.Properties
2 | import java.io.FileInputStream
3 |
4 | plugins {
5 | id("com.android.application")
6 | id("kotlin-android")
7 | // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
8 | id("dev.flutter.flutter-gradle-plugin")
9 | }
10 |
11 | val keystoreProperties = Properties()
12 | val keystorePropertiesFile = rootProject.file("key.properties")
13 | if (keystorePropertiesFile.exists()) {
14 | keystoreProperties.load(FileInputStream(keystorePropertiesFile))
15 | }
16 |
17 | android {
18 | namespace = "com.kevin.astral"
19 | compileSdk = flutter.compileSdkVersion
20 | // ndkVersion = flutter.ndkVersion
21 | ndkVersion = "27.2.12479018"
22 |
23 | compileOptions {
24 | sourceCompatibility = JavaVersion.VERSION_11
25 | targetCompatibility = JavaVersion.VERSION_11
26 | // 启用 core library desugaring
27 | isCoreLibraryDesugaringEnabled = true
28 | }
29 |
30 | kotlinOptions {
31 | jvmTarget = JavaVersion.VERSION_11.toString()
32 | }
33 | // 添加 buildFeatures 配置
34 | buildFeatures {
35 | buildConfig = true
36 | }
37 |
38 | defaultConfig {
39 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
40 | applicationId = "com.kevin.astral"
41 | // You can update the following values to match your application needs.
42 | // For more information, see: https://flutter.dev/to/review-gradle-config.
43 | minSdk = flutter.minSdkVersion
44 | targetSdk = flutter.targetSdkVersion
45 | versionCode = flutter.versionCode
46 | versionName = flutter.versionName
47 | }
48 |
49 | signingConfigs {
50 | create("release") {
51 | keyAlias = keystoreProperties["keyAlias"] as String
52 | keyPassword = keystoreProperties["keyPassword"] as String
53 | storeFile = keystoreProperties["storeFile"]?.let { file(it) }
54 | storePassword = keystoreProperties["storePassword"] as String
55 | }
56 | }
57 |
58 | buildTypes {
59 | release {
60 | signingConfig = signingConfigs.getByName("release")
61 | // signingConfig = signingConfigs.getByName("debug")
62 |
63 | }
64 | }
65 |
66 | }
67 |
68 | // 添加 dependencies 块
69 | dependencies {
70 | coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4")
71 | }
72 |
73 | flutter {
74 | source = "../.."
75 | }
76 |
--------------------------------------------------------------------------------
/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/app/src/main/kotlin/com/example/astral/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.kevin.astral
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity : FlutterActivity()
6 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-v21/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/splash_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldoubil/astral/22171ad191cdb0648683de7a019bb7aba085a54c/android/app/src/main/res/drawable/splash_logo.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldoubil/astral/22171ad191cdb0648683de7a019bb7aba085a54c/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldoubil/astral/22171ad191cdb0648683de7a019bb7aba085a54c/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldoubil/astral/22171ad191cdb0648683de7a019bb7aba085a54c/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldoubil/astral/22171ad191cdb0648683de7a019bb7aba085a54c/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldoubil/astral/22171ad191cdb0648683de7a019bb7aba085a54c/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/values-night/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/build.gradle.kts:
--------------------------------------------------------------------------------
1 | allprojects {
2 | repositories {
3 | google()
4 | mavenCentral()
5 | }
6 | }
7 |
8 | val newBuildDir: Directory = rootProject.layout.buildDirectory.dir("../../build").get()
9 | rootProject.layout.buildDirectory.value(newBuildDir)
10 |
11 | subprojects {
12 | val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name)
13 | project.layout.buildDirectory.value(newSubprojectBuildDir)
14 | }
15 | subprojects {
16 | project.evaluationDependsOn(":app")
17 | }
18 |
19 | tasks.register("clean") {
20 | delete(rootProject.layout.buildDirectory)
21 | }
22 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | zipStoreBase=GRADLE_USER_HOME
4 | zipStorePath=wrapper/dists
5 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip
6 |
--------------------------------------------------------------------------------
/android/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | val flutterSdkPath = run {
3 | val properties = java.util.Properties()
4 | file("local.properties").inputStream().use { properties.load(it) }
5 | val flutterSdkPath = properties.getProperty("flutter.sdk")
6 | require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" }
7 | flutterSdkPath
8 | }
9 |
10 | includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
11 |
12 | repositories {
13 | google()
14 | mavenCentral()
15 | gradlePluginPortal()
16 | }
17 | }
18 |
19 | plugins {
20 | id("dev.flutter.flutter-plugin-loader") version "1.0.0"
21 | id("com.android.application") version "8.7.0" apply false
22 | id("org.jetbrains.kotlin.android") version "1.8.22" apply false
23 | }
24 |
25 | include(":app")
26 |
--------------------------------------------------------------------------------
/assets/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldoubil/astral/22171ad191cdb0648683de7a019bb7aba085a54c/assets/icon.ico
--------------------------------------------------------------------------------
/devtools_options.yaml:
--------------------------------------------------------------------------------
1 | description: This file stores settings for Dart & Flutter DevTools.
2 | documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
3 | extensions:
4 |
--------------------------------------------------------------------------------
/dlls/Ak.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldoubil/astral/22171ad191cdb0648683de7a019bb7aba085a54c/dlls/Ak.dll
--------------------------------------------------------------------------------
/dlls/Packet.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldoubil/astral/22171ad191cdb0648683de7a019bb7aba085a54c/dlls/Packet.dll
--------------------------------------------------------------------------------
/dlls/Packet.lib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldoubil/astral/22171ad191cdb0648683de7a019bb7aba085a54c/dlls/Packet.lib
--------------------------------------------------------------------------------
/dlls/arm64/Packet.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldoubil/astral/22171ad191cdb0648683de7a019bb7aba085a54c/dlls/arm64/Packet.dll
--------------------------------------------------------------------------------
/dlls/arm64/Packet.lib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldoubil/astral/22171ad191cdb0648683de7a019bb7aba085a54c/dlls/arm64/Packet.lib
--------------------------------------------------------------------------------
/dlls/arm64/wintun.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldoubil/astral/22171ad191cdb0648683de7a019bb7aba085a54c/dlls/arm64/wintun.dll
--------------------------------------------------------------------------------
/dlls/i686/Packet.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldoubil/astral/22171ad191cdb0648683de7a019bb7aba085a54c/dlls/i686/Packet.dll
--------------------------------------------------------------------------------
/dlls/i686/Packet.lib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldoubil/astral/22171ad191cdb0648683de7a019bb7aba085a54c/dlls/i686/Packet.lib
--------------------------------------------------------------------------------
/dlls/i686/wintun.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldoubil/astral/22171ad191cdb0648683de7a019bb7aba085a54c/dlls/i686/wintun.dll
--------------------------------------------------------------------------------
/dlls/wintun.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldoubil/astral/22171ad191cdb0648683de7a019bb7aba085a54c/dlls/wintun.dll
--------------------------------------------------------------------------------
/flutter_rust_bridge.yaml:
--------------------------------------------------------------------------------
1 | rust_input: crate::api
2 | rust_root: rust/
3 | dart_output: lib/src/rust
--------------------------------------------------------------------------------
/fonts/MiSans-Demibold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldoubil/astral/22171ad191cdb0648683de7a019bb7aba085a54c/fonts/MiSans-Demibold.ttf
--------------------------------------------------------------------------------
/fonts/MiSans-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldoubil/astral/22171ad191cdb0648683de7a019bb7aba085a54c/fonts/MiSans-Regular.ttf
--------------------------------------------------------------------------------
/integration_test/simple_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:astral/app.dart';
2 | import 'package:flutter_test/flutter_test.dart';
3 | import 'package:astral/src/rust/frb_generated.dart';
4 | import 'package:integration_test/integration_test.dart';
5 |
6 | void main() {
7 | IntegrationTestWidgetsFlutterBinding.ensureInitialized();
8 | setUpAll(() async => await RustLib.init());
9 | testWidgets('Can call rust function', (WidgetTester tester) async {
10 | await tester.pumpWidget(const KevinApp());
11 | expect(find.textContaining('Result: `Hello, Tom!`'), findsOneWidget);
12 | });
13 | }
14 |
--------------------------------------------------------------------------------
/lib/app.dart:
--------------------------------------------------------------------------------
1 | import 'package:astral/fun/net_astral_udp.dart';
2 | import 'package:astral/screens/main_screen.dart';
3 | import 'package:flutter/material.dart';
4 | import 'package:astral/k/app_s/aps.dart';
5 | import 'package:flutter_localizations/flutter_localizations.dart';
6 | // 仅在桌面平台导入系统托盘
7 |
8 | class KevinApp extends StatefulWidget {
9 | const KevinApp({super.key});
10 | @override
11 | State createState() => _KevinAppState();
12 | }
13 |
14 | class _KevinAppState extends State {
15 | final _aps = Aps();
16 |
17 | @override
18 | void initState() {
19 | super.initState();
20 | getIpv4AndIpV6Addresses();
21 | }
22 |
23 | @override
24 | void dispose() {
25 | super.dispose();
26 | }
27 |
28 | @override
29 | Widget build(BuildContext context) {
30 | return MaterialApp(
31 | localizationsDelegates: const [
32 | // 添加国际化支持
33 | GlobalMaterialLocalizations.delegate,
34 | GlobalWidgetsLocalizations.delegate,
35 | GlobalCupertinoLocalizations.delegate,
36 | ],
37 | // Insert this line
38 | supportedLocales: const [Locale("zh", "CN"), Locale("en", "US")],
39 | theme: ThemeData(
40 | useMaterial3: true,
41 | colorSchemeSeed: _aps.themeColor.watch(context), // 设置当前主题颜色,
42 | brightness: Brightness.light,
43 | ).copyWith(
44 | textTheme: Typography.material2021().black.apply(fontFamily: 'MiSans'),
45 | primaryTextTheme: Typography.material2021().black.apply(
46 | fontFamily: 'MiSans',
47 | ),
48 | ),
49 | darkTheme: ThemeData(
50 | useMaterial3: true,
51 | colorSchemeSeed: _aps.themeColor.watch(context),
52 | brightness: Brightness.dark,
53 | ).copyWith(
54 | textTheme: Typography.material2021().white.apply(fontFamily: 'MiSans'),
55 | primaryTextTheme: Typography.material2021().white.apply(
56 | fontFamily: 'MiSans',
57 | ),
58 | ),
59 | themeMode: _aps.themeMode.watch(context), // 设置当前主题模式
60 | home: MainScreen(),
61 | );
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/lib/fun/net_astral_udp.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 | import 'dart:io';
3 | import 'package:astral/k/app_s/aps.dart';
4 | import 'package:flutter/foundation.dart';
5 |
6 | Future getIpv4AndIpV6Addresses() async {
7 | try {
8 | final client = HttpClient();
9 | final request = await client.getUrl(Uri.parse('https://ipw.cn/'));
10 | final response = await request.close();
11 | if (response.statusCode == HttpStatus.ok) {
12 | print('Failed to get public IPv6: HTTP ${response.statusCode}');
13 | final publicIPv6 = await response.transform(utf8.decoder).join();
14 | if (publicIPv6.isNotEmpty) {
15 | Aps().ipv6.value = response.statusCode.toString();
16 | }
17 | }
18 | } catch (e) {
19 | print('Error fetching public IPv6: $e');
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/lib/fun/random_name.dart:
--------------------------------------------------------------------------------
1 | import 'dart:math';
2 |
3 | const List prefixes = [
4 | '月光',
5 | '星云',
6 | '量子',
7 | '琉璃',
8 | '蒸汽',
9 | '镜像',
10 | '蜂巢',
11 | '极光',
12 | '云端',
13 | '琥珀',
14 | '棱镜',
15 | '螺旋',
16 | '黄昏',
17 | '黎光',
18 | '数据',
19 | '像素',
20 | '立方',
21 | '穹顶',
22 | '虫洞',
23 | ' fractal',
24 | '相位',
25 | '回声',
26 | '虚像',
27 | '纳米',
28 | '拓扑',
29 | '弦月',
30 | '翡翠',
31 | '静滞',
32 | '折叠',
33 | '观星',
34 | '粒子',
35 | '相位',
36 | '虹膜',
37 | '熵减',
38 | '拓扑',
39 | '全息',
40 | '磁浮',
41 | '反物质',
42 | '曲速',
43 | '克莱因',
44 | ];
45 |
46 | const List middles = [
47 | '会议室',
48 | '作战室',
49 | '温室',
50 | '档案馆',
51 | '核心',
52 | '回廊',
53 | '枢纽',
54 | '大厅',
55 | '观测台',
56 | '实验室',
57 | '咖啡角',
58 | '舰桥',
59 | '冥想间',
60 | '工坊',
61 | '沙盘',
62 | '剧场',
63 | '花房',
64 | '终端',
65 | '矩阵',
66 | '蜂巢',
67 | '图书馆',
68 | '发射井',
69 | '熔炉',
70 | '停机坪',
71 | '禁闭室',
72 | '培养舱',
73 | '画廊',
74 | '反应堆',
75 | '棱镜',
76 | '暗房',
77 | '跃迁舱',
78 | '服务器',
79 | '投影间',
80 | '解压舱',
81 | '温室',
82 | '指挥台',
83 | '孵化器',
84 | '天井',
85 | '祭坛',
86 | '回响室',
87 | ];
88 | // 创建一个随机数生成器实例
89 | final Random _random = Random();
90 |
91 | /// 生成一个随机名称,由一个前缀和一个中间部分组成
92 | String RandomName() {
93 | // 随机选择一个前缀
94 | final String prefix = prefixes[_random.nextInt(prefixes.length)];
95 | // 随机选择一个中间部分
96 | final String middle = middles[_random.nextInt(middles.length)];
97 |
98 | // 组合并返回名称
99 | return '$prefix$middle';
100 | }
101 |
--------------------------------------------------------------------------------
/lib/fun/show_add_room_dialog.dart:
--------------------------------------------------------------------------------
1 | import 'package:astral/fun/random_name.dart';
2 | import 'package:astral/screens/room_page.dart';
3 | import 'package:flutter/material.dart';
4 |
5 | Future showAddRoomDialog(BuildContext context) async {
6 | bool isEncrypted = true;
7 | String? name = RandomName();
8 | String? roomName;
9 | String? roomPassword;
10 |
11 | await showDialog(
12 | context: context,
13 | builder: (context) {
14 | return StatefulBuilder(
15 | builder: (context, setState) {
16 | return AlertDialog(
17 | title: const Text('添加房间'),
18 | content: Column(
19 | mainAxisSize: MainAxisSize.min,
20 | children: [
21 | TextField(
22 | controller: TextEditingController(text: name),
23 | decoration: const InputDecoration(labelText: '房间名称'),
24 | // 当文本字段内容改变时,同步更新外部 'name' 变量
25 | onChanged: (value) => name = value,
26 | ),
27 | const SizedBox(height: 8),
28 | SwitchListTile(
29 | title: const Text('是否保护'),
30 | value: isEncrypted,
31 | onChanged: (value) {
32 | setState(() {
33 | isEncrypted = value;
34 | });
35 | },
36 | ),
37 |
38 | if (!isEncrypted) ...[
39 | TextField(
40 | decoration: const InputDecoration(labelText: '房间号'),
41 | onChanged: (value) => roomName = value,
42 | ),
43 | const SizedBox(height: 8),
44 | TextField(
45 | decoration: const InputDecoration(labelText: '房间密码'),
46 | onChanged: (value) => roomPassword = value,
47 | ),
48 | ],
49 | ],
50 | ),
51 | actions: [
52 | TextButton(
53 | onPressed: () => Navigator.of(context).pop(),
54 | child: const Text('取消'),
55 | ),
56 | TextButton(
57 | onPressed: () {
58 | addEncryptedRoom(
59 | isEncrypted,
60 | name ?? RandomName(),
61 | roomName ?? "",
62 | roomPassword ?? "",
63 | );
64 | Navigator.of(context).pop();
65 | },
66 | child: const Text('确定'),
67 | ),
68 | ],
69 | );
70 | },
71 | );
72 | },
73 | );
74 | }
75 |
--------------------------------------------------------------------------------
/lib/fun/show_edit_room_dialog.dart:
--------------------------------------------------------------------------------
1 | import 'package:astral/k/app_s/aps.dart';
2 | import 'package:astral/k/models/room.dart';
3 | import 'package:flutter/material.dart';
4 |
5 | Future showEditRoomDialog(
6 | BuildContext context, {
7 | required Room room,
8 | }) async {
9 | String? name = room.name;
10 |
11 | await showDialog(
12 | context: context,
13 | builder: (context) {
14 | return AlertDialog(
15 | title: const Text('编辑房间'),
16 | content: Column(
17 | mainAxisSize: MainAxisSize.min,
18 | children: [
19 | TextField(
20 | controller: TextEditingController(text: name),
21 | decoration: const InputDecoration(labelText: '房间名称'),
22 | onChanged: (value) => room.name = value,
23 | ),
24 | const SizedBox(height: 8),
25 | // 显示房间类型(只读)
26 | ListTile(
27 | title: const Text('房间类型'),
28 | subtitle: Text(room.encrypted ? '加密房间' : '普通房间'),
29 | ),
30 | if (!room.encrypted) ...[
31 | TextField(
32 | controller: TextEditingController(text: room.roomName),
33 | decoration: const InputDecoration(labelText: '房间号'),
34 | onChanged: (value) => room.roomName = value,
35 | ),
36 | const SizedBox(height: 8),
37 | TextField(
38 | controller: TextEditingController(text: room.password),
39 | decoration: const InputDecoration(labelText: '房间密码'),
40 | onChanged: (value) => room.password = value,
41 | ),
42 | ],
43 | ],
44 | ),
45 | actions: [
46 | TextButton(
47 | onPressed: () => Navigator.of(context).pop(),
48 | child: const Text('取消'),
49 | ),
50 | TextButton(
51 | onPressed: () {
52 | Aps().updateRoom(room);
53 | Navigator.of(context).pop();
54 | },
55 | child: const Text('确定'),
56 | ),
57 | ],
58 | );
59 | },
60 | );
61 | }
62 |
--------------------------------------------------------------------------------
/lib/k/database/app_data.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 | import 'package:astral/k/models/all_settings.dart';
3 | import 'package:astral/k/models/net_config.dart';
4 | import 'package:astral/k/models/room.dart';
5 | import 'package:astral/k/models/rule_group.dart';
6 | import 'package:astral/k/models/server_mod.dart';
7 | import 'package:astral/k/models_mod/all_settings_cz.dart';
8 | import 'package:astral/k/models_mod/net_config_cz.dart';
9 | import 'package:astral/k/models_mod/room_cz.dart';
10 | import 'package:astral/k/models_mod/server_cz.dart';
11 | import 'package:isar/isar.dart';
12 | import 'package:astral/k/models/theme_settings.dart';
13 | import 'package:astral/k/models_mod/theme_settings_cz.dart';
14 | import 'package:path_provider/path_provider.dart';
15 | import 'package:path/path.dart' as path;
16 |
17 | class AppDatabase {
18 | static final AppDatabase _instance = AppDatabase._internal();
19 | factory AppDatabase() => _instance;
20 | AppDatabase._internal();
21 |
22 | late final Isar isar;
23 | late final ThemeSettingsRepository themeSettings;
24 | late final NetConfigRepository netConfigSetting;
25 | late final RoomCz RoomSetting;
26 | late final AllSettingsCz AllSettings;
27 | late final ServerCz ServerSetting;
28 |
29 | /// 初始化数据库
30 | Future init([String? customDbDir]) async {
31 | late final String dbDir;
32 |
33 | if (customDbDir != null) {
34 | // 使用自定义数据库目录
35 | dbDir = customDbDir;
36 | } else if (Platform.isAndroid) {
37 | // Android平台使用应用专属目录
38 | final appDocDir = await getApplicationDocumentsDirectory();
39 | dbDir = Directory(path.join(appDocDir.path, 'db')).path;
40 | } else {
41 | // 其他平台使用可执行文件所在目录
42 | final executablePath = Platform.resolvedExecutable;
43 | final executableDir = Directory(executablePath).parent.path;
44 | dbDir = Directory(path.join(executableDir, 'data', 'db')).path;
45 | }
46 |
47 | // 确保数据库目录存在
48 | await Directory(dbDir).create(recursive: true);
49 | isar = await Isar.open([
50 | ThemeSettingsSchema,
51 | NetConfigSchema,
52 | RoomSchema,
53 | AllSettingsSchema,
54 | ServerModSchema,
55 | ], directory: dbDir);
56 | themeSettings = ThemeSettingsRepository(isar);
57 | netConfigSetting = NetConfigRepository(isar);
58 | RoomSetting = RoomCz(isar);
59 | AllSettings = AllSettingsCz(isar);
60 | ServerSetting = ServerCz(isar);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/lib/k/mod/window_manager.dart:
--------------------------------------------------------------------------------
1 | import 'package:astral/fun/reg.dart';
2 | import 'package:window_manager/window_manager.dart';
3 | import 'dart:io';
4 | import 'package:flutter/material.dart';
5 | import 'package:astral/k/app_s/aps.dart';
6 |
7 | class WindowManagerUtils {
8 | static Future initializeWindow() async {
9 | // 检查当前平台是否为 Windows、MacOS 或 Linux
10 | if (Platform.isWindows || Platform.isMacOS || Platform.isLinux) {
11 | // 确保窗口管理器已初始化
12 | await windowManager.ensureInitialized();
13 | //添加信号监听
14 | // 创建响应式效果,用于监听和更新窗口标题
15 | effect(() {
16 | // 设置窗口标题为当前应用名称
17 | windowManager.setTitle(Aps().appName.value);
18 | });
19 | // 定义窗口选项配置
20 | final windowOptions = WindowOptions(
21 | size: Size(960, 540),
22 | // 设置窗口最小大小为 300x300
23 | minimumSize: Size(200, 300),
24 | // 设置窗口居中显示
25 | center: true,
26 | // 设置窗口标题
27 | title: Aps().appName.value,
28 | // 设置标题栏样式为隐藏
29 | titleBarStyle: TitleBarStyle.hidden,
30 | // 设置窗口背景为透明
31 | backgroundColor: Colors.transparent,
32 | // 设置是否在任务栏显示
33 | skipTaskbar: false,
34 | );
35 |
36 | // 等待窗口准备就绪并显示
37 | await windowManager.waitUntilReadyToShow(windowOptions, () async {
38 | // 如果 startupMinimize 为 true,则最小化窗口
39 | if (Aps().startupMinimize.value) {
40 | await windowManager.hide();
41 | } else {
42 | await windowManager.show();
43 | await windowManager.focus();
44 | }
45 | });
46 |
47 | if (Aps().startup.value) {
48 | handleStartupSetting(true);
49 | }
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/lib/k/models/all_settings.dart:
--------------------------------------------------------------------------------
1 | import 'package:isar/isar.dart';
2 | part 'all_settings.g.dart';
3 |
4 | @collection
5 | class AllSettings {
6 | /// 主键ID,固定为1因为只需要一个实例
7 | Id id = 1;
8 |
9 | /// 当前启用的房间
10 | int? room;
11 |
12 | /// 玩家名称
13 | String? playerName;
14 |
15 | /// 监听列表
16 | List? listenList;
17 |
18 | /// 自定义vpn网段
19 | List customVpn = [];
20 |
21 | ///用户列表简约模式
22 | bool userListSimple = true;
23 |
24 | /// 关闭最小化到托盘
25 | bool closeMinimize = true;
26 |
27 | /// 开机自启
28 | bool startup = false;
29 |
30 | /// 启动后最小化
31 | bool startupMinimize = false;
32 |
33 | /// 启动后自动连接
34 | bool startupAutoConnect = false;
35 |
36 | /// 自动设置网卡跃点
37 | bool autoSetMTU = true;
38 |
39 | /// 参与测试版
40 | bool beta = false;
41 |
42 | /// 自动检查更新
43 | bool autoCheckUpdate = true;
44 |
45 | /// 下载加速
46 | String downloadAccelerate = 'https://gh.xmly.dev/';
47 | }
48 |
--------------------------------------------------------------------------------
/lib/k/models/net_config.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 | import 'package:isar/isar.dart';
3 | part 'net_config.g.dart';
4 |
5 | @embedded
6 | class ConnectionInfo {
7 | late String bindAddr;
8 | late String dstAddr;
9 | late String proto;
10 | ConnectionInfo() {
11 | bindAddr = '';
12 | dstAddr = '';
13 | proto = '';
14 | }
15 | }
16 |
17 | @embedded
18 | class ConnectionManager {
19 | late String name; // 分组名称
20 | late List connections;
21 | late bool enabled;
22 | ConnectionManager() {
23 | name = '';
24 | connections = [];
25 | enabled = false;
26 | }
27 | }
28 |
29 | @collection
30 | class NetConfig {
31 | /// 主键ID,固定为1因为只需要一个实例
32 | Id id = 1;
33 |
34 | String netns = ''; // 网络命名空间
35 |
36 | String hostname = Platform.localHostname; // 主机名
37 |
38 | String instance_name = 'default'; // 实例名称
39 |
40 | String ipv4 = ''; // IPv4地址
41 |
42 | bool dhcp = true; // 是否使用DHCP
43 | String network_name = ''; // 网络名称
44 | String network_secret = ''; // 网络密钥
45 |
46 | List listeners = []; // 监听端口
47 |
48 | List peer = []; // 服务器节点地址
49 |
50 | // 子网代理
51 | List cidrproxy = []; // 代理地址
52 |
53 | // 转发配置
54 | List connectionManagers = [];
55 | /// 默认协议
56 | String default_protocol = 'tcp'; //x
57 |
58 | /// 设备名称
59 | String dev_name = '';
60 |
61 | /// 是否启用加密
62 | bool enable_encryption = true; //x
63 |
64 | /// 是否启用IPv6
65 | bool enable_ipv6 = true;
66 |
67 | /// 最大传输单元
68 | int mtu = 1360; //x
69 |
70 | /// 是否优先考虑延迟
71 | bool latency_first = false; //x
72 |
73 | /// 是否启用出口节点
74 | bool enable_exit_node = false; //x
75 |
76 | /// 是否禁用TUN设备
77 | bool no_tun = false; //x
78 |
79 | /// 是否使用smoltcp网络栈
80 | bool use_smoltcp = false; //x
81 |
82 | /// 中继网络白名单
83 | String relay_network_whitelist = '*';
84 |
85 | /// 是否禁用P2P
86 | bool disable_p2p = false; //x
87 |
88 | /// 是否中继所有对等RPC
89 | bool relay_all_peer_rpc = false; //x
90 |
91 | /// 是否禁用UDP打洞
92 | bool disable_udp_hole_punching = false; //x
93 |
94 | /// 是否启用多线程
95 | bool multi_thread = true; //x
96 |
97 | /// 数据压缩算法
98 | int data_compress_algo = 1; //x
99 |
100 | /// 是否绑定设备
101 | bool bind_device = true; //x
102 |
103 | /// 是否启用KCP代理
104 | bool enable_kcp_proxy = true; //x
105 |
106 | /// 是否禁用KCP输入
107 | bool disable_kcp_input = false; //x
108 |
109 | /// 是否禁用中继KCP
110 | bool disable_relay_kcp = true; //x
111 |
112 | /// 是否使用系统代理转发
113 | bool proxy_forward_by_system = false; //x
114 |
115 | /// accept_dns 魔术DNS
116 | bool accept_dns = false; //x
117 | }
118 |
--------------------------------------------------------------------------------
/lib/k/models/room.dart:
--------------------------------------------------------------------------------
1 | import 'package:isar/isar.dart';
2 | part 'room.g.dart';
3 |
4 | @collection
5 | class Room {
6 | /// 主键自增
7 | Id id = Isar.autoIncrement;
8 | String name = ""; // 房间别名
9 | // 是否加密
10 | bool encrypted = false;
11 | //房间名称
12 | String roomName = "";
13 | // 房间密码
14 | String password = "";
15 | // 房间标签
16 | List tags = [];
17 |
18 | //构造
19 | Room({
20 | this.id = Isar.autoIncrement,
21 | this.name = "",
22 | this.encrypted = false,
23 | this.roomName = "",
24 | this.password = "",
25 | this.tags = const [],
26 | });
27 | }
28 |
--------------------------------------------------------------------------------
/lib/k/models/rule_group.dart:
--------------------------------------------------------------------------------
1 | import 'package:isar/isar.dart';
2 | part 'rule_group.g.dart';
3 |
4 | // 操作类型枚举
5 | enum OperationType { connect, bind, sendto, recvfrom, all }
6 |
7 | // 规则类
8 | @embedded
9 | class Rule {
10 | // 操作类型
11 | @enumerated
12 | OperationType op = OperationType.all; // Default value instead of nullable
13 |
14 | // 匹配条件
15 | String? matchIp;
16 | int? matchPort;
17 |
18 | // 替换目标
19 | String? replaceIp;
20 | int? replacePort;
21 |
22 | // 网卡绑定相关
23 | String? bindNic;
24 | bool? enableAutoNic;
25 | String? nicNameFilter;
26 | // 构造函数
27 | Rule({
28 | this.op = OperationType.all, // Default value instead of nullable
29 | this.matchIp,
30 | this.matchPort,
31 | this.replaceIp,
32 | this.replacePort,
33 | this.bindNic,
34 | this.enableAutoNic,
35 | this.nicNameFilter,
36 | });
37 | }
38 |
39 | // 规则组类
40 | @collection
41 | class RuleGroup {
42 | Id id = Isar.autoIncrement;
43 |
44 | // 规则组名称
45 | String? name;
46 |
47 | // 匹配窗口标题的正则表达式
48 | String? regex;
49 |
50 | // 规则列表
51 | List rules = [];
52 |
53 | // 实现实例化
54 | RuleGroup({this.name, this.regex = '', this.rules = const []});
55 | }
56 |
--------------------------------------------------------------------------------
/lib/k/models/server_mod.dart:
--------------------------------------------------------------------------------
1 | import 'package:isar/isar.dart';
2 | part 'server_mod.g.dart';
3 |
4 | @collection
5 | class ServerMod {
6 | /// 主键自增
7 | Id id = Isar.autoIncrement;
8 | String name = ""; // 服务器名
9 | String url = ""; // 服务器地址
10 | // 是否启用
11 | bool enable = true;
12 | // tcp 开启
13 | bool tcp = true;
14 | // udp 开启
15 | bool udp = false;
16 | // ws 开启
17 | bool ws = false;
18 | // wss 开启
19 | bool wss = false;
20 | // quic 开启
21 | bool quic = false;
22 | // wg 开启
23 | bool wg = false;
24 | // txt 开启
25 | bool txt = false;
26 | // srv 开启
27 | bool srv = false;
28 | // http 开启
29 | bool http = false;
30 | // https 开启
31 | bool https = false;
32 |
33 | //构造
34 | ServerMod({
35 | this.id = Isar.autoIncrement,
36 | this.enable = false,
37 | this.name = "",
38 | this.url = "",
39 | this.tcp = true,
40 | this.udp = false,
41 | this.ws = false,
42 | this.wss = false,
43 | this.quic = false,
44 | this.wg = false,
45 | this.txt = false,
46 | this.srv = false,
47 | this.http = false,
48 | this.https = false,
49 | });
50 | }
51 |
--------------------------------------------------------------------------------
/lib/k/models/theme_settings.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:isar/isar.dart';
3 | part 'theme_settings.g.dart';
4 |
5 | /// 主题设置类
6 | @collection
7 | class ThemeSettings {
8 | /// 主键ID,固定为1因为只需要一个实例
9 | Id id = 1;
10 |
11 | /// 主题颜色值,默认为蓝色
12 | int colorValue = Colors.blue.toARGB32();
13 |
14 | /// 主题模式枚举值,默认跟随系统
15 | @enumerated
16 | ThemeMode themeModeValue = ThemeMode.system;
17 |
18 | /// 构造函数,用于初始化主题设置
19 | ThemeSettings({
20 | this.colorValue = 0xFFFF5722,
21 | this.themeModeValue = ThemeMode.system,
22 | });
23 | }
24 |
--------------------------------------------------------------------------------
/lib/k/models_mod/room_cz.dart:
--------------------------------------------------------------------------------
1 | import 'package:isar/isar.dart';
2 | import 'package:astral/k/models/room.dart'; // 添加 Room 模型的导入
3 |
4 | class RoomCz {
5 | final Isar _isar;
6 |
7 | RoomCz(this._isar) {
8 | init();
9 | }
10 |
11 | Future init() async {}
12 |
13 | // 添加房间
14 | Future addRoom(Room room) async {
15 | return await _isar.writeTxn(() async {
16 | return await _isar.rooms.put(room);
17 | });
18 | }
19 |
20 | // 根据ID获取房间
21 | Future getRoomById(int id) async {
22 | return await _isar.rooms.get(id);
23 | }
24 |
25 | // 获取所有房间
26 | Future> getAllRooms() async {
27 | return await _isar.rooms.where().findAll();
28 | }
29 |
30 | // 更新房间
31 | Future updateRoom(Room room) async {
32 | return await _isar.writeTxn(() async {
33 | return await _isar.rooms.put(room); // Isar 的 put 方法会自动处理更新
34 | });
35 | }
36 |
37 | // 删除房间
38 | Future deleteRoom(int id) async {
39 | return await _isar.writeTxn(() async {
40 | return await _isar.rooms.delete(id);
41 | });
42 | }
43 |
44 | // 根据标签查询房间
45 | Future> getRoomsByTag(String tag) async {
46 | return await _isar.rooms.filter().tagsElementEqualTo(tag).findAll();
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/lib/k/models_mod/server_cz.dart:
--------------------------------------------------------------------------------
1 | import 'package:astral/k/models/server_mod.dart';
2 | import 'package:isar/isar.dart';
3 |
4 | class ServerCz {
5 | final Isar _isar;
6 |
7 | ServerCz(this._isar) {
8 | init();
9 | }
10 |
11 | Future init() async {
12 | }
13 |
14 | // 添加服务器
15 | Future addServer(ServerMod server) async {
16 | return await _isar.writeTxn(() async {
17 | return await _isar.serverMods.put(server);
18 | });
19 | }
20 |
21 | // 设置是否启用
22 | Future setServerEnable(ServerMod server, bool enable) async {
23 | server.enable = enable;
24 | return await _isar.writeTxn(() async {
25 | return await _isar.serverMods.put(server);
26 | });
27 | }
28 |
29 | // 根据ID获取服务器
30 | Future getServerById(int id) async {
31 | return await _isar.serverMods.get(id);
32 | }
33 |
34 | // 获取所有服务器
35 | Future> getAllServers() async {
36 | return await _isar.serverMods.where().findAll();
37 | }
38 |
39 | // 更新服务器
40 | Future updateServer(ServerMod server) async {
41 | return await _isar.writeTxn(() async {
42 | return await _isar.serverMods.put(server);
43 | });
44 | }
45 |
46 | // 删除服务器 by id
47 | Future deleteServerid(int id) async {
48 | return await _isar.writeTxn(() async {
49 | return await _isar.serverMods.delete(id);
50 | });
51 | }
52 |
53 | // 删除服务器 by object
54 | Future deleteServer(ServerMod server) async {
55 | return await _isar.writeTxn(() async {
56 | return await _isar.serverMods.delete(server.id);
57 | });
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/lib/k/models_mod/theme_settings_cz.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:isar/isar.dart';
3 | import '../models/theme_settings.dart';
4 |
5 | /// 主题设置仓库类
6 | /// 负责管理和持久化主题相关的设置
7 | class ThemeSettingsRepository {
8 | /// Isar数据库实例
9 | final Isar _isar;
10 |
11 | /// 构造函数
12 | /// @param _isar Isar数据库实例
13 | /// 创建实例时自动初始化数据
14 | ThemeSettingsRepository(this._isar) {
15 | init();
16 | }
17 |
18 | /// 初始化主题设置
19 | /// 如果数据库中没有主题设置记录,则创建一个默认的设置记录
20 | Future init() async {
21 | if (await _isar.themeSettings.count() == 0) {
22 | await _isar.writeTxn(() async {
23 | await _isar.themeSettings.put(ThemeSettings()..id = 1);
24 | });
25 | }
26 | }
27 |
28 | /// 更新主题颜色
29 | /// @param colorValue 新的颜色值
30 | /// 将新的颜色值保存到数据库中
31 | Future updateThemeColor(int colorValue) async {
32 | ThemeSettings? settings = await _isar.themeSettings.get(1);
33 | if (settings != null) {
34 | settings.colorValue = colorValue;
35 | await _isar.writeTxn(() async {
36 | await _isar.themeSettings.put(settings);
37 | });
38 | }
39 | }
40 |
41 | /// 获取当前主题颜色
42 | /// @return 返回当前的主题颜色值,如果未设置则返回0
43 | Future getThemeColor() async {
44 | ThemeSettings? settings = await _isar.themeSettings.get(1);
45 | return settings?.colorValue ?? 0xFFFF5722;
46 | }
47 |
48 | /// 更新主题模式
49 | /// @param themeMode 新的主题模式(light/dark/system)
50 | /// 将新的主题模式保存到数据库中
51 | Future updateThemeMode(ThemeMode themeMode) async {
52 | ThemeSettings? settings = await _isar.themeSettings.get(1);
53 | if (settings != null) {
54 | settings.themeModeValue = themeMode;
55 | await _isar.writeTxn(() async {
56 | await _isar.themeSettings.put(settings);
57 | });
58 | }
59 | }
60 |
61 | /// 获取当前主题模式
62 | /// @return 返回当前的主题模式,如果未设置则返回系统默认模式
63 | Future getThemeMode() async {
64 | ThemeSettings? settings = await _isar.themeSettings.get(1);
65 | return settings?.themeModeValue ?? ThemeMode.system;
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/lib/k/navigtion.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class NavigationItem {
4 | final IconData icon;
5 | final IconData activeIcon;
6 | final String label;
7 | final Widget page;
8 |
9 | const NavigationItem({
10 | required this.icon,
11 | IconData? activeIcon,
12 | required this.label,
13 | required this.page,
14 | }) : activeIcon = activeIcon ?? icon;
15 | }
16 |
--------------------------------------------------------------------------------
/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'dart:io';
3 |
4 | import 'package:astral/fun/up.dart';
5 | import 'package:astral/k/database/app_data.dart';
6 | import 'package:astral/k/mod/window_manager.dart';
7 | import 'package:flutter/foundation.dart';
8 | import 'package:flutter/material.dart';
9 | import 'package:astral/src/rust/frb_generated.dart';
10 | import 'package:astral/app.dart';
11 | import 'package:sentry_flutter/sentry_flutter.dart';
12 |
13 | // 修改后的main.dart文件内容
14 | void main() async {
15 | // 添加Zone错误致命检测(必须放在最顶部)
16 | BindingBase.debugZoneErrorsAreFatal = true;
17 |
18 | runZonedGuarded(
19 | () async {
20 | WidgetsFlutterBinding.ensureInitialized();
21 | await AppDatabase().init();
22 | AppInfoUtil.init();
23 | await RustLib.init();
24 | if (!kIsWeb &&
25 | (Platform.isWindows || Platform.isLinux || Platform.isMacOS)) {
26 | await WindowManagerUtils.initializeWindow();
27 | }
28 | await SentryFlutter.init((options) {
29 | options.dsn =
30 | 'https://8ddef9dc25ba468431473fc15187df30@o4509285217402880.ingest.de.sentry.io/4509285224087632';
31 | });
32 | runApp(const KevinApp());
33 | },
34 | (exception, stackTrace) async {
35 | print(exception);
36 | print(stackTrace);
37 | await Sentry.captureException(exception, stackTrace: stackTrace);
38 | },
39 | );
40 | }
41 |
--------------------------------------------------------------------------------
/lib/screens/home_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:astral/wid/home/about_home.dart';
2 | import 'package:astral/wid/home/contributors.dart'; // 添加这行
3 | import 'package:astral/wid/home/servers_home.dart';
4 | import 'package:astral/wid/home/user_ip.dart';
5 | import 'package:astral/wid/home/virtual_ip.dart';
6 | import 'package:astral/wid/home/connect_button.dart';
7 | import 'package:flutter/material.dart';
8 | import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
9 |
10 | class HomePage extends StatefulWidget {
11 | const HomePage({super.key});
12 |
13 | @override
14 | State createState() => _HomePageState();
15 | }
16 |
17 | class _HomePageState extends State {
18 | // 根据宽度计算列数
19 | int _getColumnCount(double width) {
20 | if (width >= 1200) {
21 | return 5;
22 | } else if (width >= 900) {
23 | return 4;
24 | } else if (width >= 600) {
25 | return 3;
26 | }
27 | return 2;
28 | }
29 |
30 | @override
31 | Widget build(BuildContext context) {
32 | final width = MediaQuery.of(context).size.width;
33 | final columnCount = _getColumnCount(width);
34 | return Scaffold(
35 | body: Stack(
36 | children: [
37 | Column(
38 | children: [
39 | Expanded(
40 | child: SingleChildScrollView(
41 | child: Padding(
42 | padding: const EdgeInsets.all(14),
43 | child: StaggeredGrid.count(
44 | crossAxisCount: columnCount,
45 | mainAxisSpacing: 8,
46 | crossAxisSpacing: 8,
47 | children: [
48 | VirtualIpBox(),
49 | UserIpBox(),
50 | ServersHome(),
51 | // UdpLog(),
52 | AboutHome(),
53 | ],
54 | ),
55 | ),
56 | ),
57 | ),
58 | ],
59 | ),
60 | ],
61 | ),
62 | floatingActionButton: const ConnectButton(),
63 | );
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/lib/src/rust/api/firewall.dart:
--------------------------------------------------------------------------------
1 | // This file is automatically generated, so please do not edit it.
2 | // @generated by `flutter_rust_bridge`@ 2.10.0.
3 |
4 | // ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
5 |
6 | import '../frb_generated.dart';
7 | import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
8 |
9 | Future getFirewallStatus({required int profileIndex}) => RustLib
10 | .instance
11 | .api
12 | .crateApiFirewallGetFirewallStatus(profileIndex: profileIndex);
13 |
14 | Future setFirewallStatus({
15 | required int profileIndex,
16 | required bool enable,
17 | }) => RustLib.instance.api.crateApiFirewallSetFirewallStatus(
18 | profileIndex: profileIndex,
19 | enable: enable,
20 | );
21 |
--------------------------------------------------------------------------------
/lib/src/rust/api/hops.dart:
--------------------------------------------------------------------------------
1 | // This file is automatically generated, so please do not edit it.
2 | // @generated by `flutter_rust_bridge`@ 2.10.0.
3 |
4 | // ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
5 |
6 | import '../frb_generated.dart';
7 | import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
8 |
9 | Future> getAllInterfacesMetrics() =>
10 | RustLib.instance.api.crateApiHopsGetAllInterfacesMetrics();
11 |
12 | Future setInterfaceMetric({
13 | required String interfaceName,
14 | required int metric,
15 | }) => RustLib.instance.api.crateApiHopsSetInterfaceMetric(
16 | interfaceName: interfaceName,
17 | metric: metric,
18 | );
19 |
--------------------------------------------------------------------------------
/lib/wid/bottom_nav.dart:
--------------------------------------------------------------------------------
1 | import 'package:astral/k/app_s/aps.dart';
2 | import 'package:astral/k/navigtion.dart';
3 | import 'package:flutter/material.dart';
4 |
5 | class BottomNav extends StatelessWidget {
6 | final List navigationItems;
7 | final ColorScheme colorScheme;
8 |
9 | const BottomNav({
10 | super.key,
11 | required this.navigationItems,
12 | required this.colorScheme,
13 | });
14 |
15 | @override
16 | BottomNavigationBar build(BuildContext context) {
17 | return BottomNavigationBar(
18 | backgroundColor: colorScheme.surfaceContainerLow,
19 | elevation: 8,
20 | type: BottomNavigationBarType.fixed,
21 | selectedItemColor: colorScheme.primary,
22 | unselectedItemColor: colorScheme.onSurfaceVariant,
23 | showUnselectedLabels: true,
24 | items:
25 | navigationItems
26 | .map(
27 | (item) => BottomNavigationBarItem(
28 | icon: Icon(item.icon),
29 | activeIcon: Icon(item.activeIcon),
30 | label: item.label,
31 | ),
32 | )
33 | .toList(),
34 | currentIndex: Aps().selectedIndex.watch(context),
35 | onTap: (index) {
36 | Aps().selectedIndex.set(index);
37 | },
38 | );
39 | }
40 | }
41 |
42 |
43 |
--------------------------------------------------------------------------------
/lib/wid/home/about_home.dart:
--------------------------------------------------------------------------------
1 | import 'package:astral/fun/up.dart';
2 | import 'package:astral/k/app_s/aps.dart';
3 | import 'package:astral/src/rust/api/simple.dart';
4 | import 'package:astral/wid/home_box.dart';
5 | import 'package:flutter/material.dart';
6 | import 'package:graphview/GraphView.dart';
7 |
8 | class AboutHome extends StatefulWidget {
9 | const AboutHome({super.key});
10 |
11 | @override
12 | State createState() => _AboutHomeState();
13 | }
14 |
15 | class _AboutHomeState extends State {
16 | String version = '';
17 | @override
18 | void initState() {
19 | // TODO: implement initState
20 | super.initState();
21 | easytierVersion().then((value) {
22 | setState(() {
23 | version = value; // 异步完成后更新状态
24 | });
25 | });
26 | }
27 |
28 | @override
29 | Widget build(BuildContext context) {
30 | var colorScheme = Theme.of(context).colorScheme;
31 | return HomeBox(
32 | widthSpan: 2,
33 | child: Column(
34 | crossAxisAlignment: CrossAxisAlignment.start,
35 | children: [
36 | Row(
37 | children: [
38 | Icon(
39 | Icons.info_outline,
40 | color: colorScheme.primary,
41 | size: 22,
42 | ), // 修改标题图标
43 | const SizedBox(width: 8),
44 | const Text(
45 | '关于',
46 | style: TextStyle(fontSize: 18, fontWeight: FontWeight.w400),
47 | ),
48 | ],
49 | ),
50 | const SizedBox(height: 16),
51 | Wrap(
52 | spacing: 8,
53 | children: [
54 | Icon(
55 | Icons.smartphone,
56 | size: 20,
57 | color: colorScheme.primary,
58 | ), // 软件版本图标
59 | const Text(
60 | '软件版本: ',
61 | style: TextStyle(fontWeight: FontWeight.w700),
62 | ),
63 | Text(
64 | AppInfoUtil.getVersion(),
65 | style: TextStyle(color: colorScheme.secondary),
66 | ),
67 | ],
68 | ),
69 | const SizedBox(height: 4),
70 | Wrap(
71 | spacing: 8,
72 | children: [
73 | Icon(
74 | Icons.memory,
75 | size: 20,
76 | color: colorScheme.primary,
77 | ), // 内核版本图标
78 | const Text(
79 | '内核版本: ',
80 | style: TextStyle(fontWeight: FontWeight.w700),
81 | ),
82 | Text(version, style: TextStyle(color: colorScheme.secondary)),
83 | ],
84 | ),
85 | ],
86 | ),
87 | );
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/lib/wid/home_box.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
3 |
4 | class HomeBox extends StatefulWidget {
5 | final int widthSpan;
6 | final Widget? child;
7 | final double? fixedCellHeight; // 改为可空类型
8 | //是否开启边框
9 | final bool? isBorder;
10 |
11 | const HomeBox({
12 | super.key,
13 | required this.widthSpan,
14 | this.child,
15 | this.fixedCellHeight, // 移除默认值
16 | this.isBorder = true,
17 | });
18 |
19 | @override
20 | State createState() => _HomeBoxState();
21 | }
22 |
23 | class _HomeBoxState extends State {
24 | bool isHovered = false;
25 |
26 | @override
27 | Widget build(BuildContext context) {
28 | final theme = Theme.of(context);
29 |
30 | return widget.fixedCellHeight != null
31 | ? StaggeredGridTile.extent(
32 | crossAxisCellCount: widget.widthSpan,
33 | mainAxisExtent: widget.fixedCellHeight!,
34 | child: _buildContent(theme),
35 | )
36 | : StaggeredGridTile.fit(
37 | crossAxisCellCount: widget.widthSpan,
38 | child: _buildContent(theme),
39 | );
40 | }
41 |
42 | Widget _buildContent(ThemeData theme) {
43 | return MouseRegion(
44 | onEnter: (_) => setState(() => isHovered = true),
45 | onExit: (_) => setState(() => isHovered = false),
46 | child: Card(
47 | elevation: isHovered ? 8 : 4,
48 | shape: RoundedRectangleBorder(
49 | borderRadius: BorderRadius.circular(widget.isBorder ?? true ? 8 : 1),
50 | side: BorderSide(
51 | color: isHovered ? theme.colorScheme.primary : Colors.transparent,
52 | width: 1,
53 | ),
54 | ),
55 | child: InkWell(
56 | onTap: () {},
57 | splashColor: theme.colorScheme.primary.withValues(alpha: 0.3),
58 | highlightColor: theme.colorScheme.primary.withValues(alpha: 0.1),
59 | borderRadius: BorderRadius.circular(8),
60 | child: Container(
61 | padding: EdgeInsets.all(widget.isBorder ?? true ? 12 : 1.0),
62 | height: widget.fixedCellHeight, // height 会自动适应内容
63 | width: double.infinity,
64 | child: widget.child,
65 | ),
66 | ),
67 | ),
68 | );
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/linux/.gitignore:
--------------------------------------------------------------------------------
1 | flutter/ephemeral
2 |
--------------------------------------------------------------------------------
/linux/flutter/generated_plugin_registrant.h:
--------------------------------------------------------------------------------
1 | //
2 | // Generated file. Do not edit.
3 | //
4 |
5 | // clang-format off
6 |
7 | #ifndef GENERATED_PLUGIN_REGISTRANT_
8 | #define GENERATED_PLUGIN_REGISTRANT_
9 |
10 | #include
11 |
12 | // Registers Flutter plugins.
13 | void fl_register_plugins(FlPluginRegistry* registry);
14 |
15 | #endif // GENERATED_PLUGIN_REGISTRANT_
16 |
--------------------------------------------------------------------------------
/linux/flutter/generated_plugins.cmake:
--------------------------------------------------------------------------------
1 | #
2 | # Generated file, do not edit.
3 | #
4 |
5 | list(APPEND FLUTTER_PLUGIN_LIST
6 | flutter_localization
7 | gtk
8 | isar_flutter_libs
9 | open_file_linux
10 | screen_retriever_linux
11 | sentry_flutter
12 | system_tray
13 | url_launcher_linux
14 | window_manager
15 | )
16 |
17 | list(APPEND FLUTTER_FFI_PLUGIN_LIST
18 | rust_lib_astral
19 | )
20 |
21 | set(PLUGIN_BUNDLED_LIBRARIES)
22 |
23 | foreach(plugin ${FLUTTER_PLUGIN_LIST})
24 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})
25 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
26 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $)
27 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
28 | endforeach(plugin)
29 |
30 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
31 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin})
32 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
33 | endforeach(ffi_plugin)
34 |
--------------------------------------------------------------------------------
/linux/runner/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.13)
2 | project(runner LANGUAGES CXX)
3 |
4 | # Define the application target. To change its name, change BINARY_NAME in the
5 | # top-level CMakeLists.txt, not the value here, or `flutter run` will no longer
6 | # work.
7 | #
8 | # Any new source files that you add to the application should be added here.
9 | add_executable(${BINARY_NAME}
10 | "main.cc"
11 | "my_application.cc"
12 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
13 | )
14 |
15 | # Apply the standard set of build settings. This can be removed for applications
16 | # that need different build settings.
17 | apply_standard_settings(${BINARY_NAME})
18 |
19 | # Add preprocessor definitions for the application ID.
20 | add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
21 |
22 | # Add dependency libraries. Add any application-specific dependencies here.
23 | target_link_libraries(${BINARY_NAME} PRIVATE flutter)
24 | target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
25 |
26 | target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
27 |
--------------------------------------------------------------------------------
/linux/runner/main.cc:
--------------------------------------------------------------------------------
1 | #include "my_application.h"
2 |
3 | int main(int argc, char** argv) {
4 | g_autoptr(MyApplication) app = my_application_new();
5 | return g_application_run(G_APPLICATION(app), argc, argv);
6 | }
7 |
--------------------------------------------------------------------------------
/linux/runner/my_application.h:
--------------------------------------------------------------------------------
1 | #ifndef FLUTTER_MY_APPLICATION_H_
2 | #define FLUTTER_MY_APPLICATION_H_
3 |
4 | #include
5 |
6 | G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION,
7 | GtkApplication)
8 |
9 | /**
10 | * my_application_new:
11 | *
12 | * Creates a new Flutter-based application.
13 | *
14 | * Returns: a new #MyApplication.
15 | */
16 | MyApplication* my_application_new();
17 |
18 | #endif // FLUTTER_MY_APPLICATION_H_
19 |
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: astral
2 | description: "astral"
3 | publish_to: "none" # Remove this line if you wish to publish to pub.dev
4 |
5 |
6 | version: 2.0.0-beta.94
7 |
8 | environment:
9 | sdk: ^3.7.2
10 |
11 | dependencies:
12 | open_file: ^3.0.0
13 | vpn_service_plugin:
14 | path: vpn_service_plugin
15 | flutter:
16 | sdk: flutter
17 | path: ^1.8.3 # Add this line
18 | cupertino_icons: ^1.0.8
19 | rust_lib_astral:
20 | path: rust_builder
21 | flutter_rust_bridge: 2.10.0
22 | signals_flutter: ^6.0.2
23 | isar_flutter_libs:
24 | hosted: https://pub.isar-community.dev
25 | version: 3.1.8
26 | window_manager: ^0.4.3
27 | flutter_colorpicker: ^1.1.0
28 | flutter_staggered_grid_view: ^0.7.0
29 | uuid: ^3.0.7
30 | jwt_decoder: ^2.0.1
31 | dart_jsonwebtoken: ^3.2.0
32 | flutter_localization: ^0.3.2
33 | flutter_localizations:
34 | sdk: flutter
35 | graphview: ^1.2.0
36 | device_info_plus: ^11.4.0
37 | toml: ^0.16.0
38 | isar:
39 | hosted: https://pub.isar-community.dev
40 | version: 3.1.8
41 | package_info_plus: ^8.3.0
42 | url_launcher: ^6.3.1
43 | path_provider: ^2.1.5
44 | system_tray: ^2.0.3
45 | app_links: ^6.4.0
46 | sentry_flutter: ^8.14.2
47 | flutter_dotenv: ^5.2.1
48 | win32: ^5.12.0
49 | json_annotation: ^4.8.1
50 | permission_handler: ^11.0.1
51 |
52 | dev_dependencies:
53 | flutter_test:
54 | sdk: flutter
55 | flutter_lints: ^5.0.0
56 | build_runner: ^2.4.4
57 | json_serializable: ^6.7.1
58 | integration_test:
59 | sdk: flutter
60 | isar_generator:
61 | hosted: https://pub.isar-community.dev
62 | version: 3.1.8
63 |
64 | flutter:
65 | uses-material-design: true
66 | assets:
67 | # - assets/dlls/
68 | - assets/icon.ico
69 |
70 | fonts:
71 | - family: MiSans
72 | fonts:
73 | - asset: fonts/MiSans-Demibold.ttf
74 | - asset: fonts/MiSans-Regular.ttf
75 | weight: 800
76 |
--------------------------------------------------------------------------------
/rust/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 |
--------------------------------------------------------------------------------
/rust/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "rust_lib_astral"
3 | version = "0.1.0"
4 | edition = "2021"
5 |
6 | [lib]
7 | crate-type = ["cdylib", "staticlib"]
8 |
9 | [dependencies]
10 | flutter_rust_bridge = "=2.10.0"
11 | lazy_static = "1.4"
12 | serde_json = "1.0"
13 | serde = { version = "1.0", features = ["derive"] }
14 | once_cell = "1.19.0"
15 | dashmap = "6.1.0"
16 | chrono = "0.4.38"
17 | humansize = "1.1.1"
18 | tokio = "1.39.2"
19 | anyhow = "1.0.95"
20 | surge-ping = "0.8"
21 | easytier = { path = "./easytier" }
22 | windows = { version = "0.48.0", features = ["Win32_NetworkManagement_WindowsFirewall",
23 | "Win32_System_Com",
24 | "Win32_NetworkManagement_IpHelper",
25 | "Win32_Networking_WinSock",
26 | "Win32_System_Memory",
27 | "Win32_System_WindowsProgramming",
28 | "Win32_Foundation"] }
29 | winapi = { version = "0.3", features = ["iphlpapi", "iptypes", "netioapi", "winerror", "ws2def"] }
30 | rand = "0.9.1"
31 |
32 | [lints.rust]
33 | unexpected_cfgs = { level = "warn", check-cfg = ['cfg(frb_expand)'] }
34 |
--------------------------------------------------------------------------------
/rust/easytier/src/arch/mod.rs:
--------------------------------------------------------------------------------
1 | #[cfg(target_os = "windows")]
2 | pub mod windows;
3 |
--------------------------------------------------------------------------------
/rust/easytier/src/common/constants.rs:
--------------------------------------------------------------------------------
1 | macro_rules! define_global_var {
2 | ($name:ident, $type:ty, $init:expr) => {
3 | pub static $name: once_cell::sync::Lazy> =
4 | once_cell::sync::Lazy::new(|| tokio::sync::Mutex::new($init));
5 | };
6 | }
7 |
8 | #[macro_export]
9 | macro_rules! use_global_var {
10 | ($name:ident) => {
11 | crate::common::constants::$name.lock().await.to_owned()
12 | };
13 | }
14 |
15 | #[macro_export]
16 | macro_rules! set_global_var {
17 | ($name:ident, $val:expr) => {
18 | *crate::common::constants::$name.lock().await = $val
19 | };
20 | }
21 |
22 | define_global_var!(MANUAL_CONNECTOR_RECONNECT_INTERVAL_MS, u64, 1000);
23 |
24 | define_global_var!(OSPF_UPDATE_MY_GLOBAL_FOREIGN_NETWORK_INTERVAL_SEC, u64, 10);
25 |
26 | pub const UDP_HOLE_PUNCH_CONNECTOR_SERVICE_ID: u32 = 2;
27 |
28 | pub const WIN_SERVICE_WORK_DIR_REG_KEY: &str = "SOFTWARE\\EasyTier\\Service\\WorkDir";
29 |
30 | pub const EASYTIER_VERSION: &str = concat!(env!("CARGO_PKG_VERSION"), "-Astral");
31 |
--------------------------------------------------------------------------------
/rust/easytier/src/common/defer.rs:
--------------------------------------------------------------------------------
1 | #[doc(hidden)]
2 | pub struct Defer {
3 | // internal struct used by defer! macro
4 | func: Option,
5 | }
6 |
7 | impl Defer {
8 | pub fn new(func: F) -> Self {
9 | Self { func: Some(func) }
10 | }
11 | }
12 |
13 | impl Drop for Defer {
14 | fn drop(&mut self) {
15 | self.func.take().map(|f| f());
16 | }
17 | }
18 |
19 | #[macro_export]
20 | macro_rules! defer {
21 | ( $($tt:tt)* ) => {
22 | let _deferred = $crate::common::defer::Defer::new(|| { $($tt)* });
23 | };
24 | }
25 |
--------------------------------------------------------------------------------
/rust/easytier/src/common/error.rs:
--------------------------------------------------------------------------------
1 | use std::{io, result};
2 |
3 | use thiserror::Error;
4 |
5 | use crate::tunnel;
6 |
7 | use super::PeerId;
8 |
9 | #[derive(Error, Debug)]
10 | pub enum Error {
11 | #[error("io error")]
12 | IOError(#[from] io::Error),
13 |
14 | #[cfg(feature = "tun")]
15 | #[error("rust tun error {0}")]
16 | TunError(#[from] tun::Error),
17 |
18 | #[error("tunnel error {0}")]
19 | TunnelError(#[from] tunnel::TunnelError),
20 | #[error("Peer has no conn, PeerId: {0}")]
21 | PeerNoConnectionError(PeerId),
22 | #[error("RouteError: {0:?}")]
23 | RouteError(Option),
24 | #[error("Not found")]
25 | NotFound,
26 | #[error("Invalid Url: {0}")]
27 | InvalidUrl(String),
28 | #[error("Shell Command error: {0}")]
29 | ShellCommandError(String),
30 | // #[error("Rpc listen error: {0}")]
31 | // RpcListenError(String),
32 | #[error("Rpc connect error: {0}")]
33 | RpcConnectError(String),
34 | #[error("Timeout error: {0}")]
35 | Timeout(#[from] tokio::time::error::Elapsed),
36 | #[error("url in blacklist")]
37 | UrlInBlacklist,
38 | #[error("unknown data store error")]
39 | Unknown,
40 | #[error("anyhow error: {0}")]
41 | AnyhowError(#[from] anyhow::Error),
42 |
43 | #[error("wait resp error: {0}")]
44 | WaitRespError(String),
45 |
46 | #[error("message decode error: {0}")]
47 | MessageDecodeError(String),
48 |
49 | #[error("secret key error: {0}")]
50 | SecretKeyError(String),
51 | }
52 |
53 | pub type Result = result::Result;
54 |
55 | // impl From for std::
56 |
--------------------------------------------------------------------------------
/rust/easytier/src/common/ifcfg/darwin.rs:
--------------------------------------------------------------------------------
1 | use std::net::Ipv4Addr;
2 |
3 | use async_trait::async_trait;
4 |
5 | use super::{cidr_to_subnet_mask, run_shell_cmd, Error, IfConfiguerTrait};
6 |
7 | pub struct MacIfConfiger {}
8 | #[async_trait]
9 | impl IfConfiguerTrait for MacIfConfiger {
10 | async fn add_ipv4_route(
11 | &self,
12 | name: &str,
13 | address: Ipv4Addr,
14 | cidr_prefix: u8,
15 | cost: Option,
16 | ) -> Result<(), Error> {
17 | run_shell_cmd(
18 | format!(
19 | "route -n add {} -netmask {} -interface {} -hopcount {}",
20 | address,
21 | cidr_to_subnet_mask(cidr_prefix),
22 | name,
23 | cost.unwrap_or(7)
24 | )
25 | .as_str(),
26 | )
27 | .await
28 | }
29 |
30 | async fn remove_ipv4_route(
31 | &self,
32 | name: &str,
33 | address: Ipv4Addr,
34 | cidr_prefix: u8,
35 | ) -> Result<(), Error> {
36 | run_shell_cmd(
37 | format!(
38 | "route -n delete {} -netmask {} -interface {}",
39 | address,
40 | cidr_to_subnet_mask(cidr_prefix),
41 | name
42 | )
43 | .as_str(),
44 | )
45 | .await
46 | }
47 |
48 | async fn add_ipv4_ip(
49 | &self,
50 | name: &str,
51 | address: Ipv4Addr,
52 | cidr_prefix: u8,
53 | ) -> Result<(), Error> {
54 | run_shell_cmd(
55 | format!(
56 | "ifconfig {} {:?}/{:?} 10.8.8.8 up",
57 | name, address, cidr_prefix,
58 | )
59 | .as_str(),
60 | )
61 | .await
62 | }
63 |
64 | async fn set_link_status(&self, name: &str, up: bool) -> Result<(), Error> {
65 | run_shell_cmd(format!("ifconfig {} {}", name, if up { "up" } else { "down" }).as_str())
66 | .await
67 | }
68 |
69 | async fn remove_ip(&self, name: &str, ip: Option) -> Result<(), Error> {
70 | if ip.is_none() {
71 | run_shell_cmd(format!("ifconfig {} inet delete", name).as_str()).await
72 | } else {
73 | run_shell_cmd(
74 | format!("ifconfig {} inet {} delete", name, ip.unwrap().to_string()).as_str(),
75 | )
76 | .await
77 | }
78 | }
79 |
80 | async fn set_mtu(&self, name: &str, mtu: u32) -> Result<(), Error> {
81 | run_shell_cmd(format!("ifconfig {} mtu {}", name, mtu).as_str()).await
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/rust/easytier/src/gateway/fast_socks5/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Jonathan Dizdarevic
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/rust/easytier/src/gateway/fast_socks5/README.md:
--------------------------------------------------------------------------------
1 | Code is modified from https://github.com/dizda/fast-socks5
2 |
--------------------------------------------------------------------------------
/rust/easytier/src/gateway/fast_socks5/util/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod stream;
2 | pub mod target_addr;
3 |
--------------------------------------------------------------------------------
/rust/easytier/src/gateway/fast_socks5/util/stream.rs:
--------------------------------------------------------------------------------
1 | use std::time::Duration;
2 | use tokio::io::ErrorKind as IOErrorKind;
3 | use tokio::net::{TcpStream, ToSocketAddrs};
4 | use tokio::time::timeout;
5 |
6 | use crate::gateway::fast_socks5::{ReplyError, Result};
7 |
8 | /// Easy to destructure bytes buffers by naming each fields:
9 | ///
10 | /// # Examples (before)
11 | ///
12 | /// ```ignore
13 | /// let mut buf = [0u8; 2];
14 | /// stream.read_exact(&mut buf).await?;
15 | /// let [version, method_len] = buf;
16 | ///
17 | /// assert_eq!(version, 0x05);
18 | /// ```
19 | ///
20 | /// # Examples (after)
21 | ///
22 | /// ```ignore
23 | /// let [version, method_len] = read_exact!(stream, [0u8; 2]);
24 | ///
25 | /// assert_eq!(version, 0x05);
26 | /// ```
27 | #[macro_export]
28 | macro_rules! read_exact {
29 | ($stream: expr, $array: expr) => {{
30 | let mut x = $array;
31 | // $stream
32 | // .read_exact(&mut x)
33 | // .await
34 | // .map_err(|_| io_err("lol"))?;
35 | $stream.read_exact(&mut x).await.map(|_| x)
36 | }};
37 | }
38 |
39 | pub async fn tcp_connect_with_timeout(addr: T, request_timeout_s: u64) -> Result
40 | where
41 | T: ToSocketAddrs,
42 | {
43 | let fut = tcp_connect(addr);
44 | match timeout(Duration::from_secs(request_timeout_s), fut).await {
45 | Ok(result) => result,
46 | Err(_) => Err(ReplyError::ConnectionTimeout.into()),
47 | }
48 | }
49 |
50 | pub async fn tcp_connect(addr: T) -> Result
51 | where
52 | T: ToSocketAddrs,
53 | {
54 | match TcpStream::connect(addr).await {
55 | Ok(o) => Ok(o),
56 | Err(e) => match e.kind() {
57 | // Match other TCP errors with ReplyError
58 | IOErrorKind::ConnectionRefused => Err(ReplyError::ConnectionRefused.into()),
59 | IOErrorKind::ConnectionAborted => Err(ReplyError::ConnectionNotAllowed.into()),
60 | IOErrorKind::ConnectionReset => Err(ReplyError::ConnectionNotAllowed.into()),
61 | IOErrorKind::NotConnected => Err(ReplyError::NetworkUnreachable.into()),
62 | _ => Err(e.into()), // #[error("General failure")] ?
63 | },
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/rust/easytier/src/gateway/mod.rs:
--------------------------------------------------------------------------------
1 | use std::sync::{Arc, Mutex};
2 | use tokio::task::JoinSet;
3 |
4 | use crate::common::global_ctx::ArcGlobalCtx;
5 |
6 | pub mod icmp_proxy;
7 | pub mod ip_reassembler;
8 | pub mod tcp_proxy;
9 | #[cfg(feature = "smoltcp")]
10 | pub mod tokio_smoltcp;
11 | pub mod udp_proxy;
12 |
13 | #[cfg(feature = "socks5")]
14 | pub mod fast_socks5;
15 | #[cfg(feature = "socks5")]
16 | pub mod socks5;
17 |
18 | pub mod kcp_proxy;
19 |
20 | #[derive(Debug)]
21 | pub(crate) struct CidrSet {
22 | global_ctx: ArcGlobalCtx,
23 | cidr_set: Arc>>,
24 | tasks: JoinSet<()>,
25 | }
26 |
27 | impl CidrSet {
28 | pub fn new(global_ctx: ArcGlobalCtx) -> Self {
29 | let mut ret = Self {
30 | global_ctx,
31 | cidr_set: Arc::new(Mutex::new(vec![])),
32 | tasks: JoinSet::new(),
33 | };
34 | ret.run_cidr_updater();
35 | ret
36 | }
37 |
38 | fn run_cidr_updater(&mut self) {
39 | let global_ctx = self.global_ctx.clone();
40 | let cidr_set = self.cidr_set.clone();
41 | self.tasks.spawn(async move {
42 | let mut last_cidrs = vec![];
43 | loop {
44 | let cidrs = global_ctx.get_proxy_cidrs();
45 | if cidrs != last_cidrs {
46 | last_cidrs = cidrs.clone();
47 | cidr_set.lock().unwrap().clear();
48 | for cidr in cidrs.iter() {
49 | cidr_set.lock().unwrap().push(cidr.clone());
50 | }
51 | }
52 | tokio::time::sleep(std::time::Duration::from_secs(1)).await;
53 | }
54 | });
55 | }
56 |
57 | pub fn contains_v4(&self, ip: std::net::Ipv4Addr) -> bool {
58 | let ip = ip.into();
59 | let s = self.cidr_set.lock().unwrap();
60 | for cidr in s.iter() {
61 | if cidr.contains(&ip) {
62 | return true;
63 | }
64 | }
65 | false
66 | }
67 |
68 | pub fn is_empty(&self) -> bool {
69 | self.cidr_set.lock().unwrap().is_empty()
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/rust/easytier/src/gateway/tokio_smoltcp/channel_device.rs:
--------------------------------------------------------------------------------
1 | use futures::{Sink, Stream};
2 | use smoltcp::phy::DeviceCapabilities;
3 | use std::{
4 | io,
5 | pin::Pin,
6 | task::{Context, Poll},
7 | };
8 | use tokio::sync::mpsc::{channel, Receiver, Sender};
9 | use tokio_util::sync::{PollSendError, PollSender};
10 |
11 | use super::device::AsyncDevice;
12 |
13 | /// A device that send and receive packets using a channel.
14 | pub struct ChannelDevice {
15 | recv: Receiver>>,
16 | send: PollSender>,
17 | caps: DeviceCapabilities,
18 | }
19 |
20 | impl ChannelDevice {
21 | /// Make a new `ChannelDevice` with the given `recv` and `send` channels.
22 | ///
23 | /// The `caps` is used to determine the device capabilities. `DeviceCapabilities::max_transmission_unit` must be set.
24 | pub fn new(caps: DeviceCapabilities) -> (Self, Sender>>, Receiver>) {
25 | let (tx1, rx1) = channel(1000);
26 | let (tx2, rx2) = channel(1000);
27 | (
28 | ChannelDevice {
29 | send: PollSender::new(tx1),
30 | recv: rx2,
31 | caps,
32 | },
33 | tx2,
34 | rx1,
35 | )
36 | }
37 | }
38 |
39 | impl Stream for ChannelDevice {
40 | type Item = io::Result>;
41 |
42 | fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll