mirror of
https://github.com/lollipopkit/flutter_server_box.git
synced 2026-02-15 12:44:59 +01:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
413c45a559 | ||
|
|
6dc5536c48 | ||
|
|
76c4bf56fa | ||
|
|
a0c6642230 | ||
|
|
4198d7bd13 | ||
|
|
b06fddec07 | ||
|
|
d1f14bee59 | ||
|
|
8953f63197 | ||
|
|
193d80d826 | ||
|
|
9e308792aa | ||
|
|
fbabd8c351 | ||
|
|
1a3cb09ca2 |
60
.github/workflows/release.yml
vendored
60
.github/workflows/release.yml
vendored
@@ -11,42 +11,54 @@ permissions:
|
||||
jobs:
|
||||
releaseAL:
|
||||
name: Release android and linux
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: '0'
|
||||
- name: Install Flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: '17'
|
||||
- name: Fetch secrets
|
||||
run: |
|
||||
curl -u ${{ secrets.BASIC_AUTH }} -o android/app/app.key ${{ secrets.URL_PREFIX }}app.key
|
||||
curl -u ${{ secrets.BASIC_AUTH }} -o android/key.properties ${{ secrets.URL_PREFIX }}key.properties
|
||||
- name: Build
|
||||
run: dart run fl_build -p android,linux
|
||||
- name: Create Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: |
|
||||
build/app/outputs/flutter-apk/${{ env.APP_NAME }}_arm64.apk
|
||||
build/app/outputs/flutter-apk/${{ env.APP_NAME }}_arm.apk
|
||||
build/app/outputs/flutter-apk/${{ env.APP_NAME }}_amd64.apk
|
||||
${{ env.APP_NAME }}_amd64.AppImage
|
||||
build/app/outputs/flutter-apk/${{ env.APP_NAME }}_${{ env.BUILD_NUMBER }}_arm64.apk
|
||||
build/app/outputs/flutter-apk/${{ env.APP_NAME }}_${{ env.BUILD_NUMBER }}_arm.apk
|
||||
build/app/outputs/flutter-apk/${{ env.APP_NAME }}_${{ env.BUILD_NUMBER }}_amd64.apk
|
||||
${{ env.APP_NAME }}_${{ env.BUILD_NUMBER }}_amd64.AppImage
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# releaseWin:
|
||||
# name: Release windows
|
||||
# runs-on: windows-latest
|
||||
# steps:
|
||||
# - name: Checkout
|
||||
# uses: actions/checkout@v4
|
||||
# - name: Install Flutter
|
||||
# uses: subosito/flutter-action@v2
|
||||
# - name: Build
|
||||
# run: dart run fl_build -p windows
|
||||
# - name: Create Release
|
||||
# uses: softprops/action-gh-release@v1
|
||||
# with:
|
||||
# files: |
|
||||
# ${{ env.APP_NAME }}_amd64_windows.zip
|
||||
# env:
|
||||
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
releaseWin:
|
||||
name: Release windows
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: '0'
|
||||
- name: Install Flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
- name: Build
|
||||
run: dart run fl_build -p windows
|
||||
- name: Create Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: |
|
||||
${{ env.APP_NAME }}_${{ env.BUILD_NUMBER }}_windows_amd64.zip
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# releaseApple:
|
||||
# name: Release ios and macos
|
||||
@@ -59,7 +71,7 @@ jobs:
|
||||
# - name: Build
|
||||
# run: dart run fl_build -p ios,mac
|
||||
# - name: Create Release
|
||||
# uses: softprops/action-gh-release@v1
|
||||
# uses: softprops/action-gh-release@v2
|
||||
# with:
|
||||
# files: |
|
||||
# ${{ env.APP_NAME }}_universal_macos.zip
|
||||
|
||||
47
README.md
47
README.md
@@ -15,44 +15,30 @@ Especially thanks to <a href="https://github.com/TerminalStudio/dartssh2">dartss
|
||||
</p>
|
||||
|
||||
## ⬇️ Download
|
||||
[iOS](https://apps.apple.com/app/id1586449703) / [Android](https://cdn.lolli.tech/serverbox/latest.apk) / [macOS](https://apps.apple.com/app/id1586449703): Full support with my own certificate
|
||||
[Linux](https://cdn.lolli.tech/serverbox/latest.AppImage) / [Windows](https://cdn.lolli.tech/serverbox/latest.win.zip): Basically tested, with debug certificate
|
||||
🎉 **The `Android / Linux / Windows` version are now built via GitHub Actions**
|
||||
|
||||
[iOS & macOS](https://apps.apple.com/app/id6476033062) / [Android & Linux & Windows](https://github.com/lollipopkit/flutter_gpt_box/releases)
|
||||
|
||||
|
||||
## 🔖 Feature
|
||||
- Status chart, `SSH` Terminal, `SFTP`, `Docker & Pkg & Process`, Code editor...
|
||||
- `Status chart` (CPU, Sensors, GPU...), `SSH` Term, `SFTP`, `Docker & Pkg & Process`...
|
||||
- Platform specific: `Bio auth`、`Msg push`、`Home widget`、`watchOS App`...
|
||||
- Localization
|
||||
- English, 简体中文
|
||||
- Español, Русский язык, Português, 日本語 (Generated by GPT)
|
||||
- Deutsch (@its-tom) / 繁體中文 (@kalashnikov) / Indonesian (@azkadev) / Français (@FrancXPT) / Dutch (@QazCetelic)
|
||||
- English, 简体中文; Deutsch [@its-tom](https://github.com/its-tom), 繁體中文 [@kalashnikov](https://github.com/kalashnikov), Indonesian [@azkadev](https://github.com/azkadev), Français [@FrancXPT](https://github.com/FrancXPT), Dutch [@QazCetelic](https://github.com/QazCetelic); Español, Русский язык, Português, 日本語 (Generated by GPT)
|
||||
|
||||
|
||||
## 🏙️ ScreenShots
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<img width="277px" src="imgs/server.png">
|
||||
</td>
|
||||
<td>
|
||||
<img width="277px" src="imgs/detail.png">
|
||||
</td>
|
||||
<td>
|
||||
<img width="277px" src="imgs/sftp.png">
|
||||
</td>
|
||||
<td><img width="277px" src="imgs/server.png"></td>
|
||||
<td><img width="277px" src="imgs/detail.png"></td>
|
||||
<td><img width="277px" src="imgs/sftp.png"></td>
|
||||
</tr>
|
||||
</table>
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<img width="277px" src="imgs/editor.png">
|
||||
</td>
|
||||
<td>
|
||||
<img width="277px" src="imgs/ssh.png">
|
||||
</td>
|
||||
<td>
|
||||
<img width="277px" src="imgs/docker.png">
|
||||
</td>
|
||||
<td><img width="277px" src="imgs/editor.png"> </td>
|
||||
<td><img width="277px" src="imgs/ssh.png"></td>
|
||||
<td><img width="277px" src="imgs/docker.png"></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@@ -66,9 +52,7 @@ Before you open an issue, please read the following:
|
||||
2. Make sure whether the issue is caused by ServerBox app.
|
||||
3. Welcome all valid and positive feedback, subjective feedback (such as you think other UI is better) may not be accepted.
|
||||
|
||||
After you read the above, you can:
|
||||
- If you have **any question or feature request**, please open a [discussion](https://github.com/lollipopkit/flutter_server_box/discussions/new/choose).
|
||||
- If ServerBox app has **any bug**, please open an [issue](https://github.com/lollipopkit/flutter_server_box/issues/new).
|
||||
After you read the above, you can open an [issue](https://github.com/lollipopkit/flutter_server_box/issues/new).
|
||||
|
||||
|
||||
## 🧱 Contribution
|
||||
@@ -76,15 +60,8 @@ After you read the above, you can:
|
||||
- [l10n guide](https://blog.lolli.tech/faq/) can be found in my blog.
|
||||
|
||||
|
||||
## 👏🏼 Contributors
|
||||
<a href="https://github.com/lollipopkit/flutter_server_box/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=lollipopkit/flutter_server_box" />
|
||||
</a>
|
||||
|
||||
|
||||
## 💡 My other apps
|
||||
- [GPT Box](https://github.com/lollipopkit/flutter_gpt_box) - A third-party GPT Client for OpenAI API on all platforms.
|
||||
- [2FA Box](https://github.com/lollipopkit/flutter_2fa) - Open source 2FA app for Android, iOS and the web.
|
||||
- [More](https://github.com/lollipopkit) - Tools & etc.
|
||||
|
||||
|
||||
|
||||
42
README_zh.md
42
README_zh.md
@@ -16,12 +16,13 @@
|
||||
|
||||
|
||||
## ⬇️ Download
|
||||
[iOS](https://apps.apple.com/app/id1586449703) / [Android](https://cdn.lolli.tech/serverbox/latest.apk) / [macOS](https://apps.apple.com/app/id1586449703): 经过测试,使用自签名证书
|
||||
[Linux](https://cdn.lolli.tech/serverbox/latest.AppImage) / [Windows](https://cdn.lolli.tech/serverbox/latest.win.zip): 经过不完全测试,使用调试证书
|
||||
🎉 **现在 `Android / Linux / Windows` 版本使用 GitHub Actions 构建**。
|
||||
|
||||
[iOS & macOS](https://apps.apple.com/app/id1586449703) / [Android & Linux & Windows](https://github.com/lollipopkit/flutter_gpt_box/releases)
|
||||
|
||||
|
||||
## 🔖 特点
|
||||
- 状态图表, `SSH` 终端, `SFTP`, `Docker & 包 & 进程` 管理器, 代码编辑器...
|
||||
- `状态图表`(CPU、传感器、GPU 等), `SSH` 终端, `SFTP`, `Docker & 包 & 进程` 管理器...
|
||||
- 特殊支持:`生物认证`、`推送`、`桌面小部件`、`watchOS App`、`跟随系统颜色`...
|
||||
- 本地化
|
||||
- English, 简体中文
|
||||
@@ -32,28 +33,16 @@
|
||||
## 🏙️ 截屏
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<img width="277px" src="imgs/server.png">
|
||||
</td>
|
||||
<td>
|
||||
<img width="277px" src="imgs/detail.png">
|
||||
</td>
|
||||
<td>
|
||||
<img width="277px" src="imgs/sftp.png">
|
||||
</td>
|
||||
<td><img width="277px" src="imgs/server.png"></td>
|
||||
<td><img width="277px" src="imgs/detail.png"></td>
|
||||
<td><img width="277px" src="imgs/sftp.png"></td>
|
||||
</tr>
|
||||
</table>
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<img width="277px" src="imgs/editor.png">
|
||||
</td>
|
||||
<td>
|
||||
<img width="277px" src="imgs/ssh.png">
|
||||
</td>
|
||||
<td>
|
||||
<img width="277px" src="imgs/docker.png">
|
||||
</td>
|
||||
<td><img width="277px" src="imgs/editor.png"> </td>
|
||||
<td><img width="277px" src="imgs/ssh.png"></td>
|
||||
<td><img width="277px" src="imgs/docker.png"></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@@ -69,9 +58,7 @@
|
||||
2. 反馈问题前请检查是否是 serverbox 的问题。
|
||||
3. 欢迎所有有效、正面的反馈,主观(比如你觉得其他UI更好看)的反馈不一定会接受
|
||||
|
||||
确认了解上述内容后:
|
||||
- 如果你有**任何问题或者功能请求**,请在 [讨论](https://github.com/lollipopkit/flutter_server_box/discussions/new/choose) 中交流。
|
||||
- 如果 ServerBox app 有**任何 bug**,请在 [问题](https://github.com/lollipopkit/flutter_server_box/issues/new) 中反馈。
|
||||
确认了解上述内容后,请在 [问题](https://github.com/lollipopkit/flutter_server_box/issues/new) 中反馈。
|
||||
|
||||
|
||||
## 🧱 贡献
|
||||
@@ -79,15 +66,8 @@
|
||||
- [本地化翻译指南](https://blog.lolli.tech/faq/) 可在我的博客中找到。
|
||||
|
||||
|
||||
## 👏🏼 贡献者
|
||||
<a href="https://github.com/lollipopkit/flutter_server_box/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=lollipopkit/flutter_server_box" />
|
||||
</a>
|
||||
|
||||
|
||||
## 💡 我的其它 Apps
|
||||
- [GPT Box](https://github.com/lollipopkit/flutter_gpt_box) - 支持 OpenAI API 的 第三方全平台客户端。
|
||||
- [2FA Box](https://github.com/lollipopkit/flutter_2fa) - 开源的 2FA 应用。
|
||||
- [更多](https://github.com/lollipopkit) - 工具 & etc.
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
PODS:
|
||||
- countly_flutter (24.4.0):
|
||||
- countly_flutter (24.4.1):
|
||||
- Flutter
|
||||
- device_info_plus (0.0.1):
|
||||
- Flutter
|
||||
- file_picker (0.0.1):
|
||||
- Flutter
|
||||
@@ -10,7 +12,7 @@ PODS:
|
||||
- Flutter
|
||||
- icloud_storage (0.0.1):
|
||||
- Flutter
|
||||
- local_auth_ios (0.0.1):
|
||||
- local_auth_darwin (0.0.1):
|
||||
- Flutter
|
||||
- package_info_plus (0.4.5):
|
||||
- Flutter
|
||||
@@ -21,8 +23,6 @@ PODS:
|
||||
- Flutter
|
||||
- plain_notification_token (0.0.1):
|
||||
- Flutter
|
||||
- r_upgrade (0.0.1):
|
||||
- Flutter
|
||||
- share_plus (0.0.1):
|
||||
- Flutter
|
||||
- shared_preferences_foundation (0.0.1):
|
||||
@@ -37,17 +37,17 @@ PODS:
|
||||
|
||||
DEPENDENCIES:
|
||||
- countly_flutter (from `.symlinks/plugins/countly_flutter/ios`)
|
||||
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
|
||||
- file_picker (from `.symlinks/plugins/file_picker/ios`)
|
||||
- Flutter (from `Flutter`)
|
||||
- flutter_background_service_ios (from `.symlinks/plugins/flutter_background_service_ios/ios`)
|
||||
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
|
||||
- icloud_storage (from `.symlinks/plugins/icloud_storage/ios`)
|
||||
- local_auth_ios (from `.symlinks/plugins/local_auth_ios/ios`)
|
||||
- local_auth_darwin (from `.symlinks/plugins/local_auth_darwin/darwin`)
|
||||
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
||||
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
||||
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
||||
- plain_notification_token (from `.symlinks/plugins/plain_notification_token/ios`)
|
||||
- r_upgrade (from `.symlinks/plugins/r_upgrade/ios`)
|
||||
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
||||
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
||||
@@ -57,6 +57,8 @@ DEPENDENCIES:
|
||||
EXTERNAL SOURCES:
|
||||
countly_flutter:
|
||||
:path: ".symlinks/plugins/countly_flutter/ios"
|
||||
device_info_plus:
|
||||
:path: ".symlinks/plugins/device_info_plus/ios"
|
||||
file_picker:
|
||||
:path: ".symlinks/plugins/file_picker/ios"
|
||||
Flutter:
|
||||
@@ -67,8 +69,8 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/flutter_native_splash/ios"
|
||||
icloud_storage:
|
||||
:path: ".symlinks/plugins/icloud_storage/ios"
|
||||
local_auth_ios:
|
||||
:path: ".symlinks/plugins/local_auth_ios/ios"
|
||||
local_auth_darwin:
|
||||
:path: ".symlinks/plugins/local_auth_darwin/darwin"
|
||||
package_info_plus:
|
||||
:path: ".symlinks/plugins/package_info_plus/ios"
|
||||
path_provider_foundation:
|
||||
@@ -77,8 +79,6 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/permission_handler_apple/ios"
|
||||
plain_notification_token:
|
||||
:path: ".symlinks/plugins/plain_notification_token/ios"
|
||||
r_upgrade:
|
||||
:path: ".symlinks/plugins/r_upgrade/ios"
|
||||
share_plus:
|
||||
:path: ".symlinks/plugins/share_plus/ios"
|
||||
shared_preferences_foundation:
|
||||
@@ -91,21 +91,21 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/watch_connectivity/ios"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
countly_flutter: 5d2febe00242796cf569662e5d47da241f31b115
|
||||
countly_flutter: 56233d921c6b4e0a720774a39b8ee8110d6f8d91
|
||||
device_info_plus: 97af1d7e84681a90d0693e63169a5d50e0839a0d
|
||||
file_picker: c79185e70b9b45728cde2a8d8da454e0cb43f287
|
||||
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
||||
flutter_background_service_ios: e30e0d3ee69e4cee66272d0c78eacd48c2e94aac
|
||||
flutter_native_splash: 52501b97d1c0a5f898d687f1646226c1f93c56ef
|
||||
flutter_native_splash: edf599c81f74d093a4daf8e17bd7a018854bc778
|
||||
icloud_storage: d9ac7a33ced81df08ba7ea1bf3099cc0ee58f60a
|
||||
local_auth_ios: 5046a18c018dd973247a0564496c8898dbb5adf9
|
||||
local_auth_darwin: 4d56c90c2683319835a61274b57620df9c4520ab
|
||||
package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c
|
||||
path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c
|
||||
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
|
||||
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
|
||||
plain_notification_token: b36467dc91939a7b6754267c701bbaca14996ee1
|
||||
r_upgrade: 44d715c61914cce3d01ea225abffe894fd51c114
|
||||
share_plus: 8875f4f2500512ea181eef553c3e27dba5135aad
|
||||
shared_preferences_foundation: b4c3b4cddf1c21f02770737f147a3f5da9d39695
|
||||
url_launcher_ios: bbd758c6e7f9fd7b5b1d4cde34d2b95fcce5e812
|
||||
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
|
||||
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
|
||||
wakelock_plus: 78ec7c5b202cab7761af8e2b2b3d0671be6c4ae1
|
||||
watch_connectivity: 715eb484685e05846eab74795348a44bb2809b82
|
||||
|
||||
|
||||
@@ -690,7 +690,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CURRENT_PROJECT_VERSION = 918;
|
||||
CURRENT_PROJECT_VERSION = 923;
|
||||
DEVELOPMENT_TEAM = BA88US33G6;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
|
||||
@@ -700,7 +700,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.918;
|
||||
MARKETING_VERSION = 1.0.923;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
@@ -826,7 +826,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CURRENT_PROJECT_VERSION = 918;
|
||||
CURRENT_PROJECT_VERSION = 923;
|
||||
DEVELOPMENT_TEAM = BA88US33G6;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
|
||||
@@ -836,7 +836,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.918;
|
||||
MARKETING_VERSION = 1.0.923;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
@@ -854,7 +854,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CURRENT_PROJECT_VERSION = 918;
|
||||
CURRENT_PROJECT_VERSION = 923;
|
||||
DEVELOPMENT_TEAM = BA88US33G6;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
|
||||
@@ -864,7 +864,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.918;
|
||||
MARKETING_VERSION = 1.0.923;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
@@ -885,7 +885,7 @@
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 918;
|
||||
CURRENT_PROJECT_VERSION = 923;
|
||||
DEVELOPMENT_TEAM = BA88US33G6;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -898,7 +898,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.918;
|
||||
MARKETING_VERSION = 1.0.923;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
|
||||
@@ -924,7 +924,7 @@
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 918;
|
||||
CURRENT_PROJECT_VERSION = 923;
|
||||
DEVELOPMENT_TEAM = BA88US33G6;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -937,7 +937,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.918;
|
||||
MARKETING_VERSION = 1.0.923;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
@@ -960,7 +960,7 @@
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 918;
|
||||
CURRENT_PROJECT_VERSION = 923;
|
||||
DEVELOPMENT_TEAM = BA88US33G6;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -973,7 +973,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.918;
|
||||
MARKETING_VERSION = 1.0.923;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
@@ -996,7 +996,7 @@
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 918;
|
||||
CURRENT_PROJECT_VERSION = 923;
|
||||
DEVELOPMENT_ASSET_PATHS = "";
|
||||
DEVELOPMENT_TEAM = BA88US33G6;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
@@ -1008,7 +1008,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.918;
|
||||
MARKETING_VERSION = 1.0.923;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
|
||||
@@ -1037,7 +1037,7 @@
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 918;
|
||||
CURRENT_PROJECT_VERSION = 923;
|
||||
DEVELOPMENT_ASSET_PATHS = "";
|
||||
DEVELOPMENT_TEAM = BA88US33G6;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
@@ -1049,7 +1049,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.918;
|
||||
MARKETING_VERSION = 1.0.923;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
|
||||
PRODUCT_NAME = ServerBox;
|
||||
@@ -1075,7 +1075,7 @@
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 918;
|
||||
CURRENT_PROJECT_VERSION = 923;
|
||||
DEVELOPMENT_ASSET_PATHS = "";
|
||||
DEVELOPMENT_TEAM = BA88US33G6;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
@@ -1087,7 +1087,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.918;
|
||||
MARKETING_VERSION = 1.0.923;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
|
||||
PRODUCT_NAME = ServerBox;
|
||||
|
||||
21
lib/app.dart
21
lib/app.dart
@@ -1,6 +1,6 @@
|
||||
import 'package:dynamic_color/dynamic_color.dart';
|
||||
import 'package:fl_lib/fl_lib.dart';
|
||||
import 'package:fl_lib/l10n/gen/lib_l10n.dart';
|
||||
import 'package:fl_lib/l10n/gen_l10n/lib_l10n.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:toolbox/data/res/build_data.dart';
|
||||
@@ -16,7 +16,7 @@ class MyApp extends StatelessWidget {
|
||||
_setup(context);
|
||||
return ListenableBuilder(
|
||||
listenable: RebuildNodes.app,
|
||||
builder: (_, __) {
|
||||
builder: (context, _) {
|
||||
if (!Stores.setting.useSystemPrimaryColor.fetch()) {
|
||||
UIs.colorSeed = Color(Stores.setting.primaryColor.fetch());
|
||||
return _buildApp(context);
|
||||
@@ -74,7 +74,7 @@ class MyApp extends StatelessWidget {
|
||||
title: BuildData.name,
|
||||
themeMode: themeMode,
|
||||
theme: light,
|
||||
darkTheme: tMode < 3 ? dark : _getAmoledTheme(dark),
|
||||
darkTheme: tMode < 3 ? dark : dark.toAmoled,
|
||||
home: _buildAppContent(ctx),
|
||||
);
|
||||
}
|
||||
@@ -88,18 +88,3 @@ class MyApp extends StatelessWidget {
|
||||
void _setup(BuildContext context) async {
|
||||
SystemUIs.setTransparentNavigationBar(context);
|
||||
}
|
||||
|
||||
ThemeData _getAmoledTheme(ThemeData darkTheme) => darkTheme.copyWith(
|
||||
scaffoldBackgroundColor: Colors.black,
|
||||
dialogBackgroundColor: Colors.black,
|
||||
drawerTheme: const DrawerThemeData(backgroundColor: Colors.black),
|
||||
appBarTheme: const AppBarTheme(backgroundColor: Colors.black),
|
||||
dialogTheme: const DialogTheme(backgroundColor: Colors.black),
|
||||
bottomSheetTheme:
|
||||
const BottomSheetThemeData(backgroundColor: Colors.black),
|
||||
listTileTheme: const ListTileThemeData(tileColor: Colors.transparent),
|
||||
cardTheme: const CardTheme(color: Colors.black12),
|
||||
navigationBarTheme:
|
||||
const NavigationBarThemeData(backgroundColor: Colors.black),
|
||||
popupMenuTheme: const PopupMenuThemeData(color: Colors.black),
|
||||
);
|
||||
|
||||
@@ -79,7 +79,9 @@ extension SSHClientX on SSHClient {
|
||||
isRequestingPwd = true;
|
||||
final user = Miscs.pwdRequestWithUserReg.firstMatch(data)?.group(1);
|
||||
if (context == null) return;
|
||||
final pwd = await context.showPwdDialog(title: user, id: id);
|
||||
final pwd = context.mounted
|
||||
? await context.showPwdDialog(title: user, id: id)
|
||||
: null;
|
||||
if (pwd == null || pwd.isEmpty) {
|
||||
session.kill(SSHSignal.TERM);
|
||||
} else {
|
||||
|
||||
@@ -246,4 +246,8 @@ class AppRoutes {
|
||||
static AppRoutes pve({Key? key, required ServerPrivateInfo spi}) {
|
||||
return AppRoutes(PvePage(key: key, spi: spi), 'pve');
|
||||
}
|
||||
|
||||
static AppRoutes kvEditor({Key? key, required Map<String, String> data}) {
|
||||
return AppRoutes(KvEditor(key: key, data: data), 'kv_editor');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import 'package:icloud_storage/icloud_storage.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:toolbox/data/model/app/backup.dart';
|
||||
import 'package:toolbox/data/model/app/sync.dart';
|
||||
import 'package:toolbox/data/res/misc.dart';
|
||||
|
||||
import '../../../data/model/app/error.dart';
|
||||
|
||||
@@ -198,14 +199,13 @@ abstract final class ICloud {
|
||||
}
|
||||
|
||||
static Future<void> sync() async {
|
||||
final result = await download(relativePath: Paths.bakName);
|
||||
final result = await download(relativePath: Miscs.bakFileName);
|
||||
if (result != null) {
|
||||
_logger.warning('Download backup failed: $result');
|
||||
await backup();
|
||||
return;
|
||||
}
|
||||
|
||||
final dlFile = await File(Paths.bakPath).readAsString();
|
||||
final dlFile = await File(Paths.bak).readAsString();
|
||||
final dlBak = await Computer.shared.start(Backup.fromJsonString, dlFile);
|
||||
await dlBak.restore();
|
||||
|
||||
@@ -214,7 +214,7 @@ abstract final class ICloud {
|
||||
|
||||
static Future<void> backup() async {
|
||||
await Backup.backup();
|
||||
final uploadResult = await upload(relativePath: Paths.bakName);
|
||||
final uploadResult = await upload(relativePath: Miscs.bakFileName);
|
||||
if (uploadResult != null) {
|
||||
_logger.warning('Upload backup failed: $uploadResult');
|
||||
} else {
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'package:fl_lib/fl_lib.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:toolbox/data/model/app/backup.dart';
|
||||
import 'package:toolbox/data/model/app/error.dart';
|
||||
import 'package:toolbox/data/res/misc.dart';
|
||||
import 'package:toolbox/data/res/store.dart';
|
||||
import 'package:webdav_client/webdav_client.dart';
|
||||
|
||||
@@ -96,15 +97,14 @@ abstract final class Webdav {
|
||||
}
|
||||
|
||||
static Future<void> sync() async {
|
||||
final result = await download(relativePath: Paths.bakName);
|
||||
final result = await download(relativePath: Miscs.bakFileName);
|
||||
if (result != null) {
|
||||
_logger.warning('Download failed: $result');
|
||||
await backup();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final dlFile = await File(Paths.bakPath).readAsString();
|
||||
final dlFile = await File(Paths.bak).readAsString();
|
||||
final dlBak = await Computer.shared.start(Backup.fromJsonString, dlFile);
|
||||
await dlBak.restore();
|
||||
} catch (e) {
|
||||
@@ -117,7 +117,7 @@ abstract final class Webdav {
|
||||
/// Create a local backup and upload it to WebDAV
|
||||
static Future<void> backup() async {
|
||||
await Backup.backup();
|
||||
final uploadResult = await upload(relativePath: Paths.bakName);
|
||||
final uploadResult = await upload(relativePath: Miscs.bakFileName);
|
||||
if (uploadResult != null) {
|
||||
_logger.warning('Upload failed: $uploadResult');
|
||||
} else {
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:logging/logging.dart';
|
||||
import 'package:toolbox/data/model/server/private_key_info.dart';
|
||||
import 'package:toolbox/data/model/server/server_private_info.dart';
|
||||
import 'package:toolbox/data/model/server/snippet.dart';
|
||||
import 'package:toolbox/data/res/misc.dart';
|
||||
import 'package:toolbox/data/res/provider.dart';
|
||||
import 'package:toolbox/data/res/rebuild.dart';
|
||||
import 'package:toolbox/data/res/store.dart';
|
||||
@@ -74,7 +75,7 @@ class Backup {
|
||||
|
||||
static Future<String> backup([String? name]) async {
|
||||
final result = _diyEncrypt(json.encode(Backup.loadFromStore().toJson()));
|
||||
final path = '${Paths.doc}/${name ?? 'srvbox_bak.json'}';
|
||||
final path = '${Paths.doc}/${name ?? Miscs.bakFileName}';
|
||||
await File(path).writeAsString(result);
|
||||
return path;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ abstract final class ContainerPs {
|
||||
String? net;
|
||||
String? disk;
|
||||
|
||||
factory ContainerPs.fromRawJson(String s, ContainerType typ) => typ.ps(s);
|
||||
factory ContainerPs.fromRaw(String s, ContainerType typ) => typ.ps(s);
|
||||
|
||||
void parseStats(String s);
|
||||
}
|
||||
@@ -110,8 +110,6 @@ final class PodmanPs implements ContainerPs {
|
||||
}
|
||||
|
||||
final class DockerPs implements ContainerPs {
|
||||
final String? command;
|
||||
final String? createdAt;
|
||||
@override
|
||||
final String? id;
|
||||
@override
|
||||
@@ -129,8 +127,6 @@ final class DockerPs implements ContainerPs {
|
||||
String? disk;
|
||||
|
||||
DockerPs({
|
||||
this.command,
|
||||
this.createdAt,
|
||||
this.id,
|
||||
this.image,
|
||||
this.names,
|
||||
@@ -141,10 +137,10 @@ final class DockerPs implements ContainerPs {
|
||||
String? get name => names;
|
||||
|
||||
@override
|
||||
String? get cmd => command;
|
||||
String? get cmd => null;
|
||||
|
||||
@override
|
||||
bool get running => state == 'running';
|
||||
bool get running => state?.contains('Up ') ?? false;
|
||||
|
||||
@override
|
||||
void parseStats(String s) {
|
||||
@@ -155,26 +151,15 @@ final class DockerPs implements ContainerPs {
|
||||
disk = stats['BlockIO'];
|
||||
}
|
||||
|
||||
factory DockerPs.fromRawJson(String str) =>
|
||||
DockerPs.fromJson(json.decode(str));
|
||||
|
||||
String toRawJson() => json.encode(toJson());
|
||||
|
||||
factory DockerPs.fromJson(Map<String, dynamic> json) => DockerPs(
|
||||
command: json["Command"],
|
||||
createdAt: json["CreatedAt"],
|
||||
id: json["ID"],
|
||||
image: json["Image"],
|
||||
names: json["Names"],
|
||||
state: json["State"],
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"Command": command,
|
||||
"CreatedAt": createdAt,
|
||||
"ID": id,
|
||||
"Image": image,
|
||||
"Names": names,
|
||||
"State": state,
|
||||
};
|
||||
/// CONTAINER ID NAMES IMAGE STATUS
|
||||
/// a049d689e7a1 aria2-pro p3terx/aria2-pro Up 3 weeks
|
||||
factory DockerPs.parse(String raw) {
|
||||
final parts = raw.split(RegExp(r'\s{2,}'));
|
||||
return DockerPs(
|
||||
id: parts[0],
|
||||
names: parts[1],
|
||||
image: parts[2],
|
||||
state: parts[3],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ enum ContainerType {
|
||||
;
|
||||
|
||||
ContainerPs Function(String str) get ps => switch (this) {
|
||||
ContainerType.docker => DockerPs.fromRawJson,
|
||||
ContainerType.docker => DockerPs.parse,
|
||||
ContainerType.podman => PodmanPs.fromRawJson,
|
||||
};
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ class ServerPrivateInfo {
|
||||
final port = json["port"] as int? ?? 22;
|
||||
final user = json["user"] as String? ?? 'root';
|
||||
final name = json["name"] as String? ?? '';
|
||||
final pwd = json["authorization"] as String?;
|
||||
final pwd = json["pwd"] as String? ?? json["authorization"] as String?;
|
||||
final keyId = json["pubKeyId"] as String?;
|
||||
final tags = (json["tags"] as List?)?.cast<String>();
|
||||
final alterUrl = json["alterUrl"] as String?;
|
||||
@@ -100,7 +100,7 @@ class ServerPrivateInfo {
|
||||
data["port"] = port;
|
||||
data["user"] = user;
|
||||
if (pwd != null) {
|
||||
data["authorization"] = pwd;
|
||||
data["pwd"] = pwd;
|
||||
}
|
||||
if (keyId != null) {
|
||||
data["pubKeyId"] = keyId;
|
||||
|
||||
@@ -26,7 +26,8 @@ class ContainerProvider extends ChangeNotifier {
|
||||
ContainerErr? error;
|
||||
String? runLog;
|
||||
ContainerType type;
|
||||
bool sudo = false;
|
||||
var sudoCompleter = Completer<bool>();
|
||||
bool isBusy = false;
|
||||
|
||||
ContainerProvider({
|
||||
required this.client,
|
||||
@@ -41,6 +42,7 @@ class ContainerProvider extends ChangeNotifier {
|
||||
this.type = type;
|
||||
Stores.container.setType(type, hostId);
|
||||
error = runLog = items = images = version = null;
|
||||
sudoCompleter = Completer<bool>();
|
||||
notifyListeners();
|
||||
await refresh();
|
||||
}
|
||||
@@ -60,17 +62,27 @@ class ContainerProvider extends ChangeNotifier {
|
||||
// return value;
|
||||
// }
|
||||
|
||||
Future<bool> _requiresSudo() async {
|
||||
final psResult = await client?.run(_wrap(ContainerCmdType.ps.exec(type)));
|
||||
if (psResult == null) return true;
|
||||
if (psResult.string.toLowerCase().contains("permission denied")) {
|
||||
return true;
|
||||
void _requiresSudo() async {
|
||||
/// Podman is rootless
|
||||
if (type == ContainerType.podman) return sudoCompleter.complete(false);
|
||||
if (!Stores.setting.containerTrySudo.fetch()) {
|
||||
return sudoCompleter.complete(false);
|
||||
}
|
||||
return false;
|
||||
|
||||
final res = await client?.run(_wrap(ContainerCmdType.images.exec(type)));
|
||||
if (res?.string.toLowerCase().contains("permission denied") ?? false) {
|
||||
return sudoCompleter.complete(true);
|
||||
}
|
||||
return sudoCompleter.complete(false);
|
||||
}
|
||||
|
||||
Future<void> refresh({bool isAuto = false}) async {
|
||||
sudo = await _requiresSudo() && Stores.setting.containerTrySudo.fetch();
|
||||
if (isBusy) return;
|
||||
isBusy = true;
|
||||
|
||||
if (!sudoCompleter.isCompleted) _requiresSudo();
|
||||
|
||||
final sudo = await sudoCompleter.future;
|
||||
|
||||
/// If sudo is required and auto refresh is enabled, skip the refresh.
|
||||
/// Or this will ask for pwd again and again.
|
||||
@@ -78,17 +90,22 @@ class ContainerProvider extends ChangeNotifier {
|
||||
final includeStats = Stores.setting.containerParseStat.fetch();
|
||||
|
||||
var raw = '';
|
||||
final cmd = _wrap(ContainerCmdType.execAll(
|
||||
type,
|
||||
sudo: sudo,
|
||||
includeStats: includeStats,
|
||||
));
|
||||
final code = await client?.execWithPwd(
|
||||
_wrap(ContainerCmdType.execAll(
|
||||
type,
|
||||
sudo: sudo,
|
||||
includeStats: includeStats,
|
||||
)),
|
||||
cmd,
|
||||
context: context,
|
||||
onStdout: (data, _) => raw = '$raw$data',
|
||||
id: hostId,
|
||||
);
|
||||
|
||||
isBusy = false;
|
||||
|
||||
if (!context.mounted) return;
|
||||
|
||||
/// Code 127 means command not found
|
||||
if (code == 127 || raw.contains(_dockerNotFound)) {
|
||||
error = ContainerErr(type: ContainerErrType.notInstalled);
|
||||
@@ -126,8 +143,12 @@ class ContainerProvider extends ChangeNotifier {
|
||||
final psRaw = ContainerCmdType.ps.find(segments);
|
||||
try {
|
||||
final lines = psRaw.split('\n');
|
||||
if (type == ContainerType.docker) {
|
||||
/// Due to the fetched data is not in json format, skip table header
|
||||
lines.removeWhere((element) => element.contains('CONTAINER ID'));
|
||||
}
|
||||
lines.removeWhere((element) => element.isEmpty);
|
||||
items = lines.map((e) => ContainerPs.fromRawJson(e, type)).toList();
|
||||
items = lines.map((e) => ContainerPs.fromRaw(e, type)).toList();
|
||||
} catch (e, trace) {
|
||||
error = ContainerErr(
|
||||
type: ContainerErrType.parsePs,
|
||||
@@ -203,7 +224,7 @@ class ContainerProvider extends ChangeNotifier {
|
||||
runLog = '';
|
||||
final errs = <String>[];
|
||||
final code = await client?.execWithPwd(
|
||||
_wrap(sudo ? 'sudo -S $cmd' : cmd),
|
||||
_wrap((await sudoCompleter.future) ? 'sudo -S $cmd' : cmd),
|
||||
context: context,
|
||||
onStdout: (data, _) {
|
||||
runLog = '$runLog$data';
|
||||
@@ -254,7 +275,12 @@ enum ContainerCmdType {
|
||||
final prefix = sudo ? 'sudo -S ${type.name}' : type.name;
|
||||
return switch (this) {
|
||||
ContainerCmdType.version => '$prefix version $_jsonFmt',
|
||||
ContainerCmdType.ps => '$prefix ps -a $_jsonFmt',
|
||||
ContainerCmdType.ps => switch (type) {
|
||||
/// Use [_jsonFmt] in Docker will cause the operation to slow down.
|
||||
ContainerType.docker => '$prefix ps -a --format "table '
|
||||
'{{printf \\"${"%-30.30s " * 4}\\" .ID .Names .Image .Status}}"',
|
||||
ContainerType.podman => '$prefix ps -a $_jsonFmt',
|
||||
},
|
||||
ContainerCmdType.stats =>
|
||||
includeStats ? '$prefix stats --no-stream $_jsonFmt' : 'echo PASS',
|
||||
ContainerCmdType.images => '$prefix image ls $_jsonFmt',
|
||||
|
||||
@@ -10,20 +10,24 @@ import 'package:toolbox/core/extension/context/locale.dart';
|
||||
import 'package:toolbox/data/model/app/error.dart';
|
||||
import 'package:toolbox/data/model/server/pve.dart';
|
||||
import 'package:toolbox/data/model/server/server_private_info.dart';
|
||||
import 'package:dartssh2/dartssh2.dart';
|
||||
|
||||
typedef PveCtrlFunc = Future<bool> Function(String node, String id);
|
||||
|
||||
final class PveProvider extends ChangeNotifier {
|
||||
final ServerPrivateInfo spi;
|
||||
late final String addr;
|
||||
//late final SSHClient _client;
|
||||
late String addr;
|
||||
late final SSHClient _client;
|
||||
late final ServerSocket _serverSocket;
|
||||
final List<SSHForwardChannel> _forwards = [];
|
||||
int _localPort = 0;
|
||||
|
||||
PveProvider({required this.spi}) {
|
||||
// final client = _spi.server?.client;
|
||||
// if (client == null) {
|
||||
// throw Exception('Server client is null');
|
||||
// }
|
||||
// _client = client;
|
||||
final client = spi.server?.client;
|
||||
if (client == null) {
|
||||
throw Exception('Server client is null');
|
||||
}
|
||||
_client = client;
|
||||
final addr = spi.custom?.pveAddr;
|
||||
if (addr == null) {
|
||||
err.value = 'PVE address is null';
|
||||
@@ -41,6 +45,7 @@ final class PveProvider extends ChangeNotifier {
|
||||
..httpClientAdapter = IOHttpClientAdapter(
|
||||
createHttpClient: () {
|
||||
final client = HttpClient();
|
||||
client.connectionFactory = cf;
|
||||
if (_ignoreCert) {
|
||||
client.badCertificateCallback = (_, __, ___) => true;
|
||||
}
|
||||
@@ -50,55 +55,76 @@ final class PveProvider extends ChangeNotifier {
|
||||
);
|
||||
|
||||
final data = ValueNotifier<PveRes?>(null);
|
||||
|
||||
bool get onlyOneNode => data.value?.nodes.length == 1;
|
||||
String? release;
|
||||
bool isBusy = false;
|
||||
|
||||
// int _localPort = 0;
|
||||
// String get addr => 'http://127.0.0.1:$_localPort';
|
||||
|
||||
Future<void> _init() async {
|
||||
try {
|
||||
//await _forward();
|
||||
await _forward();
|
||||
await _login();
|
||||
await _release;
|
||||
await _getRelease();
|
||||
} on PveErr {
|
||||
err.value = l10n.pveLoginFailed;
|
||||
} catch (e) {
|
||||
Loggers.app.warning('PVE init failed', e);
|
||||
} catch (e, s) {
|
||||
Loggers.app.warning('PVE init failed', e, s);
|
||||
err.value = e.toString();
|
||||
} finally {
|
||||
connected.complete();
|
||||
}
|
||||
}
|
||||
|
||||
// Future<void> _forward() async {
|
||||
// var retries = 0;
|
||||
// while (retries < 3) {
|
||||
// try {
|
||||
// _localPort = Random().nextInt(1000) + 37000;
|
||||
// print('Forwarding local port $_localPort');
|
||||
// final serverSocket = await ServerSocket.bind('localhost', _localPort);
|
||||
// final forward = await _client.forwardLocal('127.0.0.1', 8006);
|
||||
// serverSocket.listen((socket) {
|
||||
// forward.stream.cast<List<int>>().pipe(socket);
|
||||
// socket.pipe(forward.sink);
|
||||
// });
|
||||
// return;
|
||||
// } on SocketException {
|
||||
// retries++;
|
||||
// }
|
||||
// }
|
||||
// throw Exception('Failed to bind local port');
|
||||
// }
|
||||
Future<void> _forward() async {
|
||||
final url = Uri.parse(addr);
|
||||
if (_localPort == 0) {
|
||||
_serverSocket = await ServerSocket.bind('localhost', 0);
|
||||
_localPort = _serverSocket.port;
|
||||
_serverSocket.listen((socket) async {
|
||||
final forward = await _client.forwardLocal(url.host, url.port);
|
||||
_forwards.add(forward);
|
||||
forward.stream.cast<List<int>>().pipe(socket);
|
||||
socket.cast<List<int>>().pipe(forward.sink);
|
||||
});
|
||||
final newUrl = Uri.parse(addr)
|
||||
.replace(host: 'localhost', port: _localPort)
|
||||
.toString();
|
||||
debugPrint('Forwarding $newUrl to $addr');
|
||||
}
|
||||
}
|
||||
|
||||
Future<ConnectionTask<Socket>> cf(
|
||||
Uri url, String? proxyHost, int? proxyPort) async {
|
||||
/* final serverSocket = await ServerSocket.bind(InternetAddress.anyIPv4, 0);
|
||||
final _localPort = serverSocket.port;
|
||||
serverSocket.listen((socket) async {
|
||||
final forward = await _client.forwardLocal(url.host, url.port);
|
||||
forwards.add(forward);
|
||||
forward.stream.cast<List<int>>().pipe(socket);
|
||||
socket.cast<List<int>>().pipe(forward.sink);
|
||||
});*/
|
||||
|
||||
if (url.isScheme("https")) {
|
||||
return SecureSocket.startConnect('localhost', _localPort,
|
||||
onBadCertificate: (_) => true);
|
||||
} else {
|
||||
return Socket.startConnect('localhost', _localPort);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _login() async {
|
||||
final resp = await session.post('$addr/api2/extjs/access/ticket', data: {
|
||||
'username': spi.user,
|
||||
'password': spi.pwd,
|
||||
'realm': 'pam',
|
||||
'new-format': '1'
|
||||
});
|
||||
final resp = await session.post(
|
||||
'$addr/api2/extjs/access/ticket',
|
||||
data: {
|
||||
'username': spi.user,
|
||||
'password': spi.pwd,
|
||||
'realm': 'pam',
|
||||
'new-format': '1'
|
||||
},
|
||||
options: Options(
|
||||
headers: {HttpHeaders.contentTypeHeader: Headers.jsonContentType},
|
||||
),
|
||||
);
|
||||
try {
|
||||
final ticket = resp.data['data']['ticket'];
|
||||
session.options.headers['CSRFPreventionToken'] =
|
||||
@@ -110,7 +136,7 @@ final class PveProvider extends ChangeNotifier {
|
||||
}
|
||||
|
||||
/// Returns true if the PVE version is 8.0 or later
|
||||
Future<void> get _release async {
|
||||
Future<void> _getRelease() async {
|
||||
final resp = await session.get('$addr/api2/extjs/version');
|
||||
final version = resp.data['data']['release'] as String?;
|
||||
if (version != null) {
|
||||
@@ -167,4 +193,13 @@ final class PveProvider extends ChangeNotifier {
|
||||
bool _isCtrlSuc(Response resp) {
|
||||
return resp.statusCode == 200;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> dispose() async {
|
||||
super.dispose();
|
||||
await _serverSocket.close();
|
||||
for (final forward in _forwards) {
|
||||
forward.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// This file is generated by make script. Do not edit.
|
||||
// This file is generated by fl_build. Do not edit.
|
||||
|
||||
class BuildData {
|
||||
static const String name = "ServerBox";
|
||||
static const int build = 918;
|
||||
static const int build = 923;
|
||||
static const String engine = "3.22.1";
|
||||
static const String buildAt = "2024-05-25 19:17:18";
|
||||
static const int modifications = 2;
|
||||
static const String buildAt = "2024-06-01 22:31:14";
|
||||
static const int modifications = 4;
|
||||
static const int script = 48;
|
||||
}
|
||||
|
||||
@@ -18,4 +18,6 @@ abstract final class Miscs {
|
||||
static const pkgName = 'tech.lolli.toolbox';
|
||||
|
||||
static const jsonEncoder = JsonEncoder.withIndent(' ');
|
||||
|
||||
static const bakFileName = 'srvbox_bak.json';
|
||||
}
|
||||
|
||||
@@ -280,6 +280,7 @@
|
||||
"suspend": "Suspend",
|
||||
"suspendTip": "Die Suspend-Funktion erfordert Root-Rechte und systemd-Unterstützung.",
|
||||
"switchTo": "Wechseln zu {val}",
|
||||
"sync": "Sync",
|
||||
"syncTip": "Damit einige Änderungen wirksam werden, kann ein Neustart erforderlich sein.",
|
||||
"system": "Systeme",
|
||||
"tag": "Tags",
|
||||
|
||||
@@ -280,6 +280,7 @@
|
||||
"suspend": "Suspend",
|
||||
"suspendTip": "The suspend function requires root privileges and systemd support.",
|
||||
"switchTo": "Switch to {val}",
|
||||
"sync": "Sync",
|
||||
"syncTip": "A restart may be required for some changes to take effect.",
|
||||
"system": "System",
|
||||
"tag": "Tags",
|
||||
|
||||
@@ -280,6 +280,7 @@
|
||||
"suspend": "Suspender",
|
||||
"suspendTip": "La función de suspender necesita permisos de root y soporte de systemd.",
|
||||
"switchTo": "Cambiar a {val}",
|
||||
"sync": "Sincronizar",
|
||||
"syncTip": "Puede que necesites reiniciar para que algunos cambios tengan efecto.",
|
||||
"system": "Sistema",
|
||||
"tag": "Etiqueta",
|
||||
|
||||
@@ -280,6 +280,7 @@
|
||||
"suspend": "Suspendre",
|
||||
"suspendTip": "La fonction de suspension nécessite des privilèges root et le support de systemd.",
|
||||
"switchTo": "Passer à {val}",
|
||||
"sync": "Sync",
|
||||
"syncTip": "Un redémarrage peut être nécessaire pour que certains changements prennent effet.",
|
||||
"system": "Système",
|
||||
"tag": "Étiquettes",
|
||||
|
||||
@@ -280,6 +280,7 @@
|
||||
"suspend": "Suspend",
|
||||
"suspendTip": "Fungsi penangguhan memerlukan hak akses root dan dukungan systemd.",
|
||||
"switchTo": "Beralih ke {val}",
|
||||
"sync": "Sinkronisasi",
|
||||
"syncTip": "Pengaktifan ulang mungkin diperlukan agar beberapa perubahan dapat diterapkan.",
|
||||
"system": "Sistem",
|
||||
"tag": "Tag",
|
||||
|
||||
@@ -280,6 +280,7 @@
|
||||
"suspend": "中断",
|
||||
"suspendTip": "suspend機能はroot権限とsystemdのサポートが必要です。",
|
||||
"switchTo": "{val}に切り替える",
|
||||
"sync": "同期する",
|
||||
"syncTip": "再起動が必要な場合があります。一部の変更はその後に有効になります。",
|
||||
"system": "システム",
|
||||
"tag": "タグ",
|
||||
|
||||
@@ -279,6 +279,7 @@
|
||||
"suspend": "Ophangen",
|
||||
"suspendTip": "De opschortfunctie vereist rootrechten en systemd-ondersteuning.",
|
||||
"switchTo": "Overschakelen naar {val}",
|
||||
"sync": "Sync",
|
||||
"syncTip": "Een herstart kan nodig zijn voor sommige wijzigingen om van kracht te worden.",
|
||||
"system": "Systeem",
|
||||
"tag": "Labels",
|
||||
|
||||
@@ -280,6 +280,7 @@
|
||||
"suspend": "Suspender",
|
||||
"suspendTip": "A função de suspensão requer permissões de root e suporte do systemd.",
|
||||
"switchTo": "Mudar para {val}",
|
||||
"sync": "Sincronizar",
|
||||
"syncTip": "Pode ser necessário reiniciar para algumas mudanças surtirem efeito.",
|
||||
"system": "Sistema",
|
||||
"tag": "Tag",
|
||||
|
||||
@@ -280,6 +280,7 @@
|
||||
"suspend": "приостановить",
|
||||
"suspendTip": "Функция приостановки требует прав root и поддержки systemd.",
|
||||
"switchTo": "переключиться на {val}",
|
||||
"sync": "Синхронизировать",
|
||||
"syncTip": "Возможно, потребуется перезагрузка, чтобы некоторые изменения вступили в силу.",
|
||||
"system": "система",
|
||||
"tag": "тег",
|
||||
|
||||
@@ -280,6 +280,7 @@
|
||||
"suspend": "挂起",
|
||||
"suspendTip": "suspend 功能需要 root 权限及 systemd 支持。",
|
||||
"switchTo": "切换到 {val}",
|
||||
"sync": "同步",
|
||||
"syncTip": "可能需要重新启动,某些更改才能生效。",
|
||||
"system": "系统",
|
||||
"tag": "标签",
|
||||
|
||||
@@ -280,6 +280,7 @@
|
||||
"suspend": "挂起",
|
||||
"suspendTip": "suspend 功能需要 root 權限及 systemd 支持。",
|
||||
"switchTo": "切換到 {val}",
|
||||
"sync": "同步",
|
||||
"syncTip": "可能需要重新啟動,某些更改才能生效。",
|
||||
"system": "系統",
|
||||
"tag": "标签",
|
||||
|
||||
@@ -22,6 +22,7 @@ import 'package:toolbox/data/model/server/server_private_info.dart';
|
||||
import 'package:toolbox/data/model/server/snippet.dart';
|
||||
import 'package:toolbox/data/model/ssh/virtual_key.dart';
|
||||
import 'package:toolbox/data/res/build_data.dart';
|
||||
import 'package:toolbox/data/res/misc.dart';
|
||||
import 'package:toolbox/data/res/provider.dart';
|
||||
import 'package:toolbox/data/res/store.dart';
|
||||
import 'package:toolbox/data/res/url.dart';
|
||||
@@ -64,7 +65,7 @@ void _runInZone(void Function() body) {
|
||||
Future<void> _initApp() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
await Paths.init(BuildData.name, bakName: 'srvbox');
|
||||
await Paths.init(BuildData.name, bakName: Miscs.bakFileName);
|
||||
await _initData();
|
||||
_setupDebug();
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import 'package:toolbox/core/utils/sync/icloud.dart';
|
||||
import 'package:toolbox/core/utils/sync/webdav.dart';
|
||||
import 'package:toolbox/data/model/app/backup.dart';
|
||||
import 'package:toolbox/data/model/server/server_private_info.dart';
|
||||
import 'package:toolbox/data/res/misc.dart';
|
||||
import 'package:toolbox/data/res/store.dart';
|
||||
import 'package:toolbox/data/res/url.dart';
|
||||
|
||||
@@ -259,9 +260,9 @@ class BackupPage extends StatelessWidget {
|
||||
),
|
||||
],
|
||||
);
|
||||
} catch (e, trace) {
|
||||
Loggers.app.warning('Import backup failed', e, trace);
|
||||
context.showSnackBar(e.toString());
|
||||
} catch (e, s) {
|
||||
Loggers.app.warning('Import backup failed', e, s);
|
||||
context.showErrDialog(e: e, s: s, operation: l10n.restore);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,89 +270,52 @@ class BackupPage extends StatelessWidget {
|
||||
webdavLoading.value = true;
|
||||
try {
|
||||
final files = await Webdav.list();
|
||||
if (files.isEmpty) {
|
||||
context.showSnackBar(l10n.dirEmpty);
|
||||
webdavLoading.value = false;
|
||||
return;
|
||||
}
|
||||
if (files.isEmpty) return context.showSnackBar(l10n.dirEmpty);
|
||||
|
||||
final fileName = await context.showRoundDialog<String>(
|
||||
final fileName = await context.showPickSingleDialog(
|
||||
title: l10n.restore,
|
||||
child: SizedBox(
|
||||
width: 300,
|
||||
height: 300,
|
||||
child: ListView.builder(
|
||||
itemCount: files.length,
|
||||
itemBuilder: (_, index) {
|
||||
final file = files[index];
|
||||
return ListTile(
|
||||
title: Text(file),
|
||||
onTap: () => context.pop(file),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => context.pop(),
|
||||
child: Text(l10n.cancel),
|
||||
),
|
||||
],
|
||||
items: files,
|
||||
);
|
||||
if (fileName == null) {
|
||||
webdavLoading.value = false;
|
||||
return;
|
||||
}
|
||||
if (fileName == null) return;
|
||||
|
||||
final result = await Webdav.download(relativePath: fileName);
|
||||
if (result != null) {
|
||||
Loggers.app.warning('Download webdav backup failed: $result');
|
||||
webdavLoading.value = false;
|
||||
return;
|
||||
throw result;
|
||||
}
|
||||
final dlFile = await File('${Paths.doc}/$fileName').readAsString();
|
||||
final dlBak = await Computer.shared.start(Backup.fromJsonString, dlFile);
|
||||
await dlBak.restore(force: true);
|
||||
webdavLoading.value = false;
|
||||
} catch (e) {
|
||||
context.showSnackBar(e.toString());
|
||||
rethrow;
|
||||
} catch (e, s) {
|
||||
context.showErrDialog(e: e, s: s, operation: l10n.restore);
|
||||
Loggers.app.warning('Download webdav backup failed', e, s);
|
||||
} finally {
|
||||
webdavLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onTapWebdavUp(BuildContext context) async {
|
||||
webdavLoading.value = true;
|
||||
final date = DateTime.now().ymdhms(ymdSep: "-", hmsSep: "-", sep: "-");
|
||||
final bakName = '$date-${Miscs.bakFileName}';
|
||||
try {
|
||||
webdavLoading.value = true;
|
||||
final bakName =
|
||||
'${DateTime.now().ymdhms(ymdSep: "-", hmsSep: "-", sep: "-")}-${Paths.bakName}';
|
||||
await Backup.backup(bakName);
|
||||
final uploadResult = await Webdav.upload(relativePath: bakName);
|
||||
if (uploadResult != null) {
|
||||
Loggers.app.warning('Upload webdav backup failed: $uploadResult');
|
||||
context.showSnackBar(uploadResult.toString());
|
||||
} else {
|
||||
Loggers.app.info('Upload webdav backup success');
|
||||
throw uploadResult;
|
||||
}
|
||||
} catch (e) {
|
||||
context.showSnackBar(e.toString());
|
||||
rethrow;
|
||||
Loggers.app.info('Upload webdav backup success');
|
||||
} catch (e, s) {
|
||||
context.showErrDialog(e: e, s: s, operation: l10n.upload);
|
||||
Loggers.app.warning('Upload webdav backup failed', e, s);
|
||||
} finally {
|
||||
webdavLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onTapWebdavSetting(BuildContext context) async {
|
||||
final urlCtrl = TextEditingController(
|
||||
text: Stores.setting.webdavUrl.fetch(),
|
||||
);
|
||||
final userCtrl = TextEditingController(
|
||||
text: Stores.setting.webdavUser.fetch(),
|
||||
);
|
||||
final pwdCtrl = TextEditingController(
|
||||
text: Stores.setting.webdavPwd.fetch(),
|
||||
);
|
||||
final url = TextEditingController(text: Stores.setting.webdavUrl.fetch());
|
||||
final user = TextEditingController(text: Stores.setting.webdavUser.fetch());
|
||||
final pwd = TextEditingController(text: Stores.setting.webdavPwd.fetch());
|
||||
final result = await context.showRoundDialog<bool>(
|
||||
title: 'WebDAV',
|
||||
child: Column(
|
||||
@@ -360,15 +324,15 @@ class BackupPage extends StatelessWidget {
|
||||
Input(
|
||||
label: 'URL',
|
||||
hint: 'https://example.com/webdav/',
|
||||
controller: urlCtrl,
|
||||
controller: url,
|
||||
),
|
||||
Input(
|
||||
label: l10n.user,
|
||||
controller: userCtrl,
|
||||
controller: user,
|
||||
),
|
||||
Input(
|
||||
label: l10n.pwd,
|
||||
controller: pwdCtrl,
|
||||
controller: pwd,
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -382,15 +346,13 @@ class BackupPage extends StatelessWidget {
|
||||
],
|
||||
);
|
||||
if (result == true) {
|
||||
final result =
|
||||
await Webdav.test(urlCtrl.text, userCtrl.text, pwdCtrl.text);
|
||||
if (result == null) {
|
||||
context.showSnackBar(l10n.success);
|
||||
} else {
|
||||
final result = await Webdav.test(url.text, user.text, pwd.text);
|
||||
if (result != null) {
|
||||
context.showSnackBar(result);
|
||||
return;
|
||||
}
|
||||
Webdav.changeClient(urlCtrl.text, userCtrl.text, pwdCtrl.text);
|
||||
context.showSnackBar(l10n.success);
|
||||
Webdav.changeClient(url.text, user.text, pwd.text);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -430,9 +392,9 @@ class BackupPage extends StatelessWidget {
|
||||
),
|
||||
],
|
||||
);
|
||||
} catch (e, trace) {
|
||||
Loggers.app.warning('Import backup failed', e, trace);
|
||||
context.showSnackBar(e.toString());
|
||||
} catch (e, s) {
|
||||
Loggers.app.warning('Import backup failed', e, s);
|
||||
context.showErrDialog(e: e, s: s, operation: l10n.restore);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -467,11 +429,9 @@ class BackupPage extends StatelessWidget {
|
||||
);
|
||||
context.showSnackBar(l10n.success);
|
||||
}
|
||||
} catch (e) {
|
||||
context.showRoundDialog(
|
||||
title: l10n.error,
|
||||
child: Text(e.toString()),
|
||||
);
|
||||
} catch (e, s) {
|
||||
context.showErrDialog(e: e, s: s, operation: l10n.import);
|
||||
Loggers.app.warning('Import servers failed', e, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ class EditorPage extends StatefulWidget {
|
||||
});
|
||||
|
||||
@override
|
||||
_EditorPageState createState() => _EditorPageState();
|
||||
State<EditorPage> createState() => _EditorPageState();
|
||||
}
|
||||
|
||||
class _EditorPageState extends State<EditorPage> {
|
||||
|
||||
@@ -218,7 +218,7 @@ class _HomePageState extends State<HomePage>
|
||||
|
||||
Widget _buildDrawer() {
|
||||
return Drawer(
|
||||
surfaceTintColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
|
||||
@@ -16,7 +16,7 @@ final class WearHome extends StatefulWidget {
|
||||
const WearHome({super.key});
|
||||
|
||||
@override
|
||||
_WearHomeState createState() => _WearHomeState();
|
||||
State<WearHome> createState() => _WearHomeState();
|
||||
}
|
||||
|
||||
final class _WearHomeState extends State<WearHome> with AfterLayoutMixin {
|
||||
@@ -61,11 +61,30 @@ final class _WearHomeState extends State<WearHome> with AfterLayoutMixin {
|
||||
}
|
||||
|
||||
Widget _buildEachSever(Server srv) {
|
||||
return const Padding(
|
||||
padding: EdgeInsets.all(7),
|
||||
final mem = () {
|
||||
final total = srv.status.mem.total;
|
||||
final used = srv.status.mem.total - srv.status.mem.avail;
|
||||
return '${used.bytes2Str} / ${total.bytes2Str}';
|
||||
}();
|
||||
final disk = () {
|
||||
final total = srv.status.diskUsage?.size.kb2Str;
|
||||
final used = srv.status.diskUsage?.used.kb2Str;
|
||||
return '$used / $total';
|
||||
}();
|
||||
final net = '↓ ${srv.status.netSpeed.cachedRealVals.speedIn}'
|
||||
'↑ ${srv.status.netSpeed.cachedRealVals.speedOut}';
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(7),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [],
|
||||
children: [
|
||||
Text(srv.spi.name, style: UIs.text15Bold),
|
||||
UIs.height7,
|
||||
KvRow(k: 'CPU', v: '${srv.status.cpu.usedPercent()}%'),
|
||||
KvRow(k: 'Mem', v: mem),
|
||||
KvRow(k: 'Disk', v: disk),
|
||||
KvRow(k: 'Net', v: net)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ class PingPage extends StatefulWidget {
|
||||
const PingPage({super.key});
|
||||
|
||||
@override
|
||||
_PingPageState createState() => _PingPageState();
|
||||
State<PingPage> createState() => _PingPageState();
|
||||
}
|
||||
|
||||
class _PingPageState extends State<PingPage>
|
||||
|
||||
@@ -19,7 +19,7 @@ class PrivateKeyEditPage extends StatefulWidget {
|
||||
final PrivateKeyInfo? pki;
|
||||
|
||||
@override
|
||||
_PrivateKeyEditPageState createState() => _PrivateKeyEditPageState();
|
||||
State<PrivateKeyEditPage> createState() => _PrivateKeyEditPageState();
|
||||
}
|
||||
|
||||
class _PrivateKeyEditPageState extends State<PrivateKeyEditPage> {
|
||||
|
||||
@@ -15,7 +15,7 @@ class PrivateKeysListPage extends StatefulWidget {
|
||||
const PrivateKeysListPage({super.key});
|
||||
|
||||
@override
|
||||
_PrivateKeyListState createState() => _PrivateKeyListState();
|
||||
State<PrivateKeysListPage> createState() => _PrivateKeyListState();
|
||||
}
|
||||
|
||||
class _PrivateKeyListState extends State<PrivateKeysListPage>
|
||||
|
||||
@@ -16,7 +16,7 @@ class ProcessPage extends StatefulWidget {
|
||||
const ProcessPage({super.key, required this.spi});
|
||||
|
||||
@override
|
||||
_ProcessPageState createState() => _ProcessPageState();
|
||||
State<ProcessPage> createState() => _ProcessPageState();
|
||||
}
|
||||
|
||||
class _ProcessPageState extends State<ProcessPage> {
|
||||
|
||||
@@ -19,7 +19,7 @@ final class PvePage extends StatefulWidget {
|
||||
});
|
||||
|
||||
@override
|
||||
_PvePageState createState() => _PvePageState();
|
||||
State<PvePage> createState() => _PvePageState();
|
||||
}
|
||||
|
||||
const _kHorziPadding = 11.0;
|
||||
@@ -46,6 +46,7 @@ final class _PvePageState extends State<PvePage> {
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
_timer?.cancel();
|
||||
_pve.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -32,7 +32,7 @@ class ServerDetailPage extends StatefulWidget {
|
||||
final ServerPrivateInfo spi;
|
||||
|
||||
@override
|
||||
_ServerDetailPageState createState() => _ServerDetailPageState();
|
||||
State<ServerDetailPage> createState() => _ServerDetailPageState();
|
||||
}
|
||||
|
||||
class _ServerDetailPageState extends State<ServerDetailPage>
|
||||
|
||||
@@ -20,7 +20,7 @@ class ServerEditPage extends StatefulWidget {
|
||||
final ServerPrivateInfo? spi;
|
||||
|
||||
@override
|
||||
_ServerEditPageState createState() => _ServerEditPageState();
|
||||
State<ServerEditPage> createState() => _ServerEditPageState();
|
||||
}
|
||||
|
||||
class _ServerEditPageState extends State<ServerEditPage> {
|
||||
@@ -403,15 +403,27 @@ class _ServerEditPageState extends State<ServerEditPage> {
|
||||
}
|
||||
|
||||
List<Widget> _buildPVEs() {
|
||||
const addr = 'https://127.0.0.1:8006';
|
||||
return [
|
||||
const Text('PVE', style: UIs.text13Grey),
|
||||
UIs.height7,
|
||||
Input(
|
||||
controller: _pveAddrCtrl,
|
||||
type: TextInputType.url,
|
||||
icon: MingCute.web_line,
|
||||
label: l10n.addr,
|
||||
hint: 'https://example.com:8006',
|
||||
Autocomplete<String>(
|
||||
optionsBuilder: (val) {
|
||||
final v = val.text;
|
||||
if (v.startsWith(addr.substring(0, v.length))) {
|
||||
return [addr];
|
||||
}
|
||||
return [];
|
||||
},
|
||||
onSelected: (val) => _pveAddrCtrl.text = val,
|
||||
fieldViewBuilder: (_, ctrl, node, __) => Input(
|
||||
controller: ctrl,
|
||||
type: TextInputType.url,
|
||||
icon: MingCute.web_line,
|
||||
node: node,
|
||||
label: l10n.addr,
|
||||
hint: addr,
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
leading: const Padding(
|
||||
|
||||
@@ -25,7 +25,7 @@ class ServerPage extends StatefulWidget {
|
||||
const ServerPage({super.key});
|
||||
|
||||
@override
|
||||
_ServerPageState createState() => _ServerPageState();
|
||||
State<ServerPage> createState() => _ServerPageState();
|
||||
}
|
||||
|
||||
class _ServerPageState extends State<ServerPage>
|
||||
|
||||
@@ -23,7 +23,7 @@ class SettingPage extends StatefulWidget {
|
||||
const SettingPage({super.key});
|
||||
|
||||
@override
|
||||
_SettingPageState createState() => _SettingPageState();
|
||||
State<SettingPage> createState() => _SettingPageState();
|
||||
}
|
||||
|
||||
class _SettingPageState extends State<SettingPage> {
|
||||
@@ -461,14 +461,12 @@ class _SettingPageState extends State<SettingPage> {
|
||||
_setting.fontPath.put(path);
|
||||
} else {
|
||||
final fontFile = File(path);
|
||||
final newPath = '${Paths.fontPath}/${path.split('/').last}';
|
||||
await fontFile.copy(newPath);
|
||||
_setting.fontPath.put(newPath);
|
||||
await fontFile.copy(Paths.font);
|
||||
_setting.fontPath.put(Paths.font);
|
||||
}
|
||||
|
||||
context.pop();
|
||||
RebuildNodes.app.rebuild();
|
||||
return;
|
||||
}
|
||||
|
||||
Widget _buildTermFontSize() {
|
||||
@@ -1163,19 +1161,29 @@ class _SettingPageState extends State<SettingPage> {
|
||||
return ListTile(
|
||||
leading: const Icon(Icons.image),
|
||||
title: Text('Logo ${l10n.addr}'),
|
||||
subtitle: SimpleMarkdown(data: '[${l10n.doc}](${Urls.appWiki})'),
|
||||
trailing: const Icon(Icons.keyboard_arrow_right),
|
||||
onTap: () {
|
||||
final ctrl =
|
||||
TextEditingController(text: _setting.serverLogoUrl.fetch());
|
||||
context.showRoundDialog(
|
||||
title: 'Logo ${l10n.addr}',
|
||||
child: Input(
|
||||
controller: ctrl,
|
||||
autoFocus: true,
|
||||
hint: 'https://example.com/logo.png',
|
||||
icon: Icons.link,
|
||||
onSubmitted: onSave,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Input(
|
||||
controller: ctrl,
|
||||
autoFocus: true,
|
||||
hint: 'https://example.com/logo.png',
|
||||
icon: Icons.link,
|
||||
maxLines: 2,
|
||||
onSubmitted: onSave,
|
||||
),
|
||||
ListTile(
|
||||
title: Text(l10n.doc),
|
||||
trailing: const Icon(Icons.open_in_new),
|
||||
onTap: () => Urls.appWiki.launch(),
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:fl_lib/fl_lib.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:toolbox/core/extension/context/locale.dart';
|
||||
import 'package:toolbox/core/route.dart';
|
||||
import 'package:toolbox/data/res/store.dart';
|
||||
import 'package:toolbox/view/page/setting/platform/platform_pub.dart';
|
||||
import 'package:watch_connectivity/watch_connectivity.dart';
|
||||
|
||||
class AndroidSettingsPage extends StatefulWidget {
|
||||
const AndroidSettingsPage({super.key});
|
||||
|
||||
@override
|
||||
_AndroidSettingsPageState createState() => _AndroidSettingsPageState();
|
||||
State<AndroidSettingsPage> createState() => _AndroidSettingsPageState();
|
||||
}
|
||||
|
||||
class _AndroidSettingsPageState extends State<AndroidSettingsPage> {
|
||||
late SharedPreferences _sp;
|
||||
final wc = WatchConnectivity();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -34,6 +35,7 @@ class _AndroidSettingsPageState extends State<AndroidSettingsPage> {
|
||||
children: [
|
||||
_buildBgRun(),
|
||||
_buildAndroidWidgetSharedPreference(),
|
||||
_buildWatch(),
|
||||
if (BioAuth.isPlatformSupported)
|
||||
PlatformPublicSettings.buildBioAuth(),
|
||||
].map((e) => CardX(child: e)).toList(),
|
||||
@@ -49,10 +51,9 @@ class _AndroidSettingsPageState extends State<AndroidSettingsPage> {
|
||||
);
|
||||
}
|
||||
|
||||
void _saveWidgetSP(String data, Map<String, String> old) {
|
||||
void _saveWidgetSP(Map<String, String> map, Map<String, String> old) {
|
||||
context.pop();
|
||||
try {
|
||||
final map = Map<String, String>.from(json.decode(data));
|
||||
final keysDel = old.keys.toSet().difference(map.keys.toSet());
|
||||
for (final key in keysDel) {
|
||||
_sp.remove(key);
|
||||
@@ -70,7 +71,7 @@ class _AndroidSettingsPageState extends State<AndroidSettingsPage> {
|
||||
return ListTile(
|
||||
title: Text(l10n.homeWidgetUrlConfig),
|
||||
trailing: const Icon(Icons.keyboard_arrow_right),
|
||||
onTap: () {
|
||||
onTap: () async {
|
||||
final data = <String, String>{};
|
||||
_sp.getKeys().forEach((key) {
|
||||
final val = _sp.getString(key);
|
||||
@@ -78,25 +79,57 @@ class _AndroidSettingsPageState extends State<AndroidSettingsPage> {
|
||||
data[key] = val;
|
||||
}
|
||||
});
|
||||
final ctrl = TextEditingController(text: json.encode(data));
|
||||
context.showRoundDialog(
|
||||
title: l10n.homeWidgetUrlConfig,
|
||||
child: Input(
|
||||
autoFocus: true,
|
||||
controller: ctrl,
|
||||
label: 'JSON',
|
||||
type: TextInputType.visiblePassword,
|
||||
maxLines: 7,
|
||||
onSubmitted: (p0) => _saveWidgetSP(p0, data),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
_saveWidgetSP(ctrl.text, data);
|
||||
},
|
||||
child: Text(l10n.ok),
|
||||
),
|
||||
],
|
||||
final result = await AppRoutes.kvEditor(data: data).go(context);
|
||||
if (result != null) {
|
||||
if (result is Map<String, String>) {
|
||||
_saveWidgetSP(result, data);
|
||||
} else {
|
||||
final err = 'Save Android widget SharedPreference failed: '
|
||||
'unexpected type: ${result.runtimeType}';
|
||||
Loggers.app.warning(err);
|
||||
context.showRoundDialog(
|
||||
title: l10n.error,
|
||||
child: SingleChildScrollView(
|
||||
child: SimpleMarkdown(data: '$err\n\n```$result```'),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildWatch() {
|
||||
return FutureWidget(
|
||||
future: wc.isReachable,
|
||||
error: (e, s) {
|
||||
Loggers.app.warning('WatchOS error', e, s);
|
||||
return ListTile(
|
||||
title: const Text('Watch app'),
|
||||
subtitle: Text(l10n.viewErr, style: UIs.textGrey),
|
||||
trailing: const Icon(Icons.keyboard_arrow_right),
|
||||
onTap: () {
|
||||
context.showRoundDialog(
|
||||
title: l10n.error,
|
||||
child: SingleChildScrollView(
|
||||
child: SimpleMarkdown(data: '${e.toString()}\n```$s```'),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
success: (val) {
|
||||
if (val == null) {
|
||||
return ListTile(
|
||||
title: const Text('Watch app'),
|
||||
subtitle: Text(l10n.watchNotPaired, style: UIs.textGrey),
|
||||
);
|
||||
}
|
||||
return ListTile(
|
||||
title: const Text('Watch app'),
|
||||
subtitle: Text(l10n.sync, style: UIs.textGrey),
|
||||
trailing: const Icon(Icons.keyboard_arrow_right),
|
||||
onTap: () async {},
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:fl_lib/fl_lib.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:toolbox/core/extension/context/locale.dart';
|
||||
import 'package:toolbox/core/route.dart';
|
||||
import 'package:toolbox/core/utils/misc.dart';
|
||||
import 'package:toolbox/data/res/misc.dart';
|
||||
import 'package:toolbox/data/res/store.dart';
|
||||
import 'package:toolbox/view/page/setting/platform/platform_pub.dart';
|
||||
import 'package:watch_connectivity/watch_connectivity.dart';
|
||||
@@ -14,7 +11,7 @@ class IOSSettingsPage extends StatefulWidget {
|
||||
const IOSSettingsPage({super.key});
|
||||
|
||||
@override
|
||||
_IOSSettingsPageState createState() => _IOSSettingsPageState();
|
||||
State<IOSSettingsPage> createState() => _IOSSettingsPageState();
|
||||
}
|
||||
|
||||
class _IOSSettingsPageState extends State<IOSSettingsPage> {
|
||||
@@ -83,7 +80,7 @@ class _IOSSettingsPageState extends State<IOSSettingsPage> {
|
||||
}
|
||||
|
||||
Widget _buildWatchApp() {
|
||||
return FutureWidget<Map<String, dynamic>?>(
|
||||
return FutureWidget(
|
||||
future: () async {
|
||||
if (!await wc.isPaired) {
|
||||
return null;
|
||||
@@ -115,19 +112,12 @@ class _IOSSettingsPageState extends State<IOSSettingsPage> {
|
||||
}
|
||||
|
||||
void _onTapWatchApp(Map<String, dynamic> map) async {
|
||||
/// Encode [map] to String with indent `\t`
|
||||
final text = Miscs.jsonEncoder.convert(map);
|
||||
final result = await AppRoutes.editor(
|
||||
text: text,
|
||||
langCode: 'json',
|
||||
title: 'Watch app',
|
||||
).go<String>(context);
|
||||
if (result == null) {
|
||||
return;
|
||||
}
|
||||
final urls = Map<String, String>.from(map['urls'] as Map? ?? {});
|
||||
final result = await AppRoutes.kvEditor(data: urls).go(context);
|
||||
if (result == null || result! is Map<String, String>) return;
|
||||
|
||||
try {
|
||||
final newCtx = json.decode(result) as Map<String, dynamic>;
|
||||
await wc.updateApplicationContext(newCtx);
|
||||
await wc.updateApplicationContext({'urls': result});
|
||||
} catch (e, trace) {
|
||||
context.showRoundDialog(
|
||||
title: l10n.error,
|
||||
|
||||
@@ -8,7 +8,7 @@ class ServerOrderPage extends StatefulWidget {
|
||||
const ServerOrderPage({super.key});
|
||||
|
||||
@override
|
||||
_ServerOrderPageState createState() => _ServerOrderPageState();
|
||||
State<ServerOrderPage> createState() => _ServerOrderPageState();
|
||||
}
|
||||
|
||||
class _ServerOrderPageState extends State<ServerOrderPage> {
|
||||
|
||||
@@ -8,7 +8,7 @@ class SSHVirtKeySettingPage extends StatefulWidget {
|
||||
const SSHVirtKeySettingPage({super.key});
|
||||
|
||||
@override
|
||||
_SSHVirtKeySettingPageState createState() => _SSHVirtKeySettingPageState();
|
||||
State<SSHVirtKeySettingPage> createState() => _SSHVirtKeySettingPageState();
|
||||
}
|
||||
|
||||
class _SSHVirtKeySettingPageState extends State<SSHVirtKeySettingPage> {
|
||||
|
||||
@@ -13,7 +13,7 @@ class SnippetEditPage extends StatefulWidget {
|
||||
final Snippet? snippet;
|
||||
|
||||
@override
|
||||
_SnippetEditPageState createState() => _SnippetEditPageState();
|
||||
State<SnippetEditPage> createState() => _SnippetEditPageState();
|
||||
}
|
||||
|
||||
class _SnippetEditPageState extends State<SnippetEditPage>
|
||||
|
||||
@@ -12,7 +12,7 @@ class SnippetListPage extends StatefulWidget {
|
||||
const SnippetListPage({super.key});
|
||||
|
||||
@override
|
||||
_SnippetListPageState createState() => _SnippetListPageState();
|
||||
State<SnippetListPage> createState() => _SnippetListPageState();
|
||||
}
|
||||
|
||||
class _SnippetListPageState extends State<SnippetListPage> {
|
||||
|
||||
@@ -30,7 +30,7 @@ class SSHPage extends StatefulWidget {
|
||||
final String? initCmd;
|
||||
final bool notFromTab;
|
||||
final Function()? onSessionEnd;
|
||||
final FocusNode? focus;
|
||||
final GlobalKey<TerminalViewState>? terminalKey;
|
||||
|
||||
const SSHPage({
|
||||
super.key,
|
||||
@@ -38,11 +38,11 @@ class SSHPage extends StatefulWidget {
|
||||
this.initCmd,
|
||||
this.notFromTab = true,
|
||||
this.onSessionEnd,
|
||||
this.focus,
|
||||
this.terminalKey,
|
||||
});
|
||||
|
||||
@override
|
||||
_SSHPageState createState() => _SSHPageState();
|
||||
State<SSHPage> createState() => _SSHPageState();
|
||||
}
|
||||
|
||||
const _horizonPadding = 7.0;
|
||||
@@ -52,7 +52,7 @@ class _SSHPageState extends State<SSHPage> with AutomaticKeepAliveClientMixin {
|
||||
late final _terminal = Terminal(inputHandler: _keyboard);
|
||||
final TerminalController _terminalController = TerminalController();
|
||||
final List<List<VirtKey>> _virtKeysList = [];
|
||||
late final _focus = widget.focus ?? FocusNode();
|
||||
late final _termKey = widget.terminalKey ?? GlobalKey<TerminalViewState>();
|
||||
|
||||
late MediaQueryData _media;
|
||||
late TerminalStyle _terminalStyle;
|
||||
@@ -144,8 +144,8 @@ class _SSHPageState extends State<SSHPage> with AutomaticKeepAliveClientMixin {
|
||||
),
|
||||
child: TerminalView(
|
||||
_terminal,
|
||||
key: _termKey,
|
||||
controller: _terminalController,
|
||||
focusNode: _focus,
|
||||
keyboardType: TextInputType.text,
|
||||
enableSuggestions: true,
|
||||
textStyle: _terminalStyle,
|
||||
@@ -282,11 +282,7 @@ class _SSHPageState extends State<SSHPage> with AutomaticKeepAliveClientMixin {
|
||||
Future<void> _doVirtualKeyFunc(VirtualKeyFunc type) async {
|
||||
switch (type) {
|
||||
case VirtualKeyFunc.toggleIME:
|
||||
if (!_focus.hasFocus) {
|
||||
_focus.requestFocus();
|
||||
} else {
|
||||
_focus.unfocus();
|
||||
}
|
||||
_termKey.currentState?.toggleFocus();
|
||||
break;
|
||||
case VirtualKeyFunc.backspace:
|
||||
_terminal.keyInput(TerminalKey.backspace);
|
||||
|
||||
@@ -17,8 +17,8 @@ class SSHTabPage extends StatefulWidget {
|
||||
|
||||
class _SSHTabPageState extends State<SSHTabPage>
|
||||
with TickerProviderStateMixin, AutomaticKeepAliveClientMixin {
|
||||
late final _tabMap = <String, ({Widget page, FocusNode? focus})>{
|
||||
l10n.add: (page: _buildAddPage(), focus: null),
|
||||
late final _tabMap = <String, ({Widget page})>{
|
||||
l10n.add: (page: _buildAddPage()),
|
||||
};
|
||||
late var _tabController = TabController(
|
||||
length: _tabMap.length,
|
||||
@@ -38,8 +38,7 @@ class _SSHTabPageState extends State<SSHTabPage>
|
||||
dividerColor: Colors.transparent,
|
||||
onTap: (value) {
|
||||
_fabRN.value = value;
|
||||
final mapKey = _tabMap.keys.elementAt(value);
|
||||
_tabMap[mapKey]?.focus?.requestFocus();
|
||||
FocusScope.of(context).unfocus();
|
||||
},
|
||||
),
|
||||
body: _buildBody(),
|
||||
@@ -70,20 +69,27 @@ class _SSHTabPageState extends State<SSHTabPage>
|
||||
IconBtn(
|
||||
icon: Icons.close,
|
||||
onTap: () async {
|
||||
final confirm = await context.showRoundDialog<bool>(
|
||||
title: l10n.attention,
|
||||
child: Text('${l10n.close} SSH ${l10n.conn}($e) ?'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => context.pop(true),
|
||||
child: Text(l10n.ok, style: UIs.textRed),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () => context.pop(false),
|
||||
child: Text(l10n.cancel),
|
||||
),
|
||||
],
|
||||
final confirm = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
title: Text(l10n.attention),
|
||||
content: Text('${l10n.close} SSH ${l10n.conn}($e) ?'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => context.pop(true),
|
||||
child: Text(l10n.ok, style: UIs.textRed),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () => context.pop(false),
|
||||
child: Text(l10n.cancel),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
Future.delayed(const Duration(milliseconds: 50),
|
||||
FocusScope.of(context).unfocus);
|
||||
if (confirm != true) {
|
||||
return;
|
||||
}
|
||||
@@ -142,18 +148,15 @@ class _SSHTabPageState extends State<SSHTabPage>
|
||||
}
|
||||
return spi.name;
|
||||
}();
|
||||
final focus = FocusNode();
|
||||
_tabMap[name] = (
|
||||
page: SSHPage(
|
||||
spi: spi,
|
||||
focus: focus,
|
||||
notFromTab: false,
|
||||
onSessionEnd: () {
|
||||
_tabMap.remove(name);
|
||||
_refreshTabs();
|
||||
},
|
||||
),
|
||||
focus: focus,
|
||||
);
|
||||
_refreshTabs();
|
||||
final idx = _tabMap.length - 1;
|
||||
|
||||
@@ -32,7 +32,7 @@ class SftpPage extends StatefulWidget {
|
||||
});
|
||||
|
||||
@override
|
||||
_SftpPageState createState() => _SftpPageState();
|
||||
State<SftpPage> createState() => _SftpPageState();
|
||||
}
|
||||
|
||||
class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
|
||||
@@ -494,19 +494,22 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
|
||||
onPressed: () async {
|
||||
context.pop();
|
||||
try {
|
||||
await context.showLoadingDialog(fn: () async {
|
||||
final remotePath = _getRemotePath(file);
|
||||
if (useRmr) {
|
||||
await _client!.run('rm -r "$remotePath"');
|
||||
} else if (file.attr.isDirectory) {
|
||||
await _status.client!.rmdir(remotePath);
|
||||
} else {
|
||||
await _status.client!.remove(remotePath);
|
||||
}
|
||||
});
|
||||
await context.showLoadingDialog(
|
||||
fn: () async {
|
||||
final remotePath = _getRemotePath(file);
|
||||
if (useRmr) {
|
||||
await _client!.run('rm -r "$remotePath"');
|
||||
} else if (file.attr.isDirectory) {
|
||||
await _status.client!.rmdir(remotePath);
|
||||
} else {
|
||||
await _status.client!.remove(remotePath);
|
||||
}
|
||||
},
|
||||
onErr: (e, s) {},
|
||||
);
|
||||
_listDir();
|
||||
} catch (e, s) {
|
||||
_showErrDialog(context, e, 'Delete', s);
|
||||
context.showErrDialog(e: e, s: s, operation: l10n.delete);
|
||||
}
|
||||
},
|
||||
child: Text(l10n.delete, style: UIs.textRed),
|
||||
@@ -547,13 +550,16 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
|
||||
}
|
||||
context.pop();
|
||||
try {
|
||||
await context.showLoadingDialog(fn: () async {
|
||||
final dir = '${_status.path!.path}/${textController.text}';
|
||||
await _status.client!.mkdir(dir);
|
||||
});
|
||||
await context.showLoadingDialog(
|
||||
fn: () async {
|
||||
final dir = '${_status.path!.path}/${textController.text}';
|
||||
await _status.client!.mkdir(dir);
|
||||
},
|
||||
onErr: (e, s) {},
|
||||
);
|
||||
_listDir();
|
||||
} catch (e, s) {
|
||||
_showErrDialog(context, e, 'Create folder', s);
|
||||
context.showErrDialog(e: e, s: s, operation: l10n.createFolder);
|
||||
}
|
||||
},
|
||||
child: Text(l10n.ok, style: UIs.textRed),
|
||||
@@ -591,13 +597,16 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
|
||||
}
|
||||
context.pop();
|
||||
try {
|
||||
await context.showLoadingDialog(fn: () async {
|
||||
final path = '${_status.path!.path}/${textController.text}';
|
||||
await _client!.run('touch "$path"');
|
||||
});
|
||||
await context.showLoadingDialog(
|
||||
fn: () async {
|
||||
final path = '${_status.path!.path}/${textController.text}';
|
||||
await _client!.run('touch "$path"');
|
||||
},
|
||||
onErr: (e, s) {},
|
||||
);
|
||||
_listDir();
|
||||
} catch (e, s) {
|
||||
_showErrDialog(context, e, 'Create file', s);
|
||||
context.showErrDialog(e: e, s: s, operation: l10n.createFile);
|
||||
}
|
||||
},
|
||||
child: Text(l10n.ok, style: UIs.textRed),
|
||||
@@ -636,13 +645,16 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
|
||||
}
|
||||
context.pop();
|
||||
try {
|
||||
await context.showLoadingDialog(fn: () async {
|
||||
final newName = textController.text;
|
||||
await _status.client?.rename(file.filename, newName);
|
||||
});
|
||||
await context.showLoadingDialog(
|
||||
fn: () async {
|
||||
final newName = textController.text;
|
||||
await _status.client?.rename(file.filename, newName);
|
||||
},
|
||||
onErr: (e, s) {},
|
||||
);
|
||||
_listDir();
|
||||
} catch (e, s) {
|
||||
_showErrDialog(context, e, 'Rename', s);
|
||||
context.showErrDialog(e: e, s: s, operation: l10n.rename);
|
||||
}
|
||||
},
|
||||
child: Text(l10n.rename, style: UIs.textRed),
|
||||
@@ -672,29 +684,6 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
|
||||
_listDir();
|
||||
}
|
||||
|
||||
Future<void> _showErrDialog(
|
||||
BuildContext ctx, Object e, String op, StackTrace s) async {
|
||||
Loggers.app.warning('$op failed', e, s);
|
||||
return ctx.showRoundDialog(
|
||||
title: l10n.error,
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
Text(e.toString()),
|
||||
const SizedBox(height: 7),
|
||||
SimpleMarkdown(data: s.toString()),
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => ctx.pop(),
|
||||
child: Text(l10n.ok),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
String _getRemotePath(SftpName name) {
|
||||
final prePath = _status.path!.path;
|
||||
// Only support Linux as remote now, so the seperator is '/'
|
||||
|
||||
@@ -12,7 +12,7 @@ class SftpMissionPage extends StatefulWidget {
|
||||
const SftpMissionPage({super.key});
|
||||
|
||||
@override
|
||||
_SftpMissionPageState createState() => _SftpMissionPageState();
|
||||
State<SftpMissionPage> createState() => _SftpMissionPageState();
|
||||
}
|
||||
|
||||
class _SftpMissionPageState extends State<SftpMissionPage> {
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <dynamic_color/dynamic_color_plugin.h>
|
||||
#include <gtk/gtk_plugin.h>
|
||||
#include <screen_retriever/screen_retriever_plugin.h>
|
||||
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||
#include <window_manager/window_manager_plugin.h>
|
||||
@@ -16,9 +15,6 @@ void fl_register_plugins(FlPluginRegistry* registry) {
|
||||
g_autoptr(FlPluginRegistrar) dynamic_color_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "DynamicColorPlugin");
|
||||
dynamic_color_plugin_register_with_registrar(dynamic_color_registrar);
|
||||
g_autoptr(FlPluginRegistrar) gtk_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "GtkPlugin");
|
||||
gtk_plugin_register_with_registrar(gtk_registrar);
|
||||
g_autoptr(FlPluginRegistrar) screen_retriever_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverPlugin");
|
||||
screen_retriever_plugin_register_with_registrar(screen_retriever_registrar);
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
dynamic_color
|
||||
gtk
|
||||
screen_retriever
|
||||
url_launcher_linux
|
||||
window_manager
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
import FlutterMacOS
|
||||
import Foundation
|
||||
|
||||
import app_links
|
||||
import device_info_plus
|
||||
import dynamic_color
|
||||
import icloud_storage
|
||||
@@ -19,7 +18,6 @@ import wakelock_plus
|
||||
import window_manager
|
||||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin"))
|
||||
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
||||
DynamicColorPlugin.register(with: registry.registrar(forPlugin: "DynamicColorPlugin"))
|
||||
IcloudStoragePlugin.register(with: registry.registrar(forPlugin: "IcloudStoragePlugin"))
|
||||
|
||||
@@ -471,7 +471,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 918;
|
||||
CURRENT_PROJECT_VERSION = 923;
|
||||
DEVELOPMENT_TEAM = BA88US33G6;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "Server Box";
|
||||
@@ -481,7 +481,7 @@
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MARKETING_VERSION = 1.0.918;
|
||||
MARKETING_VERSION = 1.0.923;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
||||
PRODUCT_NAME = "Server Box";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
@@ -608,7 +608,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 918;
|
||||
CURRENT_PROJECT_VERSION = 923;
|
||||
DEVELOPMENT_TEAM = BA88US33G6;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "Server Box";
|
||||
@@ -618,7 +618,7 @@
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MARKETING_VERSION = 1.0.918;
|
||||
MARKETING_VERSION = 1.0.923;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
||||
PRODUCT_NAME = "Server Box";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
@@ -638,7 +638,7 @@
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "3rd Party Mac Developer Application";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 918;
|
||||
CURRENT_PROJECT_VERSION = 923;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=macosx*]" = BA88US33G6;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
@@ -649,7 +649,7 @@
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MARKETING_VERSION = 1.0.918;
|
||||
MARKETING_VERSION = 1.0.923;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
||||
PRODUCT_NAME = "Server Box";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
|
||||
103
pubspec.lock
103
pubspec.lock
@@ -33,22 +33,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.2"
|
||||
app_links:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: app_links
|
||||
sha256: "96e677810b83707ff5e10fac11e4839daa0ea4e0123c35864c092699165eb3db"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.1"
|
||||
archive:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: archive
|
||||
sha256: ecf4273855368121b1caed0d10d4513c7241dfc813f7d3c8933b36622ae9b265
|
||||
sha256: "6bd38d335f0954f5fad9c79e614604fbf03a0e5b975923dd001b6ea965ef5b4b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.5.1"
|
||||
version: "3.6.0"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -101,10 +93,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_daemon
|
||||
sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1"
|
||||
sha256: "79b2aef6ac2ed00046867ed354c88778c9c0f029df8a20fe10b5436826721ef9"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.1"
|
||||
version: "4.0.2"
|
||||
build_resolvers:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -117,10 +109,10 @@ packages:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: build_runner
|
||||
sha256: "3ac61a79bfb6f6cc11f693591063a7f19a7af628dc52f141743edac5c16e8c22"
|
||||
sha256: "1414d6d733a85d8ad2f1dfcb3ea7945759e35a123cb99ccfac75d0758f75edfa"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.9"
|
||||
version: "2.4.10"
|
||||
build_runner_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -231,10 +223,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: countly_flutter
|
||||
sha256: "829853407896350bdb4881ddc92326cf87b8d70ad92a78c4775cd05666d5a965"
|
||||
sha256: "366f0c7769b998a06579235b0afd3797bc0bc7c8ce80bb673fca59c217f66012"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "24.4.0"
|
||||
version: "24.4.1"
|
||||
cross_file:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -336,10 +328,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: extended_image
|
||||
sha256: d7f091d068fcac7246c4b22a84b8dac59a62e04d29a5c172710c696e67a22f94
|
||||
sha256: "9786aab821aac117763d6e4419cd49f5031fbaacfe3fd212c5b313d0334c37a9"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.2.0"
|
||||
version: "8.2.1"
|
||||
extended_image_library:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -392,9 +384,9 @@ packages:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
path: "."
|
||||
ref: "8e1e3500fac2a39a43bed68aeb3cbdf792a1bd35"
|
||||
resolved-ref: "8e1e3500fac2a39a43bed68aeb3cbdf792a1bd35"
|
||||
url: "https://github.com/lollipopkit/fl_build.git"
|
||||
ref: "v1.0.13"
|
||||
resolved-ref: baded8103bd046806929b8d6547d5bce38060b06
|
||||
url: "https://github.com/lppcg/fl_build.git"
|
||||
source: git
|
||||
version: "1.0.0"
|
||||
fl_chart:
|
||||
@@ -409,9 +401,9 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "."
|
||||
ref: main
|
||||
resolved-ref: a2aa5359253ad83000ff2612ed2c5729cb0dcfc5
|
||||
url: "https://github.com/lollipopkit/fl_lib"
|
||||
ref: "v1.0.31"
|
||||
resolved-ref: f6322c5c2fad5230d1a882e79d52d1a0d246b8f4
|
||||
url: "https://github.com/lppcg/fl_lib"
|
||||
source: git
|
||||
version: "0.0.1"
|
||||
flutter:
|
||||
@@ -500,10 +492,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_markdown_latex
|
||||
sha256: b07ade84e661a7a2e7b7e59bcfa95e481d38fd8782a8f9f2349a8cb1b056ae89
|
||||
sha256: "6902b6774800fd5d7b594fa2080781586f1b851f0964d41bb7fd28c61e3ef2a7"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.2"
|
||||
version: "0.3.3"
|
||||
flutter_math_fork:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -570,14 +562,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.1"
|
||||
gtk:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: gtk
|
||||
sha256: e8ce9ca4b1df106e4d72dad201d345ea1a036cc12c360f1a7d5a758f78ffa42c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
highlight:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -670,10 +654,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: image
|
||||
sha256: "4c68bfd5ae83e700b5204c1e74451e7bf3cf750e6843c6e158289cf56bda018e"
|
||||
sha256: "2237616a36c0d69aef7549ab439b833fb7f9fb9fc861af2cc9ac3eedddd69ca8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.7"
|
||||
version: "4.2.0"
|
||||
intl:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -766,10 +750,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: local_auth_darwin
|
||||
sha256: "959145a4cf6f0de745b9ec9ac60101270eb4c5b8b7c2a0470907014adc1c618d"
|
||||
sha256: e424ebf90d5233452be146d4a7da4bcd7a70278b67791592f3fde1bda8eef9e2
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
version: "1.3.1"
|
||||
local_auth_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1166,10 +1150,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf_web_socket
|
||||
sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1"
|
||||
sha256: "073c147238594ecd0d193f3456a5fe91c4b0abbcc68bf5cd95b36c4e194ac611"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.4"
|
||||
version: "2.0.0"
|
||||
shortid:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1307,10 +1291,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_android
|
||||
sha256: "360a6ed2027f18b73c8d98e159dda67a61b7f2e0f6ec26e86c3ada33b0621775"
|
||||
sha256: "17cd5e205ea615e2c6ea7a77323a11712dffa0720a8a90540db57a01347f9ad9"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.3.1"
|
||||
version: "6.3.2"
|
||||
url_launcher_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1434,20 +1418,19 @@ packages:
|
||||
watch_connectivity:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "."
|
||||
ref: master
|
||||
resolved-ref: ad5f151a5591b64d384ef22b7855b7a2a53ad3d3
|
||||
url: "https://github.com/lollipopkit/watch_connectivity"
|
||||
source: git
|
||||
version: "0.1.5"
|
||||
name: watch_connectivity
|
||||
sha256: a4257a314601c8448662d1a91421321a2e8a3207937fec4792116f1270ea8766
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.0"
|
||||
watch_connectivity_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: watch_connectivity_platform_interface
|
||||
sha256: "9074115391bd764c08a17346fcbc4d5c0b555672defbe6928ac648503b54aa9c"
|
||||
sha256: "82e8f165eac779d71bff7f6269a8be530798311cf7f3c33f1594d93468747d11"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.2"
|
||||
version: "0.2.0"
|
||||
watcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1464,14 +1447,22 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.1"
|
||||
web_socket:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web_socket
|
||||
sha256: "217f49b5213796cb508d6a942a5dc604ce1cb6a0a6b3d8cb3f0c314f0ecea712"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.4"
|
||||
web_socket_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web_socket_channel
|
||||
sha256: "58c6666b342a38816b2e7e50ed0f1e261959630becd4c879c4f26bfa14aa5a42"
|
||||
sha256: a2d56211ee4d35d9b344d9d4ce60f362e4f5d1aafb988302906bd732bc731276
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.5"
|
||||
version: "3.0.0"
|
||||
webdav_client:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1485,10 +1476,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: win32
|
||||
sha256: "0eaf06e3446824099858367950a813472af675116bf63f008a4c2a75ae13e9cb"
|
||||
sha256: a79dbe579cb51ecd6d30b17e0cae4e0ea15e2c0e66f69ad4198f22a6789e94f4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.5.0"
|
||||
version: "5.5.1"
|
||||
win32_registry:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1525,8 +1516,8 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "."
|
||||
ref: a343bc2fdc11fbc7dbfc1f170692426a6fe01cb9
|
||||
resolved-ref: a343bc2fdc11fbc7dbfc1f170692426a6fe01cb9
|
||||
ref: master
|
||||
resolved-ref: "13a280e77dd077b439af24ad3d054d318ae5df4a"
|
||||
url: "https://github.com/lollipopkit/xterm.dart"
|
||||
source: git
|
||||
version: "4.0.0"
|
||||
|
||||
53
pubspec.yaml
53
pubspec.yaml
@@ -15,56 +15,53 @@ dependencies:
|
||||
hive_flutter: ^1.1.0
|
||||
dio: ^5.2.1
|
||||
after_layout: ^1.1.0
|
||||
dartssh2:
|
||||
git:
|
||||
ref: dev
|
||||
url: https://github.com/lollipopkit/dartssh2
|
||||
circle_chart:
|
||||
git:
|
||||
url: https://github.com/lollipopkit/circle_chart
|
||||
ref: main
|
||||
easy_isolate: ^1.3.0
|
||||
intl: ^0.19.0
|
||||
xterm:
|
||||
git:
|
||||
ref: a343bc2fdc11fbc7dbfc1f170692426a6fe01cb9
|
||||
url: https://github.com/lollipopkit/xterm.dart
|
||||
plain_notification_token: ^0.0.4
|
||||
highlight: ^0.7.0
|
||||
flutter_highlight: ^0.7.0
|
||||
code_text_field: ^1.1.0
|
||||
shared_preferences: ^2.1.1
|
||||
dynamic_color: ^1.6.6
|
||||
watch_connectivity:
|
||||
git:
|
||||
ref: master
|
||||
url: https://github.com/lollipopkit/watch_connectivity
|
||||
watch_connectivity: ^0.2.0
|
||||
#flutter_secure_storage: ^9.0.0
|
||||
xml: ^6.4.2 # for parsing nvidia-smi
|
||||
flutter_displaymode: ^0.6.0
|
||||
computer:
|
||||
git:
|
||||
ref: master
|
||||
url: https://github.com/lollipopkit/dart_computer
|
||||
flutter_background_service: ^5.0.5
|
||||
fl_chart: ^0.67.0
|
||||
wakelock_plus: ^1.2.4
|
||||
extended_image: ^8.2.0
|
||||
wake_on_lan: ^4.1.1+3
|
||||
flutter_adaptive_scaffold: ^0.1.10+2
|
||||
device_info_plus: ^10.1.0
|
||||
extended_image: ^8.2.1
|
||||
dartssh2:
|
||||
git:
|
||||
url: https://github.com/lollipopkit/dartssh2
|
||||
ref: dev
|
||||
circle_chart:
|
||||
git:
|
||||
url: https://github.com/lollipopkit/circle_chart
|
||||
ref: main
|
||||
xterm:
|
||||
git:
|
||||
url: https://github.com/lollipopkit/xterm.dart
|
||||
ref: master
|
||||
computer:
|
||||
git:
|
||||
url: https://github.com/lollipopkit/dart_computer
|
||||
ref: master
|
||||
fl_lib:
|
||||
git:
|
||||
url: https://github.com/lollipopkit/fl_lib
|
||||
ref: main
|
||||
device_info_plus: ^10.1.0
|
||||
url: https://github.com/lppcg/fl_lib
|
||||
ref: v1.0.31
|
||||
|
||||
dependency_overrides:
|
||||
# dartssh2:
|
||||
# path: ../dartssh2
|
||||
# fl_lib:
|
||||
# path: ../fl_lib
|
||||
# xterm:
|
||||
# path: ../xterm.dart
|
||||
# xterm:
|
||||
# path: ../xterm.dart
|
||||
|
||||
dev_dependencies:
|
||||
flutter_native_splash: ^2.1.6
|
||||
@@ -76,8 +73,8 @@ dev_dependencies:
|
||||
fl_build:
|
||||
# path: ../fl_build
|
||||
git:
|
||||
url: https://github.com/lollipopkit/fl_build.git
|
||||
ref: 8e1e3500fac2a39a43bed68aeb3cbdf792a1bd35
|
||||
url: https://github.com/lppcg/fl_build.git
|
||||
ref: v1.0.13
|
||||
|
||||
flutter:
|
||||
generate: true
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <app_links/app_links_plugin_c_api.h>
|
||||
#include <dynamic_color/dynamic_color_plugin_c_api.h>
|
||||
#include <local_auth_windows/local_auth_plugin.h>
|
||||
#include <permission_handler_windows/permission_handler_windows_plugin.h>
|
||||
@@ -16,8 +15,6 @@
|
||||
#include <window_manager/window_manager_plugin.h>
|
||||
|
||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
AppLinksPluginCApiRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("AppLinksPluginCApi"));
|
||||
DynamicColorPluginCApiRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("DynamicColorPluginCApi"));
|
||||
LocalAuthPluginRegisterWithRegistrar(
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
app_links
|
||||
dynamic_color
|
||||
local_auth_windows
|
||||
permission_handler_windows
|
||||
|
||||
Reference in New Issue
Block a user