Compare commits
121 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aaa69f0f95 | ||
|
|
64676bc5cb | ||
|
|
a15c04956c | ||
|
|
e3c885483b | ||
|
|
493c86cacb | ||
|
|
ea7c8caf14 | ||
|
|
9db04a60c2 | ||
|
|
610f46da0d | ||
|
|
b8e5418ff2 | ||
|
|
0e21755acb | ||
|
|
73248011a1 | ||
|
|
969643d3df | ||
|
|
c90d0e4b3b | ||
|
|
f9aadc6b0f | ||
|
|
8fd4cc1fe1 | ||
|
|
432d76f024 | ||
|
|
ca8211e1a4 | ||
|
|
a3b48fc01c | ||
|
|
8be94aa09c | ||
|
|
5db1253ab8 | ||
|
|
ceedd86310 | ||
|
|
6a0254623f | ||
|
|
1c6ec56032 | ||
|
|
287869ed45 | ||
|
|
e4dbc3ba12 | ||
|
|
426e5689f8 | ||
|
|
afda5fd4a4 | ||
|
|
0a21b2820c | ||
|
|
87b3b76b0b | ||
|
|
41ec46f1d3 | ||
|
|
7a359588db | ||
|
|
255abe8b11 | ||
|
|
b0936c5e6e | ||
|
|
2907ac74d4 | ||
|
|
ea678f37b0 | ||
|
|
076082c945 | ||
|
|
5ee98f90e8 | ||
|
|
c988dd88d7 | ||
|
|
f7d6c461dc | ||
|
|
14771ae946 | ||
|
|
7e9086b20e | ||
|
|
4b3c4870ba | ||
|
|
43cebd0c04 | ||
|
|
5ce13109b0 | ||
|
|
6e428c91d1 | ||
|
|
4430045550 | ||
|
|
282cb06091 | ||
|
|
772c2743b5 | ||
|
|
90199b89a5 | ||
|
|
e1d2e3f3e5 | ||
|
|
3f9fe1f2c6 | ||
|
|
c79bbc5756 | ||
|
|
63e1bec2b9 | ||
|
|
d26b7c6f75 | ||
|
|
da8dc4fa54 | ||
|
|
413b81c47f | ||
|
|
9ef419e3df | ||
|
|
39893912d9 | ||
|
|
49456ca6c3 | ||
|
|
6b9b8f0dbb | ||
|
|
5eb48b2717 | ||
|
|
5339cfca70 | ||
|
|
1462b2d0b8 | ||
|
|
3798a23183 | ||
|
|
ddaf916170 | ||
|
|
d6e37b058f | ||
|
|
2e9ad7d7cb | ||
|
|
190da74f66 | ||
|
|
f1315dda7f | ||
|
|
43e6105eb3 | ||
|
|
d785209eb6 | ||
|
|
da8b6a9010 | ||
|
|
1fd68722da | ||
|
|
c9a2c1d0e4 | ||
|
|
161f536a62 | ||
|
|
1a32e9944e | ||
|
|
6deb753198 | ||
|
|
4e33a98631 | ||
|
|
dcb9464d8f | ||
|
|
b94936b29f | ||
|
|
108d0a5a5b | ||
|
|
4814a2de28 | ||
|
|
5d8eeff502 | ||
|
|
0bc4087266 | ||
|
|
921209b901 | ||
|
|
fa9d754470 | ||
|
|
1f50a1f0f4 | ||
|
|
80e84c0421 | ||
|
|
5059872c3f | ||
|
|
8add244776 | ||
|
|
04e23fd7e4 | ||
|
|
336b11b808 | ||
|
|
8d9dba361c | ||
|
|
64ce3638cb | ||
|
|
f3ceb73f0e | ||
|
|
131dbe934c | ||
|
|
58288e89bd | ||
|
|
22c43c7124 | ||
|
|
2bf0b25ee5 | ||
|
|
3bc03c1364 | ||
|
|
490d71f8c9 | ||
|
|
edceb5900e | ||
|
|
e5ef28415b | ||
|
|
46b98df153 | ||
|
|
9f34021c90 | ||
|
|
8121eef839 | ||
|
|
da48d1f66c | ||
|
|
b167287c5b | ||
|
|
41f9da6bf8 | ||
|
|
e7c7fc8186 | ||
|
|
b950dd2d68 | ||
|
|
6d34de14d3 | ||
|
|
a5a84c0cdd | ||
|
|
701b1b811f | ||
|
|
97267cdfbf | ||
|
|
40ce37d230 | ||
|
|
8a9ade355c | ||
|
|
9bffec64b5 | ||
|
|
a03ee2ae0e | ||
|
|
ee889235fe | ||
|
|
94d6d80497 |
14
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# These are supported funding model platforms
|
||||||
|
|
||||||
|
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||||
|
patreon: # Replace with a single Patreon username
|
||||||
|
open_collective: # Replace with a single Open Collective username
|
||||||
|
ko_fi: lollipopkit
|
||||||
|
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||||
|
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||||
|
liberapay: # Replace with a single Liberapay username
|
||||||
|
issuehunt: # Replace with a single IssueHunt username
|
||||||
|
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
||||||
|
polar: # Replace with a single Polar username
|
||||||
|
buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
|
||||||
|
custom: ['https://afdian.com/a/lollipopkit'] # Replace with up to 4 custom sponsorship URLs
|
||||||
45
.github/workflows/release.yml
vendored
@@ -9,16 +9,17 @@ permissions:
|
|||||||
contents: write
|
contents: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
releaseAL:
|
releaseAndroid:
|
||||||
name: Release android and linux
|
name: Release android
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
|
||||||
fetch-depth: '0'
|
|
||||||
- name: Install Flutter
|
- name: Install Flutter
|
||||||
uses: subosito/flutter-action@v2
|
uses: subosito/flutter-action@v2
|
||||||
|
with:
|
||||||
|
channel: 'stable'
|
||||||
|
flutter-version: '3.22.3'
|
||||||
- uses: actions/setup-java@v4
|
- uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
distribution: 'zulu'
|
distribution: 'zulu'
|
||||||
@@ -28,14 +29,37 @@ jobs:
|
|||||||
curl -u ${{ secrets.BASIC_AUTH }} -o android/app/app.key ${{ secrets.URL_PREFIX }}app.key
|
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
|
curl -u ${{ secrets.BASIC_AUTH }} -o android/key.properties ${{ secrets.URL_PREFIX }}key.properties
|
||||||
- name: Build
|
- name: Build
|
||||||
run: dart run fl_build -p android,linux
|
run: dart run fl_build -p android
|
||||||
|
- name: Rename for fdroid
|
||||||
|
run: |
|
||||||
|
mv build/app/outputs/flutter-apk/${{ env.APP_NAME }}_${{ env.BUILD_NUMBER }}_arm64.apk build/app/outputs/flutter-apk/${{ env.APP_NAME }}_v1.0.${{ env.BUILD_NUMBER }}_arm64.apk
|
||||||
|
mv build/app/outputs/flutter-apk/${{ env.APP_NAME }}_${{ env.BUILD_NUMBER }}_arm.apk build/app/outputs/flutter-apk/${{ env.APP_NAME }}_v1.0.${{ env.BUILD_NUMBER }}_arm.apk
|
||||||
|
mv build/app/outputs/flutter-apk/${{ env.APP_NAME }}_${{ env.BUILD_NUMBER }}_amd64.apk build/app/outputs/flutter-apk/${{ env.APP_NAME }}_v1.0.${{ env.BUILD_NUMBER }}_amd64.apk
|
||||||
|
- name: Create Release
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
with:
|
||||||
|
files: |
|
||||||
|
build/app/outputs/flutter-apk/${{ env.APP_NAME }}_v1.0.${{ env.BUILD_NUMBER }}_arm64.apk
|
||||||
|
build/app/outputs/flutter-apk/${{ env.APP_NAME }}_v1.0.${{ env.BUILD_NUMBER }}_arm.apk
|
||||||
|
build/app/outputs/flutter-apk/${{ env.APP_NAME }}_v1.0.${{ env.BUILD_NUMBER }}_amd64.apk
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
releaseLinux:
|
||||||
|
name: Release linux
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Install Flutter
|
||||||
|
uses: subosito/flutter-action@v2
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
dart run fl_build -p linux
|
||||||
- name: Create Release
|
- name: Create Release
|
||||||
uses: softprops/action-gh-release@v2
|
uses: softprops/action-gh-release@v2
|
||||||
with:
|
with:
|
||||||
files: |
|
files: |
|
||||||
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.APP_NAME }}_${{ env.BUILD_NUMBER }}_amd64.AppImage
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -46,8 +70,6 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
|
||||||
fetch-depth: '0'
|
|
||||||
- name: Install Flutter
|
- name: Install Flutter
|
||||||
uses: subosito/flutter-action@v2
|
uses: subosito/flutter-action@v2
|
||||||
- name: Build
|
- name: Build
|
||||||
@@ -68,6 +90,9 @@ jobs:
|
|||||||
# uses: actions/checkout@v4
|
# uses: actions/checkout@v4
|
||||||
# - name: Install Flutter
|
# - name: Install Flutter
|
||||||
# uses: subosito/flutter-action@v2
|
# uses: subosito/flutter-action@v2
|
||||||
|
# with:
|
||||||
|
# channel: 'stable'
|
||||||
|
# flutter-version: '3.22.2'
|
||||||
# - name: Build
|
# - name: Build
|
||||||
# run: dart run fl_build -p ios,mac
|
# run: dart run fl_build -p ios,mac
|
||||||
# - name: Create Release
|
# - name: Create Release
|
||||||
|
|||||||
3
.gitignore
vendored
@@ -57,9 +57,10 @@ test.dart
|
|||||||
|
|
||||||
# Linux release
|
# Linux release
|
||||||
linux.AppDir
|
linux.AppDir
|
||||||
ServerBox-x86_64.AppImage
|
**/*.AppImage
|
||||||
|
|
||||||
untranlated.json
|
untranlated.json
|
||||||
|
|
||||||
.vscode/settings.json
|
.vscode/settings.json
|
||||||
more_build_data.json
|
more_build_data.json
|
||||||
|
trans.txt
|
||||||
|
|||||||
30
.metadata
@@ -4,7 +4,7 @@
|
|||||||
# This file should be version controlled and should not be manually edited.
|
# This file should be version controlled and should not be manually edited.
|
||||||
|
|
||||||
version:
|
version:
|
||||||
revision: "bae5e49bc2a867403c43b2aae2de8f8c33b037e4"
|
revision: "761747bfc538b5af34aa0d3fac380f1bc331ec49"
|
||||||
channel: "stable"
|
channel: "stable"
|
||||||
|
|
||||||
project_type: app
|
project_type: app
|
||||||
@@ -13,26 +13,26 @@ project_type: app
|
|||||||
migration:
|
migration:
|
||||||
platforms:
|
platforms:
|
||||||
- platform: root
|
- platform: root
|
||||||
create_revision: bae5e49bc2a867403c43b2aae2de8f8c33b037e4
|
create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
|
||||||
base_revision: bae5e49bc2a867403c43b2aae2de8f8c33b037e4
|
base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
|
||||||
- platform: android
|
- platform: android
|
||||||
create_revision: bae5e49bc2a867403c43b2aae2de8f8c33b037e4
|
create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
|
||||||
base_revision: bae5e49bc2a867403c43b2aae2de8f8c33b037e4
|
base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
|
||||||
- platform: ios
|
- platform: ios
|
||||||
create_revision: bae5e49bc2a867403c43b2aae2de8f8c33b037e4
|
create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
|
||||||
base_revision: bae5e49bc2a867403c43b2aae2de8f8c33b037e4
|
base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
|
||||||
- platform: linux
|
- platform: linux
|
||||||
create_revision: bae5e49bc2a867403c43b2aae2de8f8c33b037e4
|
create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
|
||||||
base_revision: bae5e49bc2a867403c43b2aae2de8f8c33b037e4
|
base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
|
||||||
- platform: macos
|
- platform: macos
|
||||||
create_revision: bae5e49bc2a867403c43b2aae2de8f8c33b037e4
|
create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
|
||||||
base_revision: bae5e49bc2a867403c43b2aae2de8f8c33b037e4
|
base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
|
||||||
- platform: web
|
- platform: web
|
||||||
create_revision: bae5e49bc2a867403c43b2aae2de8f8c33b037e4
|
create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
|
||||||
base_revision: bae5e49bc2a867403c43b2aae2de8f8c33b037e4
|
base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
|
||||||
- platform: windows
|
- platform: windows
|
||||||
create_revision: bae5e49bc2a867403c43b2aae2de8f8c33b037e4
|
create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
|
||||||
base_revision: bae5e49bc2a867403c43b2aae2de8f8c33b037e4
|
base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
|
||||||
|
|
||||||
# User provided section
|
# User provided section
|
||||||
|
|
||||||
|
|||||||
4
.vscode/launch.json
vendored
@@ -8,6 +8,10 @@
|
|||||||
"name": "debug",
|
"name": "debug",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"type": "dart",
|
"type": "dart",
|
||||||
|
"env": {
|
||||||
|
// Comment this line to use the default display
|
||||||
|
"DISPLAY": ":1"
|
||||||
|
}
|
||||||
// "args": [
|
// "args": [
|
||||||
// "-v"
|
// "-v"
|
||||||
// ]
|
// ]
|
||||||
|
|||||||
46
README.md
@@ -4,7 +4,6 @@ English | [简体中文](README_zh.md)
|
|||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img alt="lang" src="https://img.shields.io/badge/lang-dart-pink">
|
<img alt="lang" src="https://img.shields.io/badge/lang-dart-pink">
|
||||||
<img alt="countly" src="https://img.shields.io/badge/analysis-countly-pink">
|
|
||||||
<img alt="license" src="https://img.shields.io/badge/license-GPLv3-pink">
|
<img alt="license" src="https://img.shields.io/badge/license-GPLv3-pink">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@@ -14,31 +13,38 @@ A Flutter project which provide charts to display <a href="../../issues/43">Linu
|
|||||||
Especially thanks to <a href="https://github.com/TerminalStudio/dartssh2">dartssh2</a> & <a href="https://github.com/TerminalStudio/xterm.dart">xterm.dart</a>.
|
Especially thanks to <a href="https://github.com/TerminalStudio/dartssh2">dartssh2</a> & <a href="https://github.com/TerminalStudio/xterm.dart">xterm.dart</a>.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
## ⬇️ Download
|
|
||||||
🎉 **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)
|
## 📥 Install
|
||||||
|
|
||||||
|
Platform | From
|
||||||
|
--- | ---
|
||||||
|
iOS / macOS | [AppStore](https://apps.apple.com/app/id1586449703)
|
||||||
|
Android | [GitHub](https://github.com/lollipopkit/flutter_server_box/releases) / [CDN](https://cdn.lolli.tech/serverbox/?sort=time&order=desc&layout=grid) / [F-Droid](https://f-droid.org/packages/tech.lolli.toolbox) / [OpenAPK](https://www.openapk.net/serverbox/tech.lolli.toolbox/)
|
||||||
|
Linux / Windows | [GitHub](https://github.com/lollipopkit/flutter_server_box/releases) / [CDN](https://cdn.lolli.tech/serverbox/?sort=time&order=desc&layout=grid)
|
||||||
|
|
||||||
|
**Please only download pkgs from the source that you trust!**
|
||||||
|
- `AppStore` & `CDN` packages are built by myself
|
||||||
|
- Github releases are built by Github Actions
|
||||||
|
- Other sources are built by themselves
|
||||||
|
|
||||||
|
|
||||||
## 🔖 Feature
|
## 🔖 Feature
|
||||||
- `Status chart` (CPU, Sensors, GPU...), `SSH` Term, `SFTP`, `Docker & Pkg & Process`...
|
- `Status chart` (CPU, Sensors, GPU...), `SSH` Term, `SFTP`, `Docker & Process`...
|
||||||
- Platform specific: `Bio auth`、`Msg push`、`Home widget`、`watchOS App`...
|
- Platform specific: `Bio auth`、`Msg push`、`Home widget`、`watchOS App`...
|
||||||
- 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)
|
- 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), Türkçe [@mikropsoft](https://github.com/mikropsoft); Español, Русский язык, Português, 日本語 (Generated by GPT)
|
||||||
|
|
||||||
|
|
||||||
## 🏙️ ScreenShots
|
## 🏙️ ScreenShots
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td><img width="277px" src="imgs/server.png"></td>
|
<td><img width="277px" src="https://cdn.lolli.tech/serverbox/screenshot/1.png"></td>
|
||||||
<td><img width="277px" src="imgs/detail.png"></td>
|
<td><img width="277px" src="https://cdn.lolli.tech/serverbox/screenshot/2.png"></td>
|
||||||
<td><img width="277px" src="imgs/sftp.png"></td>
|
<td><img width="277px" src="https://cdn.lolli.tech/serverbox/screenshot/3.png"></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
|
||||||
<table>
|
|
||||||
<tr>
|
<tr>
|
||||||
<td><img width="277px" src="imgs/editor.png"> </td>
|
<td><img width="277px" src="https://cdn.lolli.tech/serverbox/screenshot/4.png"> </td>
|
||||||
<td><img width="277px" src="imgs/ssh.png"></td>
|
<td><img width="277px" src="https://cdn.lolli.tech/serverbox/screenshot/5.png"></td>
|
||||||
<td><img width="277px" src="imgs/docker.png"></td>
|
<td><img width="277px" src="https://cdn.lolli.tech/serverbox/screenshot/6.png"></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
@@ -56,8 +62,16 @@ After you read the above, you can open an [issue](https://github.com/lollipopkit
|
|||||||
|
|
||||||
|
|
||||||
## 🧱 Contribution
|
## 🧱 Contribution
|
||||||
- Any positive contribution is welcome.
|
Any positive contribution is welcome.
|
||||||
- [l10n guide](https://blog.lolli.tech/faq/) can be found in my blog.
|
|
||||||
|
### Development
|
||||||
|
1. Setup [Flutter](https://flutter.dev/docs/get-started/install) environment.
|
||||||
|
2. Clone this repo, run `flutter run` to start the app.
|
||||||
|
3. Run `dart run fl_build -p PLATFORM` to build the app.
|
||||||
|
|
||||||
|
### Translation
|
||||||
|
- [Guide](https://blog.lpkt.cn/posts/faq/) can be found in my blog.
|
||||||
|
- We need your help! Just feel free to open a PR.
|
||||||
|
|
||||||
|
|
||||||
## 💡 My other apps
|
## 💡 My other apps
|
||||||
|
|||||||
42
README_zh.md
@@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img alt="lang" src="https://img.shields.io/badge/lang-dart-pink">
|
<img alt="lang" src="https://img.shields.io/badge/lang-dart-pink">
|
||||||
<img alt="countly" src="https://img.shields.io/badge/analysis-countly-pink">
|
|
||||||
<img alt="license" src="https://img.shields.io/badge/license-GPLv3-pink">
|
<img alt="license" src="https://img.shields.io/badge/license-GPLv3-pink">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@@ -14,35 +13,40 @@
|
|||||||
特别感谢 <a href="https://github.com/TerminalStudio/dartssh2">dartssh2</a> & <a href="https://github.com/TerminalStudio/xterm.dart">xterm.dart</a>。
|
特别感谢 <a href="https://github.com/TerminalStudio/dartssh2">dartssh2</a> & <a href="https://github.com/TerminalStudio/xterm.dart">xterm.dart</a>。
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
## 📥 安装
|
||||||
|
|
||||||
## ⬇️ Download
|
平台 | 下载
|
||||||
🎉 **现在 `Android / Linux / Windows` 版本使用 GitHub Actions 构建**。
|
--- | ---
|
||||||
|
iOS / macOS | [AppStore](https://apps.apple.com/app/id1586449703)
|
||||||
|
Android | [GitHub](https://github.com/lollipopkit/flutter_server_box/releases) / [CDN](https://cdn.lolli.tech/serverbox/?sort=time&order=desc&layout=grid) / [F-Droid](https://f-droid.org/packages/tech.lolli.toolbox) / [OpenAPK](https://www.openapk.net/serverbox/tech.lolli.toolbox/)
|
||||||
|
Linux / Windows | [GitHub](https://github.com/lollipopkit/flutter_server_box/releases) / [CDN](https://cdn.lolli.tech/serverbox/?sort=time&order=desc&layout=grid)
|
||||||
|
|
||||||
[iOS & macOS](https://apps.apple.com/app/id1586449703) / [Android & Linux & Windows](https://github.com/lollipopkit/flutter_gpt_box/releases)
|
**请不要从不受信任的来源下载!**
|
||||||
|
- `AppStore` & `CDN` 的包由我构建
|
||||||
|
- Github 的包由 Github Actions 构建
|
||||||
|
- 其他来源由其所有者构建
|
||||||
|
|
||||||
|
|
||||||
## 🔖 特点
|
## 🔖 特点
|
||||||
- `状态图表`(CPU、传感器、GPU 等), `SSH` 终端, `SFTP`, `Docker & 包 & 进程` 管理器...
|
- `状态图表`(CPU、传感器、GPU 等), `SSH` 终端, `SFTP`, `Docker & 进程` 管理...
|
||||||
- 特殊支持:`生物认证`、`推送`、`桌面小部件`、`watchOS App`、`跟随系统颜色`...
|
- 特殊支持:`生物认证`、`推送`、`桌面小部件`、`watchOS App`、`跟随系统颜色`...
|
||||||
- 本地化
|
- 本地化
|
||||||
- English, 简体中文
|
- English, 简体中文
|
||||||
- Español, Русский язык, Português, 日本語 (Generated by GPT)
|
- Español, Русский язык, Português, 日本語 (Generated by GPT)
|
||||||
- Deutsch (@its-tom) / 繁體中文 (@kalashnikov) / Indonesian (@azkadev) / Français (@FrancXPT) / Dutch (@QazCetelic)
|
- 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), Türkçe [@mikropsoft](https://github.com/mikropsoft);
|
||||||
|
|
||||||
|
|
||||||
## 🏙️ 截屏
|
## 🏙️ 截屏
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td><img width="277px" src="imgs/server.png"></td>
|
<td><img width="277px" src="https://cdn.lolli.tech/serverbox/screenshot/1.png"></td>
|
||||||
<td><img width="277px" src="imgs/detail.png"></td>
|
<td><img width="277px" src="https://cdn.lolli.tech/serverbox/screenshot/2.png"></td>
|
||||||
<td><img width="277px" src="imgs/sftp.png"></td>
|
<td><img width="277px" src="https://cdn.lolli.tech/serverbox/screenshot/3.png"></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
|
||||||
<table>
|
|
||||||
<tr>
|
<tr>
|
||||||
<td><img width="277px" src="imgs/editor.png"> </td>
|
<td><img width="277px" src="https://cdn.lolli.tech/serverbox/screenshot/4.png"> </td>
|
||||||
<td><img width="277px" src="imgs/ssh.png"></td>
|
<td><img width="277px" src="https://cdn.lolli.tech/serverbox/screenshot/5.png"></td>
|
||||||
<td><img width="277px" src="imgs/docker.png"></td>
|
<td><img width="277px" src="https://cdn.lolli.tech/serverbox/screenshot/6.png"></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
@@ -62,9 +66,15 @@
|
|||||||
|
|
||||||
|
|
||||||
## 🧱 贡献
|
## 🧱 贡献
|
||||||
- 任何正面的贡献都欢迎。
|
任何正面的贡献都欢迎。
|
||||||
- [本地化翻译指南](https://blog.lolli.tech/faq/) 可在我的博客中找到。
|
|
||||||
|
|
||||||
|
### 开发
|
||||||
|
1. 安装 [Flutter](https://flutter.dev/docs/get-started/install)
|
||||||
|
2. 克隆这个仓库, 运行 `flutter run` 启动应用
|
||||||
|
3. 运行 `dart run fl_build -p PLATFORM` 构建应用
|
||||||
|
|
||||||
|
### 翻译
|
||||||
|
[指南](https://blog.lolli.tech/faq/) 可在我的博客中找到。
|
||||||
|
|
||||||
## 💡 我的其它 Apps
|
## 💡 我的其它 Apps
|
||||||
- [GPT Box](https://github.com/lollipopkit/flutter_gpt_box) - 支持 OpenAI API 的 第三方全平台客户端。
|
- [GPT Box](https://github.com/lollipopkit/flutter_gpt_box) - 支持 OpenAI API 的 第三方全平台客户端。
|
||||||
|
|||||||
@@ -100,3 +100,14 @@ flutter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {}
|
dependencies {}
|
||||||
|
|
||||||
|
ext.abiCodes = ["x86_64": 1, "armeabi-v7a": 2, "arm64-v8a": 3]
|
||||||
|
import com.android.build.OutputFile
|
||||||
|
android.applicationVariants.all { variant ->
|
||||||
|
variant.outputs.each { output ->
|
||||||
|
def abiVersionCode = project.ext.abiCodes.get(output.getFilter(OutputFile.ABI))
|
||||||
|
if (abiVersionCode != null) {
|
||||||
|
output.versionCodeOverride = variant.versionCode * 10 + abiVersionCode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
6
android/app/proguard-rules.pro
vendored
@@ -1,7 +1 @@
|
|||||||
-keep class io.flutter.app.** { *; }
|
|
||||||
-keep class io.flutter.plugin.** { *; }
|
|
||||||
-keep class io.flutter.util.** { *; }
|
|
||||||
-keep class io.flutter.view.** { *; }
|
|
||||||
-keep class io.flutter.** { *; }
|
|
||||||
-keep class io.flutter.plugins.** { *; }
|
|
||||||
-keep class com.jcraft.** { *; }
|
-keep class com.jcraft.** { *; }
|
||||||
|
|||||||
@@ -27,9 +27,12 @@ class HomeWidget : AppWidgetProvider() {
|
|||||||
private fun updateAppWidget(context: Context, appWidgetManager: AppWidgetManager, appWidgetId: Int) {
|
private fun updateAppWidget(context: Context, appWidgetManager: AppWidgetManager, appWidgetId: Int) {
|
||||||
val views = RemoteViews(context.packageName, R.layout.home_widget)
|
val views = RemoteViews(context.packageName, R.layout.home_widget)
|
||||||
val sp = context.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE)
|
val sp = context.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE)
|
||||||
var url = sp.getString("$appWidgetId", null)
|
var url = sp.getString("widget_$appWidgetId", null)
|
||||||
val gUrl = sp.getString("*", null)
|
|
||||||
if (url.isNullOrEmpty()) {
|
if (url.isNullOrEmpty()) {
|
||||||
|
url = sp.getString("$appWidgetId", null)
|
||||||
|
}
|
||||||
|
if (url.isNullOrEmpty()) {
|
||||||
|
val gUrl = sp.getString("widget_*", null)
|
||||||
url = gUrl
|
url = gUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,3 +3,4 @@ distributionPath=wrapper/dists
|
|||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip
|
||||||
|
distributionSha256Sum=6001aba9b2204d26fa25a5800bb9382cf3ee01ccb78fe77317b2872336eb2f80
|
||||||
|
|||||||
6
fastlane/metadata/android/en-US/full_description.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
A Flutter project which provide charts to display Linux server status and tools to manage server.
|
||||||
|
Especially thanks to dartssh2 & xterm.dart.
|
||||||
|
|
||||||
|
* Status chart (CPU, Sensors, GPU...), SSH Term, SFTP, Docker & Pkg & Process...
|
||||||
|
* Platform specific: Bio auth、Msg push、Home widget、watchOS App...
|
||||||
|
* English, 简体中文; Deutsch, 繁體中文, Indonesian, Français, Dutch; Español, Русский язык, Português, 日本語
|
||||||
BIN
fastlane/metadata/android/en-US/images/icon.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 135 KiB After Width: | Height: | Size: 135 KiB |
|
Before Width: | Height: | Size: 115 KiB After Width: | Height: | Size: 115 KiB |
|
Before Width: | Height: | Size: 173 KiB After Width: | Height: | Size: 173 KiB |
|
Before Width: | Height: | Size: 135 KiB After Width: | Height: | Size: 135 KiB |
|
Before Width: | Height: | Size: 135 KiB After Width: | Height: | Size: 135 KiB |
|
Before Width: | Height: | Size: 144 KiB After Width: | Height: | Size: 144 KiB |
1
fastlane/metadata/android/en-US/short_description.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
A server status & toolbox app using Flutter
|
||||||
1
fastlane/metadata/android/en-US/title.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
ServerBox
|
||||||
7
fastlane/metadata/android/zh-CN/full_description.txt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
使用 Flutter 开发的 Linux 服务器工具箱,提供服务器状态图表和管理工具。
|
||||||
|
特别感谢 dartssh2 & xterm.dart。
|
||||||
|
|
||||||
|
特点:
|
||||||
|
* 状态图表(CPU、传感器、GPU 等), SSH 终端, SFTP, Docker & 包 & 进程管理器...
|
||||||
|
* 特殊支持:生物认证、推送、桌面小部件、watchOS App、跟随系统颜色...
|
||||||
|
* 本地化 (English, 简体中文, Español, Русский язык, Português, 日本語, Deutsch, 繁體中文, Indonesian, Français, Dutch
|
||||||
BIN
fastlane/metadata/android/zh-CN/images/icon.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
fastlane/metadata/android/zh-CN/images/phoneScreenshots/1.png
Normal file
|
After Width: | Height: | Size: 135 KiB |
BIN
fastlane/metadata/android/zh-CN/images/phoneScreenshots/2.png
Normal file
|
After Width: | Height: | Size: 115 KiB |
BIN
fastlane/metadata/android/zh-CN/images/phoneScreenshots/3.png
Normal file
|
After Width: | Height: | Size: 173 KiB |
BIN
fastlane/metadata/android/zh-CN/images/phoneScreenshots/4.png
Normal file
|
After Width: | Height: | Size: 135 KiB |
BIN
fastlane/metadata/android/zh-CN/images/phoneScreenshots/5.png
Normal file
|
After Width: | Height: | Size: 135 KiB |
BIN
fastlane/metadata/android/zh-CN/images/phoneScreenshots/6.png
Normal file
|
After Width: | Height: | Size: 144 KiB |
1
fastlane/metadata/android/zh-CN/short_description.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
使用 Flutter 开发的服务器状态和工具箱应用
|
||||||
1
fastlane/metadata/android/zh-CN/title.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
ServerBox
|
||||||
|
Before Width: | Height: | Size: 112 KiB |
@@ -1,6 +1,4 @@
|
|||||||
PODS:
|
PODS:
|
||||||
- countly_flutter (24.4.1):
|
|
||||||
- Flutter
|
|
||||||
- device_info_plus (0.0.1):
|
- device_info_plus (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- file_picker (0.0.1):
|
- file_picker (0.0.1):
|
||||||
@@ -36,7 +34,6 @@ PODS:
|
|||||||
- Flutter
|
- Flutter
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
- countly_flutter (from `.symlinks/plugins/countly_flutter/ios`)
|
|
||||||
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
|
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
|
||||||
- file_picker (from `.symlinks/plugins/file_picker/ios`)
|
- file_picker (from `.symlinks/plugins/file_picker/ios`)
|
||||||
- Flutter (from `Flutter`)
|
- Flutter (from `Flutter`)
|
||||||
@@ -55,8 +52,6 @@ DEPENDENCIES:
|
|||||||
- watch_connectivity (from `.symlinks/plugins/watch_connectivity/ios`)
|
- watch_connectivity (from `.symlinks/plugins/watch_connectivity/ios`)
|
||||||
|
|
||||||
EXTERNAL SOURCES:
|
EXTERNAL SOURCES:
|
||||||
countly_flutter:
|
|
||||||
:path: ".symlinks/plugins/countly_flutter/ios"
|
|
||||||
device_info_plus:
|
device_info_plus:
|
||||||
:path: ".symlinks/plugins/device_info_plus/ios"
|
:path: ".symlinks/plugins/device_info_plus/ios"
|
||||||
file_picker:
|
file_picker:
|
||||||
@@ -91,7 +86,6 @@ EXTERNAL SOURCES:
|
|||||||
:path: ".symlinks/plugins/watch_connectivity/ios"
|
:path: ".symlinks/plugins/watch_connectivity/ios"
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
countly_flutter: 56233d921c6b4e0a720774a39b8ee8110d6f8d91
|
|
||||||
device_info_plus: 97af1d7e84681a90d0693e63169a5d50e0839a0d
|
device_info_plus: 97af1d7e84681a90d0693e63169a5d50e0839a0d
|
||||||
file_picker: c79185e70b9b45728cde2a8d8da454e0cb43f287
|
file_picker: c79185e70b9b45728cde2a8d8da454e0cb43f287
|
||||||
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
||||||
|
|||||||
@@ -690,7 +690,7 @@
|
|||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||||
CURRENT_PROJECT_VERSION = 923;
|
CURRENT_PROJECT_VERSION = 1051;
|
||||||
DEVELOPMENT_TEAM = BA88US33G6;
|
DEVELOPMENT_TEAM = BA88US33G6;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
|
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
|
||||||
@@ -700,7 +700,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.923;
|
MARKETING_VERSION = 1.0.1051;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
@@ -826,7 +826,7 @@
|
|||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||||
CURRENT_PROJECT_VERSION = 923;
|
CURRENT_PROJECT_VERSION = 1051;
|
||||||
DEVELOPMENT_TEAM = BA88US33G6;
|
DEVELOPMENT_TEAM = BA88US33G6;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
|
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
|
||||||
@@ -836,7 +836,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.923;
|
MARKETING_VERSION = 1.0.1051;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
@@ -854,7 +854,7 @@
|
|||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||||
CURRENT_PROJECT_VERSION = 923;
|
CURRENT_PROJECT_VERSION = 1051;
|
||||||
DEVELOPMENT_TEAM = BA88US33G6;
|
DEVELOPMENT_TEAM = BA88US33G6;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
|
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
|
||||||
@@ -864,7 +864,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.923;
|
MARKETING_VERSION = 1.0.1051;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
@@ -885,7 +885,7 @@
|
|||||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 923;
|
CURRENT_PROJECT_VERSION = 1051;
|
||||||
DEVELOPMENT_TEAM = BA88US33G6;
|
DEVELOPMENT_TEAM = BA88US33G6;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
@@ -898,7 +898,7 @@
|
|||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.923;
|
MARKETING_VERSION = 1.0.1051;
|
||||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||||
MTL_FAST_MATH = YES;
|
MTL_FAST_MATH = YES;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
|
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
|
||||||
@@ -924,7 +924,7 @@
|
|||||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 923;
|
CURRENT_PROJECT_VERSION = 1051;
|
||||||
DEVELOPMENT_TEAM = BA88US33G6;
|
DEVELOPMENT_TEAM = BA88US33G6;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
@@ -937,7 +937,7 @@
|
|||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.923;
|
MARKETING_VERSION = 1.0.1051;
|
||||||
MTL_FAST_MATH = YES;
|
MTL_FAST_MATH = YES;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
|
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
@@ -960,7 +960,7 @@
|
|||||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 923;
|
CURRENT_PROJECT_VERSION = 1051;
|
||||||
DEVELOPMENT_TEAM = BA88US33G6;
|
DEVELOPMENT_TEAM = BA88US33G6;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
@@ -973,7 +973,7 @@
|
|||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.923;
|
MARKETING_VERSION = 1.0.1051;
|
||||||
MTL_FAST_MATH = YES;
|
MTL_FAST_MATH = YES;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
|
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
@@ -996,7 +996,7 @@
|
|||||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 923;
|
CURRENT_PROJECT_VERSION = 1051;
|
||||||
DEVELOPMENT_ASSET_PATHS = "";
|
DEVELOPMENT_ASSET_PATHS = "";
|
||||||
DEVELOPMENT_TEAM = BA88US33G6;
|
DEVELOPMENT_TEAM = BA88US33G6;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
@@ -1008,7 +1008,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.923;
|
MARKETING_VERSION = 1.0.1051;
|
||||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||||
MTL_FAST_MATH = YES;
|
MTL_FAST_MATH = YES;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
|
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
|
||||||
@@ -1037,7 +1037,7 @@
|
|||||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 923;
|
CURRENT_PROJECT_VERSION = 1051;
|
||||||
DEVELOPMENT_ASSET_PATHS = "";
|
DEVELOPMENT_ASSET_PATHS = "";
|
||||||
DEVELOPMENT_TEAM = BA88US33G6;
|
DEVELOPMENT_TEAM = BA88US33G6;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
@@ -1049,7 +1049,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.923;
|
MARKETING_VERSION = 1.0.1051;
|
||||||
MTL_FAST_MATH = YES;
|
MTL_FAST_MATH = YES;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
|
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
|
||||||
PRODUCT_NAME = ServerBox;
|
PRODUCT_NAME = ServerBox;
|
||||||
@@ -1075,7 +1075,7 @@
|
|||||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 923;
|
CURRENT_PROJECT_VERSION = 1051;
|
||||||
DEVELOPMENT_ASSET_PATHS = "";
|
DEVELOPMENT_ASSET_PATHS = "";
|
||||||
DEVELOPMENT_TEAM = BA88US33G6;
|
DEVELOPMENT_TEAM = BA88US33G6;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
@@ -1087,7 +1087,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.923;
|
MARKETING_VERSION = 1.0.1051;
|
||||||
MTL_FAST_MATH = YES;
|
MTL_FAST_MATH = YES;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
|
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
|
||||||
PRODUCT_NAME = ServerBox;
|
PRODUCT_NAME = ServerBox;
|
||||||
|
|||||||
77
lib/app.dart
@@ -3,10 +3,14 @@ import 'package:fl_lib/fl_lib.dart';
|
|||||||
import 'package:fl_lib/l10n/gen_l10n/lib_l10n.dart';
|
import 'package:fl_lib/l10n/gen_l10n/lib_l10n.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||||
import 'package:toolbox/data/res/build_data.dart';
|
import 'package:server_box/core/extension/context/locale.dart';
|
||||||
import 'package:toolbox/data/res/rebuild.dart';
|
import 'package:server_box/data/res/build_data.dart';
|
||||||
import 'package:toolbox/data/res/store.dart';
|
import 'package:server_box/data/res/rebuild.dart';
|
||||||
import 'package:toolbox/view/page/home/home.dart';
|
import 'package:server_box/data/res/store.dart';
|
||||||
|
import 'package:server_box/view/page/home/home.dart';
|
||||||
|
import 'package:icons_plus/icons_plus.dart';
|
||||||
|
|
||||||
|
part 'intro.dart';
|
||||||
|
|
||||||
class MyApp extends StatelessWidget {
|
class MyApp extends StatelessWidget {
|
||||||
const MyApp({super.key});
|
const MyApp({super.key});
|
||||||
@@ -15,11 +19,25 @@ class MyApp extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
_setup(context);
|
_setup(context);
|
||||||
return ListenableBuilder(
|
return ListenableBuilder(
|
||||||
listenable: RebuildNodes.app,
|
listenable: RNodes.app,
|
||||||
builder: (context, _) {
|
builder: (context, _) {
|
||||||
if (!Stores.setting.useSystemPrimaryColor.fetch()) {
|
if (!Stores.setting.useSystemPrimaryColor.fetch()) {
|
||||||
UIs.colorSeed = Color(Stores.setting.primaryColor.fetch());
|
final colorSeed = Color(Stores.setting.colorSeed.fetch());
|
||||||
return _buildApp(context);
|
UIs.colorSeed = colorSeed;
|
||||||
|
// Past code uses [UIs.primaryColor] as the primary color
|
||||||
|
UIs.primaryColor = colorSeed;
|
||||||
|
return _buildApp(
|
||||||
|
context,
|
||||||
|
light: ThemeData(
|
||||||
|
useMaterial3: true,
|
||||||
|
colorSchemeSeed: UIs.colorSeed,
|
||||||
|
),
|
||||||
|
dark: ThemeData(
|
||||||
|
useMaterial3: true,
|
||||||
|
brightness: Brightness.dark,
|
||||||
|
colorSchemeSeed: UIs.colorSeed,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return DynamicColorBuilder(
|
return DynamicColorBuilder(
|
||||||
builder: (light, dark) {
|
builder: (light, dark) {
|
||||||
@@ -32,10 +50,10 @@ class MyApp extends StatelessWidget {
|
|||||||
brightness: Brightness.dark,
|
brightness: Brightness.dark,
|
||||||
colorScheme: dark,
|
colorScheme: dark,
|
||||||
);
|
);
|
||||||
if (context.isDark && light != null) {
|
if (context.isDark && dark != null) {
|
||||||
UIs.primaryColor = light.primary;
|
|
||||||
} else if (!context.isDark && dark != null) {
|
|
||||||
UIs.primaryColor = dark.primary;
|
UIs.primaryColor = dark.primary;
|
||||||
|
} else if (!context.isDark && light != null) {
|
||||||
|
UIs.primaryColor = light.primary;
|
||||||
}
|
}
|
||||||
return _buildApp(context, light: lightTheme, dark: darkTheme);
|
return _buildApp(context, light: lightTheme, dark: darkTheme);
|
||||||
},
|
},
|
||||||
@@ -44,7 +62,8 @@ class MyApp extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildApp(BuildContext ctx, {ThemeData? light, ThemeData? dark}) {
|
Widget _buildApp(BuildContext ctx,
|
||||||
|
{required ThemeData light, required ThemeData dark}) {
|
||||||
final tMode = Stores.setting.themeMode.fetch();
|
final tMode = Stores.setting.themeMode.fetch();
|
||||||
// Issue #57
|
// Issue #57
|
||||||
final themeMode = switch (tMode) {
|
final themeMode = switch (tMode) {
|
||||||
@@ -54,16 +73,6 @@ class MyApp extends StatelessWidget {
|
|||||||
};
|
};
|
||||||
final locale = Stores.setting.locale.fetch().toLocale;
|
final locale = Stores.setting.locale.fetch().toLocale;
|
||||||
|
|
||||||
light ??= ThemeData(
|
|
||||||
useMaterial3: true,
|
|
||||||
colorSchemeSeed: UIs.colorSeed,
|
|
||||||
);
|
|
||||||
dark ??= ThemeData(
|
|
||||||
useMaterial3: true,
|
|
||||||
brightness: Brightness.dark,
|
|
||||||
colorSchemeSeed: UIs.colorSeed,
|
|
||||||
);
|
|
||||||
|
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
locale: locale,
|
locale: locale,
|
||||||
localizationsDelegates: const [
|
localizationsDelegates: const [
|
||||||
@@ -71,17 +80,27 @@ class MyApp extends StatelessWidget {
|
|||||||
...AppLocalizations.localizationsDelegates,
|
...AppLocalizations.localizationsDelegates,
|
||||||
],
|
],
|
||||||
supportedLocales: AppLocalizations.supportedLocales,
|
supportedLocales: AppLocalizations.supportedLocales,
|
||||||
|
localeListResolutionCallback: LocaleUtil.resolve,
|
||||||
|
navigatorObservers: [AppRouteObserver.instance],
|
||||||
title: BuildData.name,
|
title: BuildData.name,
|
||||||
themeMode: themeMode,
|
themeMode: themeMode,
|
||||||
theme: light,
|
theme: light.fixWindowsFont,
|
||||||
darkTheme: tMode < 3 ? dark : dark.toAmoled,
|
darkTheme: (tMode < 3 ? dark : dark.toAmoled).fixWindowsFont,
|
||||||
home: _buildAppContent(ctx),
|
home: Builder(
|
||||||
);
|
builder: (context) {
|
||||||
}
|
context.setLibL10n();
|
||||||
|
final appL10n = AppLocalizations.of(context);
|
||||||
|
if (appL10n != null) l10n = appL10n;
|
||||||
|
|
||||||
Widget _buildAppContent(BuildContext ctx) {
|
final intros = _IntroPage.builders;
|
||||||
//if (Pros.app.isWearOS) return const WearHome();
|
if (intros.isNotEmpty) {
|
||||||
return const HomePage();
|
return _IntroPage(intros);
|
||||||
|
}
|
||||||
|
|
||||||
|
return const HomePage();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:toolbox/data/res/misc.dart';
|
import 'package:server_box/data/res/misc.dart';
|
||||||
|
|
||||||
abstract final class BgRunMC {
|
abstract final class BgRunMC {
|
||||||
static const _channel = MethodChannel('${Miscs.pkgName}/app_retain');
|
static const _channel = MethodChannel('${Miscs.pkgName}/app_retain');
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:toolbox/data/res/misc.dart';
|
import 'package:server_box/data/res/misc.dart';
|
||||||
import 'package:toolbox/data/res/store.dart';
|
import 'package:server_box/data/res/store.dart';
|
||||||
|
|
||||||
abstract final class HomeWidgetMC {
|
abstract final class HomeWidgetMC {
|
||||||
static const _channel = MethodChannel('${Miscs.pkgName}/home_widget');
|
static const _channel = MethodChannel('${Miscs.pkgName}/home_widget');
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import 'package:toolbox/data/res/build_data.dart';
|
import 'package:server_box/data/res/build_data.dart';
|
||||||
|
|
||||||
extension BuildDataX on BuildData {
|
extension BuildDataX on BuildData {
|
||||||
static const versionStr = 'v1.0.${BuildData.build}';
|
static const versionStr = 'v1.0.${BuildData.build}';
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:dartssh2/dartssh2.dart';
|
import 'package:dartssh2/dartssh2.dart';
|
||||||
|
import 'package:server_box/view/widget/unix_perm.dart';
|
||||||
|
|
||||||
extension SftpFileX on SftpFileMode {
|
extension SftpFileX on SftpFileMode {
|
||||||
String get str {
|
String get str {
|
||||||
@@ -8,6 +9,26 @@ extension SftpFileX on SftpFileMode {
|
|||||||
|
|
||||||
return '$user$group$other';
|
return '$user$group$other';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UnixPerm toUnixPerm() {
|
||||||
|
return UnixPerm(
|
||||||
|
user: RWX(
|
||||||
|
r: userRead,
|
||||||
|
w: userWrite,
|
||||||
|
x: userExecute,
|
||||||
|
),
|
||||||
|
group: RWX(
|
||||||
|
r: groupRead,
|
||||||
|
w: groupWrite,
|
||||||
|
x: groupExecute,
|
||||||
|
),
|
||||||
|
other: RWX(
|
||||||
|
r: otherRead,
|
||||||
|
w: otherWrite,
|
||||||
|
x: otherExecute,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String _getRoleMode(bool r, bool w, bool x) {
|
String _getRoleMode(bool r, bool w, bool x) {
|
||||||
|
|||||||
@@ -1,27 +1,26 @@
|
|||||||
import 'package:fl_lib/fl_lib.dart';
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:toolbox/data/model/server/private_key_info.dart';
|
import 'package:server_box/data/model/server/private_key_info.dart';
|
||||||
import 'package:toolbox/data/model/server/server_private_info.dart';
|
import 'package:server_box/data/model/server/server_private_info.dart';
|
||||||
import 'package:toolbox/data/res/build_data.dart';
|
import 'package:server_box/data/res/build_data.dart';
|
||||||
import 'package:toolbox/data/res/provider.dart';
|
import 'package:server_box/data/res/store.dart';
|
||||||
import 'package:toolbox/data/res/store.dart';
|
import 'package:server_box/view/page/backup.dart';
|
||||||
import 'package:toolbox/view/page/backup.dart';
|
import 'package:server_box/view/page/container.dart';
|
||||||
import 'package:toolbox/view/page/container.dart';
|
import 'package:server_box/view/page/home/home.dart';
|
||||||
import 'package:toolbox/view/page/home/home.dart';
|
import 'package:server_box/view/page/iperf.dart';
|
||||||
import 'package:toolbox/view/page/iperf.dart';
|
import 'package:server_box/view/page/ping.dart';
|
||||||
import 'package:toolbox/view/page/ping.dart';
|
import 'package:server_box/view/page/private_key/edit.dart';
|
||||||
import 'package:toolbox/view/page/private_key/edit.dart';
|
import 'package:server_box/view/page/private_key/list.dart';
|
||||||
import 'package:toolbox/view/page/private_key/list.dart';
|
import 'package:server_box/view/page/pve.dart';
|
||||||
import 'package:toolbox/view/page/pve.dart';
|
import 'package:server_box/view/page/server/detail/view.dart';
|
||||||
import 'package:toolbox/view/page/server/detail/view.dart';
|
import 'package:server_box/view/page/setting/platform/android.dart';
|
||||||
import 'package:toolbox/view/page/setting/platform/android.dart';
|
import 'package:server_box/view/page/setting/platform/ios.dart';
|
||||||
import 'package:toolbox/view/page/setting/platform/ios.dart';
|
import 'package:server_box/view/page/setting/seq/srv_func_seq.dart';
|
||||||
import 'package:toolbox/view/page/setting/seq/srv_func_seq.dart';
|
import 'package:server_box/view/page/snippet/result.dart';
|
||||||
import 'package:toolbox/view/page/snippet/result.dart';
|
import 'package:server_box/view/page/ssh/page.dart';
|
||||||
import 'package:toolbox/view/page/ssh/page.dart';
|
import 'package:server_box/view/page/setting/seq/virt_key.dart';
|
||||||
import 'package:toolbox/view/page/setting/seq/virt_key.dart';
|
import 'package:server_box/view/page/storage/local.dart';
|
||||||
import 'package:toolbox/view/page/storage/local.dart';
|
|
||||||
|
|
||||||
import '../data/model/server/snippet.dart';
|
import '../data/model/server/snippet.dart';
|
||||||
import '../view/page/editor.dart';
|
import '../view/page/editor.dart';
|
||||||
@@ -102,12 +101,14 @@ class AppRoutes {
|
|||||||
Key? key,
|
Key? key,
|
||||||
required ServerPrivateInfo spi,
|
required ServerPrivateInfo spi,
|
||||||
String? initCmd,
|
String? initCmd,
|
||||||
|
Snippet? initSnippet,
|
||||||
}) {
|
}) {
|
||||||
return AppRoutes(
|
return AppRoutes(
|
||||||
SSHPage(
|
SSHPage(
|
||||||
key: key,
|
key: key,
|
||||||
spi: spi,
|
spi: spi,
|
||||||
initCmd: initCmd,
|
initCmd: initCmd,
|
||||||
|
initSnippet: initSnippet,
|
||||||
),
|
),
|
||||||
'ssh_term',
|
'ssh_term',
|
||||||
);
|
);
|
||||||
@@ -155,11 +156,7 @@ class AppRoutes {
|
|||||||
return AppRoutes(
|
return AppRoutes(
|
||||||
DebugPage(
|
DebugPage(
|
||||||
key: key,
|
key: key,
|
||||||
args: DebugPageArgs(
|
args: const DebugPageArgs(title: 'Logs(${BuildData.build})'),
|
||||||
notifier: Pros.debug.widgets,
|
|
||||||
onClear: Pros.debug.clear,
|
|
||||||
title: 'Logs(${BuildData.build})',
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
'debug',
|
'debug',
|
||||||
);
|
);
|
||||||
@@ -246,8 +243,4 @@ class AppRoutes {
|
|||||||
static AppRoutes pve({Key? key, required ServerPrivateInfo spi}) {
|
static AppRoutes pve({Key? key, required ServerPrivateInfo spi}) {
|
||||||
return AppRoutes(PvePage(key: key, spi: spi), 'pve');
|
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');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,14 +2,9 @@ import 'package:fl_lib/fl_lib.dart';
|
|||||||
import 'package:plain_notification_token/plain_notification_token.dart';
|
import 'package:plain_notification_token/plain_notification_token.dart';
|
||||||
|
|
||||||
Future<String?> getToken() async {
|
Future<String?> getToken() async {
|
||||||
if (isIOS) {
|
if (!isIOS) return null;
|
||||||
final plainNotificationToken = PlainNotificationToken();
|
final instance = ApnsToken()..requestPermission();
|
||||||
plainNotificationToken.requestPermission();
|
// Wait until Permission dialog closed
|
||||||
|
await instance.onIosSettingsRegistered.first;
|
||||||
// If you want to wait until Permission dialog close,
|
return await instance.getToken();
|
||||||
// you need wait changing setting registered.
|
|
||||||
await plainNotificationToken.onIosSettingsRegistered.first;
|
|
||||||
return await plainNotificationToken.getToken();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import 'dart:async';
|
|||||||
|
|
||||||
import 'package:dartssh2/dartssh2.dart';
|
import 'package:dartssh2/dartssh2.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:toolbox/data/model/app/error.dart';
|
import 'package:server_box/data/model/app/error.dart';
|
||||||
import 'package:toolbox/data/res/store.dart';
|
import 'package:server_box/data/res/store.dart';
|
||||||
|
|
||||||
import '../../data/model/server/server_private_info.dart';
|
import '../../data/model/server/server_private_info.dart';
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ import 'dart:async';
|
|||||||
|
|
||||||
import 'package:fl_lib/fl_lib.dart';
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:toolbox/core/extension/context/locale.dart';
|
import 'package:server_box/core/extension/context/locale.dart';
|
||||||
import 'package:toolbox/data/model/server/server_private_info.dart';
|
import 'package:server_box/data/model/server/server_private_info.dart';
|
||||||
import 'package:toolbox/data/res/provider.dart';
|
import 'package:server_box/data/res/provider.dart';
|
||||||
|
|
||||||
abstract final class KeybordInteractive {
|
abstract final class KeybordInteractive {
|
||||||
static FutureOr<List<String>?> defaultHandle(
|
static FutureOr<List<String>?> defaultHandle(
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ import 'package:computer/computer.dart';
|
|||||||
import 'package:fl_lib/fl_lib.dart';
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
import 'package:icloud_storage/icloud_storage.dart';
|
import 'package:icloud_storage/icloud_storage.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:toolbox/data/model/app/backup.dart';
|
import 'package:server_box/data/model/app/backup.dart';
|
||||||
import 'package:toolbox/data/model/app/sync.dart';
|
import 'package:server_box/data/model/app/sync.dart';
|
||||||
import 'package:toolbox/data/res/misc.dart';
|
import 'package:server_box/data/res/misc.dart';
|
||||||
|
|
||||||
import '../../../data/model/app/error.dart';
|
import '../../../data/model/app/error.dart';
|
||||||
|
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ import 'dart:io';
|
|||||||
import 'package:computer/computer.dart';
|
import 'package:computer/computer.dart';
|
||||||
import 'package:fl_lib/fl_lib.dart';
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:toolbox/data/model/app/backup.dart';
|
import 'package:server_box/data/model/app/backup.dart';
|
||||||
import 'package:toolbox/data/model/app/error.dart';
|
import 'package:server_box/data/model/app/error.dart';
|
||||||
import 'package:toolbox/data/res/misc.dart';
|
import 'package:server_box/data/res/misc.dart';
|
||||||
import 'package:toolbox/data/res/store.dart';
|
import 'package:server_box/data/res/store.dart';
|
||||||
import 'package:webdav_client/webdav_client.dart';
|
import 'package:webdav_client/webdav_client.dart';
|
||||||
|
|
||||||
abstract final class Webdav {
|
abstract final class Webdav {
|
||||||
|
|||||||
@@ -3,13 +3,13 @@ import 'dart:io';
|
|||||||
|
|
||||||
import 'package:fl_lib/fl_lib.dart';
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:toolbox/data/model/server/private_key_info.dart';
|
import 'package:server_box/data/model/server/private_key_info.dart';
|
||||||
import 'package:toolbox/data/model/server/server_private_info.dart';
|
import 'package:server_box/data/model/server/server_private_info.dart';
|
||||||
import 'package:toolbox/data/model/server/snippet.dart';
|
import 'package:server_box/data/model/server/snippet.dart';
|
||||||
import 'package:toolbox/data/res/misc.dart';
|
import 'package:server_box/data/res/misc.dart';
|
||||||
import 'package:toolbox/data/res/provider.dart';
|
import 'package:server_box/data/res/provider.dart';
|
||||||
import 'package:toolbox/data/res/rebuild.dart';
|
import 'package:server_box/data/res/rebuild.dart';
|
||||||
import 'package:toolbox/data/res/store.dart';
|
import 'package:server_box/data/res/store.dart';
|
||||||
|
|
||||||
const backupFormatVersion = 1;
|
const backupFormatVersion = 1;
|
||||||
|
|
||||||
@@ -90,87 +90,113 @@ class Backup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Snippets
|
// Snippets
|
||||||
final nowSnippets = Stores.snippet.box.keys.toSet();
|
if (force) {
|
||||||
final bakSnippets = snippets.map((e) => e.name).toSet();
|
for (final s in snippets) {
|
||||||
final newSnippets = bakSnippets.difference(nowSnippets);
|
Stores.snippet.box.put(s.name, s);
|
||||||
final delSnippets = nowSnippets.difference(bakSnippets);
|
}
|
||||||
final updateSnippets = nowSnippets.intersection(bakSnippets);
|
} else {
|
||||||
for (final s in newSnippets) {
|
final nowSnippets = Stores.snippet.box.keys.toSet();
|
||||||
Stores.snippet.box.put(s, snippets.firstWhere((e) => e.name == s));
|
final bakSnippets = snippets.map((e) => e.name).toSet();
|
||||||
}
|
final newSnippets = bakSnippets.difference(nowSnippets);
|
||||||
for (final s in delSnippets) {
|
final delSnippets = nowSnippets.difference(bakSnippets);
|
||||||
Stores.snippet.box.delete(s);
|
final updateSnippets = nowSnippets.intersection(bakSnippets);
|
||||||
}
|
for (final s in newSnippets) {
|
||||||
for (final s in updateSnippets) {
|
Stores.snippet.box.put(s, snippets.firstWhere((e) => e.name == s));
|
||||||
Stores.snippet.box.put(s, snippets.firstWhere((e) => e.name == s));
|
}
|
||||||
|
for (final s in delSnippets) {
|
||||||
|
Stores.snippet.box.delete(s);
|
||||||
|
}
|
||||||
|
for (final s in updateSnippets) {
|
||||||
|
Stores.snippet.box.put(s, snippets.firstWhere((e) => e.name == s));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServerPrivateInfo
|
// ServerPrivateInfo
|
||||||
final nowSpis = Stores.server.box.keys.toSet();
|
if (force) {
|
||||||
final bakSpis = spis.map((e) => e.id).toSet();
|
for (final s in spis) {
|
||||||
final newSpis = bakSpis.difference(nowSpis);
|
Stores.server.box.put(s.id, s);
|
||||||
final delSpis = nowSpis.difference(bakSpis);
|
}
|
||||||
final updateSpis = nowSpis.intersection(bakSpis);
|
} else {
|
||||||
for (final s in newSpis) {
|
final nowSpis = Stores.server.box.keys.toSet();
|
||||||
Stores.server.box.put(s, spis.firstWhere((e) => e.id == s));
|
final bakSpis = spis.map((e) => e.id).toSet();
|
||||||
}
|
final newSpis = bakSpis.difference(nowSpis);
|
||||||
for (final s in delSpis) {
|
final delSpis = nowSpis.difference(bakSpis);
|
||||||
Stores.server.box.delete(s);
|
final updateSpis = nowSpis.intersection(bakSpis);
|
||||||
}
|
for (final s in newSpis) {
|
||||||
for (final s in updateSpis) {
|
Stores.server.box.put(s, spis.firstWhere((e) => e.id == s));
|
||||||
Stores.server.box.put(s, spis.firstWhere((e) => e.id == s));
|
}
|
||||||
|
for (final s in delSpis) {
|
||||||
|
Stores.server.box.delete(s);
|
||||||
|
}
|
||||||
|
for (final s in updateSpis) {
|
||||||
|
Stores.server.box.put(s, spis.firstWhere((e) => e.id == s));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrivateKeyInfo
|
// PrivateKeyInfo
|
||||||
final nowKeys = Stores.key.box.keys.toSet();
|
if (force) {
|
||||||
final bakKeys = keys.map((e) => e.id).toSet();
|
for (final s in keys) {
|
||||||
final newKeys = bakKeys.difference(nowKeys);
|
Stores.key.box.put(s.id, s);
|
||||||
final delKeys = nowKeys.difference(bakKeys);
|
}
|
||||||
final updateKeys = nowKeys.intersection(bakKeys);
|
} else {
|
||||||
for (final s in newKeys) {
|
final nowKeys = Stores.key.box.keys.toSet();
|
||||||
Stores.key.box.put(s, keys.firstWhere((e) => e.id == s));
|
final bakKeys = keys.map((e) => e.id).toSet();
|
||||||
}
|
final newKeys = bakKeys.difference(nowKeys);
|
||||||
for (final s in delKeys) {
|
final delKeys = nowKeys.difference(bakKeys);
|
||||||
Stores.key.box.delete(s);
|
final updateKeys = nowKeys.intersection(bakKeys);
|
||||||
}
|
for (final s in newKeys) {
|
||||||
for (final s in updateKeys) {
|
Stores.key.box.put(s, keys.firstWhere((e) => e.id == s));
|
||||||
Stores.key.box.put(s, keys.firstWhere((e) => e.id == s));
|
}
|
||||||
|
for (final s in delKeys) {
|
||||||
|
Stores.key.box.delete(s);
|
||||||
|
}
|
||||||
|
for (final s in updateKeys) {
|
||||||
|
Stores.key.box.put(s, keys.firstWhere((e) => e.id == s));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// History
|
// History
|
||||||
final nowHistory = Stores.history.box.keys.toSet();
|
if (force) {
|
||||||
final bakHistory = history.keys.toSet();
|
Stores.history.box.putAll(history);
|
||||||
final newHistory = bakHistory.difference(nowHistory);
|
} else {
|
||||||
final delHistory = nowHistory.difference(bakHistory);
|
final nowHistory = Stores.history.box.keys.toSet();
|
||||||
final updateHistory = nowHistory.intersection(bakHistory);
|
final bakHistory = history.keys.toSet();
|
||||||
for (final s in newHistory) {
|
final newHistory = bakHistory.difference(nowHistory);
|
||||||
Stores.history.box.put(s, history[s]);
|
final delHistory = nowHistory.difference(bakHistory);
|
||||||
}
|
final updateHistory = nowHistory.intersection(bakHistory);
|
||||||
for (final s in delHistory) {
|
for (final s in newHistory) {
|
||||||
Stores.history.box.delete(s);
|
Stores.history.box.put(s, history[s]);
|
||||||
}
|
}
|
||||||
for (final s in updateHistory) {
|
for (final s in delHistory) {
|
||||||
Stores.history.box.put(s, history[s]);
|
Stores.history.box.delete(s);
|
||||||
|
}
|
||||||
|
for (final s in updateHistory) {
|
||||||
|
Stores.history.box.put(s, history[s]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Container
|
// Container
|
||||||
final nowContainer = Stores.container.box.keys.toSet();
|
if (force) {
|
||||||
final bakContainer = container.keys.toSet();
|
Stores.container.box.putAll(container);
|
||||||
final newContainer = bakContainer.difference(nowContainer);
|
} else {
|
||||||
final delContainer = nowContainer.difference(bakContainer);
|
final nowContainer = Stores.container.box.keys.toSet();
|
||||||
final updateContainer = nowContainer.intersection(bakContainer);
|
final bakContainer = container.keys.toSet();
|
||||||
for (final s in newContainer) {
|
final newContainer = bakContainer.difference(nowContainer);
|
||||||
Stores.container.box.put(s, container[s]);
|
final delContainer = nowContainer.difference(bakContainer);
|
||||||
}
|
final updateContainer = nowContainer.intersection(bakContainer);
|
||||||
for (final s in delContainer) {
|
for (final s in newContainer) {
|
||||||
Stores.container.box.delete(s);
|
Stores.container.box.put(s, container[s]);
|
||||||
}
|
}
|
||||||
for (final s in updateContainer) {
|
for (final s in delContainer) {
|
||||||
Stores.container.box.put(s, container[s]);
|
Stores.container.box.delete(s);
|
||||||
|
}
|
||||||
|
for (final s in updateContainer) {
|
||||||
|
Stores.container.box.put(s, container[s]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Pros.reload();
|
Pros.reload();
|
||||||
RebuildNodes.app.rebuild();
|
RNodes.app.notify();
|
||||||
|
|
||||||
_logger.info('Restore success');
|
_logger.info('Restore success');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import 'package:toolbox/core/extension/context/locale.dart';
|
import 'package:server_box/core/extension/context/locale.dart';
|
||||||
|
|
||||||
enum ErrFrom {
|
enum ErrFrom {
|
||||||
unknown,
|
unknown,
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
typedef GhId = String;
|
|
||||||
|
|
||||||
extension GhIdX on GhId {
|
|
||||||
String get url => 'https://github.com/$this';
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:toolbox/core/extension/context/locale.dart';
|
import 'package:server_box/core/extension/context/locale.dart';
|
||||||
|
|
||||||
enum ContainerMenu {
|
enum ContainerMenu {
|
||||||
start,
|
start,
|
||||||
@@ -21,46 +22,27 @@ enum ContainerMenu {
|
|||||||
terminal,
|
terminal,
|
||||||
//stats,
|
//stats,
|
||||||
];
|
];
|
||||||
} else {
|
|
||||||
return [start, rm, logs];
|
|
||||||
}
|
}
|
||||||
|
return [start, rm, logs];
|
||||||
}
|
}
|
||||||
|
|
||||||
IconData get icon {
|
IconData get icon => switch (this) {
|
||||||
switch (this) {
|
ContainerMenu.start => Icons.play_arrow,
|
||||||
case ContainerMenu.start:
|
ContainerMenu.stop => Icons.stop,
|
||||||
return Icons.play_arrow;
|
ContainerMenu.restart => Icons.restart_alt,
|
||||||
case ContainerMenu.stop:
|
ContainerMenu.rm => Icons.delete,
|
||||||
return Icons.stop;
|
ContainerMenu.logs => Icons.logo_dev,
|
||||||
case ContainerMenu.restart:
|
ContainerMenu.terminal => Icons.terminal,
|
||||||
return Icons.restart_alt;
|
// DockerMenuType.stats => Icons.bar_chart,
|
||||||
case ContainerMenu.rm:
|
};
|
||||||
return Icons.delete;
|
|
||||||
case ContainerMenu.logs:
|
|
||||||
return Icons.logo_dev;
|
|
||||||
case ContainerMenu.terminal:
|
|
||||||
return Icons.terminal;
|
|
||||||
// case DockerMenuType.stats:
|
|
||||||
// return Icons.bar_chart;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String get toStr {
|
String get toStr => switch (this) {
|
||||||
switch (this) {
|
ContainerMenu.start => l10n.start,
|
||||||
case ContainerMenu.start:
|
ContainerMenu.stop => l10n.stop,
|
||||||
return l10n.start;
|
ContainerMenu.restart => l10n.restart,
|
||||||
case ContainerMenu.stop:
|
ContainerMenu.rm => libL10n.delete,
|
||||||
return l10n.stop;
|
ContainerMenu.logs => libL10n.log,
|
||||||
case ContainerMenu.restart:
|
ContainerMenu.terminal => l10n.terminal,
|
||||||
return l10n.restart;
|
// DockerMenuType.stats => s.stats,
|
||||||
case ContainerMenu.rm:
|
};
|
||||||
return l10n.delete;
|
|
||||||
case ContainerMenu.logs:
|
|
||||||
return l10n.log;
|
|
||||||
case ContainerMenu.terminal:
|
|
||||||
return l10n.terminal;
|
|
||||||
// case DockerMenuType.stats:
|
|
||||||
// return s.stats;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hive_flutter/hive_flutter.dart';
|
import 'package:hive_flutter/hive_flutter.dart';
|
||||||
import 'package:icons_plus/icons_plus.dart';
|
import 'package:icons_plus/icons_plus.dart';
|
||||||
import 'package:toolbox/core/extension/context/locale.dart';
|
import 'package:server_box/core/extension/context/locale.dart';
|
||||||
|
|
||||||
part 'server_func.g.dart';
|
part 'server_func.g.dart';
|
||||||
|
|
||||||
@@ -15,8 +15,8 @@ enum ServerFuncBtn {
|
|||||||
container,
|
container,
|
||||||
@HiveField(3)
|
@HiveField(3)
|
||||||
process,
|
process,
|
||||||
@HiveField(4)
|
//@HiveField(4)
|
||||||
pkg,
|
//pkg,
|
||||||
@HiveField(5)
|
@HiveField(5)
|
||||||
snippet,
|
snippet,
|
||||||
@HiveField(6)
|
@HiveField(6)
|
||||||
@@ -30,14 +30,14 @@ enum ServerFuncBtn {
|
|||||||
sftp,
|
sftp,
|
||||||
container,
|
container,
|
||||||
process,
|
process,
|
||||||
pkg,
|
//pkg,
|
||||||
snippet,
|
snippet,
|
||||||
].map((e) => e.index).toList();
|
].map((e) => e.index).toList();
|
||||||
|
|
||||||
IconData get icon => switch (this) {
|
IconData get icon => switch (this) {
|
||||||
sftp => Icons.insert_drive_file,
|
sftp => Icons.insert_drive_file,
|
||||||
snippet => Icons.code,
|
snippet => Icons.code,
|
||||||
pkg => Icons.system_security_update,
|
//pkg => Icons.system_security_update,
|
||||||
container => FontAwesome.docker_brand,
|
container => FontAwesome.docker_brand,
|
||||||
process => Icons.list_alt_outlined,
|
process => Icons.list_alt_outlined,
|
||||||
terminal => Icons.terminal,
|
terminal => Icons.terminal,
|
||||||
@@ -47,7 +47,7 @@ enum ServerFuncBtn {
|
|||||||
String get toStr => switch (this) {
|
String get toStr => switch (this) {
|
||||||
sftp => 'SFTP',
|
sftp => 'SFTP',
|
||||||
snippet => l10n.snippet,
|
snippet => l10n.snippet,
|
||||||
pkg => l10n.pkg,
|
//pkg => l10n.pkg,
|
||||||
container => l10n.container,
|
container => l10n.container,
|
||||||
process => l10n.process,
|
process => l10n.process,
|
||||||
terminal => l10n.terminal,
|
terminal => l10n.terminal,
|
||||||
|
|||||||
@@ -21,8 +21,6 @@ class ServerFuncBtnAdapter extends TypeAdapter<ServerFuncBtn> {
|
|||||||
return ServerFuncBtn.container;
|
return ServerFuncBtn.container;
|
||||||
case 3:
|
case 3:
|
||||||
return ServerFuncBtn.process;
|
return ServerFuncBtn.process;
|
||||||
case 4:
|
|
||||||
return ServerFuncBtn.pkg;
|
|
||||||
case 5:
|
case 5:
|
||||||
return ServerFuncBtn.snippet;
|
return ServerFuncBtn.snippet;
|
||||||
case 6:
|
case 6:
|
||||||
@@ -47,9 +45,6 @@ class ServerFuncBtnAdapter extends TypeAdapter<ServerFuncBtn> {
|
|||||||
case ServerFuncBtn.process:
|
case ServerFuncBtn.process:
|
||||||
writer.writeByte(3);
|
writer.writeByte(3);
|
||||||
break;
|
break;
|
||||||
case ServerFuncBtn.pkg:
|
|
||||||
writer.writeByte(4);
|
|
||||||
break;
|
|
||||||
case ServerFuncBtn.snippet:
|
case ServerFuncBtn.snippet:
|
||||||
writer.writeByte(5);
|
writer.writeByte(5);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
import 'package:hive_flutter/hive_flutter.dart';
|
import 'package:hive_flutter/hive_flutter.dart';
|
||||||
import 'package:toolbox/core/extension/context/locale.dart';
|
import 'package:server_box/core/extension/context/locale.dart';
|
||||||
import 'package:toolbox/data/model/server/server.dart';
|
import 'package:server_box/data/model/server/server.dart';
|
||||||
import 'package:toolbox/data/res/store.dart';
|
import 'package:server_box/data/res/store.dart';
|
||||||
|
|
||||||
part 'net_view.g.dart';
|
part 'net_view.g.dart';
|
||||||
|
|
||||||
@@ -14,27 +15,17 @@ enum NetViewType {
|
|||||||
@HiveField(2)
|
@HiveField(2)
|
||||||
traffic;
|
traffic;
|
||||||
|
|
||||||
NetViewType get next {
|
NetViewType get next => switch (this) {
|
||||||
switch (this) {
|
conn => speed,
|
||||||
case conn:
|
speed => traffic,
|
||||||
return speed;
|
traffic => conn,
|
||||||
case speed:
|
};
|
||||||
return traffic;
|
|
||||||
case traffic:
|
|
||||||
return conn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String get toStr {
|
String get toStr => switch (this) {
|
||||||
switch (this) {
|
NetViewType.conn => l10n.conn,
|
||||||
case NetViewType.conn:
|
NetViewType.traffic => l10n.traffic,
|
||||||
return l10n.conn;
|
NetViewType.speed => l10n.speed,
|
||||||
case NetViewType.traffic:
|
};
|
||||||
return l10n.traffic;
|
|
||||||
case NetViewType.speed:
|
|
||||||
return l10n.speed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(String, String) build(ServerStatus ss) {
|
(String, String) build(ServerStatus ss) {
|
||||||
final ignoreLocal = Stores.setting.ignoreLocalNet.fetch();
|
final ignoreLocal = Stores.setting.ignoreLocalNet.fetch();
|
||||||
@@ -42,7 +33,7 @@ enum NetViewType {
|
|||||||
case NetViewType.conn:
|
case NetViewType.conn:
|
||||||
return (
|
return (
|
||||||
'${l10n.conn}:\n${ss.tcp.maxConn}',
|
'${l10n.conn}:\n${ss.tcp.maxConn}',
|
||||||
'${l10n.failed}:\n${ss.tcp.fail}',
|
'${libL10n.fail}:\n${ss.tcp.fail}',
|
||||||
);
|
);
|
||||||
case NetViewType.speed:
|
case NetViewType.speed:
|
||||||
if (ignoreLocal) {
|
if (ignoreLocal) {
|
||||||
@@ -69,25 +60,15 @@ enum NetViewType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int toJson() {
|
int toJson() => switch (this) {
|
||||||
switch (this) {
|
NetViewType.conn => 0,
|
||||||
case NetViewType.conn:
|
NetViewType.speed => 1,
|
||||||
return 0;
|
NetViewType.traffic => 2,
|
||||||
case NetViewType.speed:
|
};
|
||||||
return 1;
|
|
||||||
case NetViewType.traffic:
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static NetViewType fromJson(int json) {
|
static NetViewType fromJson(int json) => switch (json) {
|
||||||
switch (json) {
|
0 => NetViewType.conn,
|
||||||
case 0:
|
1 => NetViewType.speed,
|
||||||
return NetViewType.conn;
|
_ => NetViewType.traffic,
|
||||||
case 2:
|
};
|
||||||
return NetViewType.traffic;
|
|
||||||
default:
|
|
||||||
return NetViewType.speed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
import 'package:flutter/foundation.dart';
|
|
||||||
|
|
||||||
class RebuildNode implements Listenable {
|
|
||||||
final List<VoidCallback> _listeners = [];
|
|
||||||
|
|
||||||
RebuildNode();
|
|
||||||
|
|
||||||
@override
|
|
||||||
void addListener(VoidCallback listener) {
|
|
||||||
_listeners.add(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void removeListener(VoidCallback listener) {
|
|
||||||
_listeners.remove(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
void rebuild() {
|
|
||||||
for (var listener in _listeners) {
|
|
||||||
listener();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import 'package:fl_lib/fl_lib.dart';
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:icons_plus/icons_plus.dart';
|
import 'package:icons_plus/icons_plus.dart';
|
||||||
import 'package:toolbox/core/extension/context/locale.dart';
|
import 'package:server_box/core/extension/context/locale.dart';
|
||||||
import 'package:toolbox/data/res/store.dart';
|
import 'package:server_box/data/res/store.dart';
|
||||||
|
|
||||||
enum ServerDetailCards {
|
enum ServerDetailCards {
|
||||||
about(Icons.info),
|
about(Icons.info),
|
||||||
@@ -31,7 +31,7 @@ enum ServerDetailCards {
|
|||||||
static final names = values.map((e) => e.name).toList();
|
static final names = values.map((e) => e.name).toList();
|
||||||
|
|
||||||
String get toStr => switch (this) {
|
String get toStr => switch (this) {
|
||||||
about => l10n.about,
|
about => libL10n.about,
|
||||||
cpu => 'CPU',
|
cpu => 'CPU',
|
||||||
mem => 'RAM',
|
mem => 'RAM',
|
||||||
swap => 'Swap',
|
swap => 'Swap',
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import 'package:toolbox/core/extension/context/locale.dart';
|
import 'package:server_box/core/extension/context/locale.dart';
|
||||||
|
|
||||||
import '../../res/build_data.dart';
|
import '../../res/build_data.dart';
|
||||||
import '../server/system.dart';
|
import '../server/system.dart';
|
||||||
@@ -12,52 +12,58 @@ enum ShellFunc {
|
|||||||
suspend,
|
suspend,
|
||||||
;
|
;
|
||||||
|
|
||||||
static const _homeVar = '\$HOME';
|
|
||||||
static const seperator = 'SrvBoxSep';
|
static const seperator = 'SrvBoxSep';
|
||||||
|
|
||||||
/// The suffix `\t` is for formatting
|
/// The suffix `\t` is for formatting
|
||||||
static const cmdDivider = '\necho $seperator\n\t';
|
static const cmdDivider = '\necho $seperator\n\t';
|
||||||
static const _srvBoxDir = '.config/server_box';
|
|
||||||
static const scriptFile = 'mobile_v${BuildData.script}.sh';
|
|
||||||
|
|
||||||
/// Issue #159
|
/// srvboxm -> ServerBox Mobile
|
||||||
|
static const scriptFile = 'srvboxm_v${BuildData.script}.sh';
|
||||||
|
static const scriptDirHome = '~/.config/server_box';
|
||||||
|
static const scriptDirTmp = '/tmp/server_box';
|
||||||
|
|
||||||
|
static final _scriptDirMap = <String, String>{};
|
||||||
|
|
||||||
|
/// Get the script directory for the given [id].
|
||||||
///
|
///
|
||||||
/// Use script commit count as version of shell script.
|
/// Default is [scriptDirTmp]/[scriptFile], if this path is not accessible,
|
||||||
///
|
/// it will be changed to [scriptDirHome]/[scriptFile].
|
||||||
/// So different version of app can run at the same time.
|
static String getScriptDir(String id) {
|
||||||
///
|
return _scriptDirMap.putIfAbsent(id, () {
|
||||||
/// **Can't** use it in SFTP, because SFTP can't recognize `$HOME`
|
return scriptDirTmp;
|
||||||
static String getShellPath(String home) => '$home/$_srvBoxDir/$scriptFile';
|
});
|
||||||
|
|
||||||
static const srvBoxDir = '$_homeVar/$_srvBoxDir';
|
|
||||||
static const _installShellPath = '$_homeVar/$_srvBoxDir/$scriptFile';
|
|
||||||
|
|
||||||
// Issue #299, chmod ~/.config to avoid permission issue
|
|
||||||
static const installShellCmd = """
|
|
||||||
chmod +x ~/.config &> /dev/null
|
|
||||||
mkdir -p $_homeVar/$_srvBoxDir
|
|
||||||
cat > $_installShellPath
|
|
||||||
chmod +x $_installShellPath
|
|
||||||
""";
|
|
||||||
|
|
||||||
String get flag {
|
|
||||||
switch (this) {
|
|
||||||
case ShellFunc.status:
|
|
||||||
return 's';
|
|
||||||
// case ShellFunc.docker:
|
|
||||||
// return 'd';
|
|
||||||
case ShellFunc.process:
|
|
||||||
return 'p';
|
|
||||||
case ShellFunc.shutdown:
|
|
||||||
return 'sd';
|
|
||||||
case ShellFunc.reboot:
|
|
||||||
return 'r';
|
|
||||||
case ShellFunc.suspend:
|
|
||||||
return 'sp';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String get exec => 'sh $_installShellPath -$flag';
|
static void switchScriptDir(String id) => switch (_scriptDirMap[id]) {
|
||||||
|
scriptDirTmp => _scriptDirMap[id] = scriptDirHome,
|
||||||
|
scriptDirHome => _scriptDirMap[id] = scriptDirTmp,
|
||||||
|
_ => _scriptDirMap[id] = scriptDirHome,
|
||||||
|
};
|
||||||
|
|
||||||
|
static String getScriptPath(String id) {
|
||||||
|
return '${getScriptDir(id)}/$scriptFile';
|
||||||
|
}
|
||||||
|
|
||||||
|
static String getInstallShellCmd(String id) {
|
||||||
|
final scriptDir = getScriptDir(id);
|
||||||
|
final scriptPath = '$scriptDir/$scriptFile';
|
||||||
|
return """
|
||||||
|
mkdir -p $scriptDir
|
||||||
|
cat > $scriptPath
|
||||||
|
chmod 744 $scriptPath
|
||||||
|
""";
|
||||||
|
}
|
||||||
|
|
||||||
|
String get flag => switch (this) {
|
||||||
|
ShellFunc.process => 'p',
|
||||||
|
ShellFunc.shutdown => 'sd',
|
||||||
|
ShellFunc.reboot => 'r',
|
||||||
|
ShellFunc.suspend => 'sp',
|
||||||
|
ShellFunc.status => 's',
|
||||||
|
// ShellFunc.docker=> 'd',
|
||||||
|
};
|
||||||
|
|
||||||
|
String exec(String id) => 'sh ${getScriptPath(id)} -$flag';
|
||||||
|
|
||||||
String get name {
|
String get name {
|
||||||
switch (this) {
|
switch (this) {
|
||||||
@@ -213,6 +219,7 @@ enum StatusCmdType {
|
|||||||
'for f in /sys/class/power_supply/*/uevent; do cat "\$f"; echo; done'),
|
'for f in /sys/class/power_supply/*/uevent; do cat "\$f"; echo; done'),
|
||||||
nvidia._('nvidia-smi -q -x'),
|
nvidia._('nvidia-smi -q -x'),
|
||||||
sensors._('sensors'),
|
sensors._('sensors'),
|
||||||
|
cpuBrand._('cat /proc/cpuinfo | grep "model name"'),
|
||||||
;
|
;
|
||||||
|
|
||||||
final String cmd;
|
final String cmd;
|
||||||
@@ -231,6 +238,7 @@ enum BSDStatusCmdType {
|
|||||||
mem._('top -l 1 | grep PhysMem'),
|
mem._('top -l 1 | grep PhysMem'),
|
||||||
//temp,
|
//temp,
|
||||||
host._('hostname'),
|
host._('hostname'),
|
||||||
|
cpuBrand._('sysctl -n machdep.cpu.brand_string'),
|
||||||
;
|
;
|
||||||
|
|
||||||
final String cmd;
|
final String cmd;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:toolbox/view/page/ping.dart';
|
import 'package:server_box/view/page/ping.dart';
|
||||||
import 'package:toolbox/view/page/server/tab.dart';
|
import 'package:server_box/view/page/server/tab.dart';
|
||||||
import 'package:toolbox/view/page/snippet/list.dart';
|
import 'package:server_box/view/page/snippet/list.dart';
|
||||||
import 'package:toolbox/view/page/ssh/tab.dart';
|
import 'package:server_box/view/page/ssh/tab.dart';
|
||||||
|
|
||||||
enum AppTab {
|
enum AppTab {
|
||||||
server,
|
server,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:fl_lib/fl_lib.dart';
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
import 'package:toolbox/data/model/container/type.dart';
|
import 'package:server_box/data/model/container/type.dart';
|
||||||
|
|
||||||
abstract final class ContainerImg {
|
abstract final class ContainerImg {
|
||||||
final String? repository = null;
|
final String? repository = null;
|
||||||
@@ -72,7 +72,7 @@ final class DockerImg implements ContainerImg {
|
|||||||
final String repository;
|
final String repository;
|
||||||
final String size;
|
final String size;
|
||||||
@override
|
@override
|
||||||
final String tag;
|
final String? tag;
|
||||||
|
|
||||||
DockerImg({
|
DockerImg({
|
||||||
required this.containers,
|
required this.containers,
|
||||||
@@ -95,14 +95,30 @@ final class DockerImg implements ContainerImg {
|
|||||||
|
|
||||||
String toRawJson() => json.encode(toJson());
|
String toRawJson() => json.encode(toJson());
|
||||||
|
|
||||||
factory DockerImg.fromJson(Map<String, dynamic> json) => DockerImg(
|
factory DockerImg.fromJson(Map<String, dynamic> json) {
|
||||||
containers: json["Containers"],
|
final containers = switch (json["Containers"]) {
|
||||||
createdAt: json["CreatedAt"],
|
final String a => a,
|
||||||
id: json["ID"],
|
final Object? a => a.toString(),
|
||||||
repository: json["Repository"],
|
};
|
||||||
size: json["Size"],
|
final repo = switch (json["Repository"] ?? json["Names"]) {
|
||||||
tag: json["Tag"],
|
final String a => a,
|
||||||
);
|
final List a => a.firstOrNull.toString(),
|
||||||
|
final Object? a => a.toString(),
|
||||||
|
};
|
||||||
|
final size = switch (json["Size"]) {
|
||||||
|
final String a => a,
|
||||||
|
final int a => a.bytes2Str,
|
||||||
|
final Object? a => a.toString(),
|
||||||
|
};
|
||||||
|
return DockerImg(
|
||||||
|
containers: containers,
|
||||||
|
createdAt: json["CreatedAt"],
|
||||||
|
id: json["ID"] ?? json["Id"] ?? '',
|
||||||
|
repository: repo,
|
||||||
|
size: size,
|
||||||
|
tag: json["Tag"],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => {
|
||||||
"Containers": containers,
|
"Containers": containers,
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:fl_lib/fl_lib.dart';
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
import 'package:toolbox/core/extension/context/locale.dart';
|
import 'package:server_box/core/extension/context/locale.dart';
|
||||||
import 'package:toolbox/data/model/container/type.dart';
|
import 'package:server_box/data/model/container/type.dart';
|
||||||
|
import 'package:server_box/data/res/misc.dart';
|
||||||
|
|
||||||
abstract final class ContainerPs {
|
sealed class ContainerPs {
|
||||||
final String? id = null;
|
final String? id = null;
|
||||||
final String? image = null;
|
final String? image = null;
|
||||||
String? get name;
|
String? get name;
|
||||||
@@ -140,7 +141,10 @@ final class DockerPs implements ContainerPs {
|
|||||||
String? get cmd => null;
|
String? get cmd => null;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool get running => state?.contains('Up ') ?? false;
|
bool get running {
|
||||||
|
if (state?.contains('Exited') == true) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void parseStats(String s) {
|
void parseStats(String s) {
|
||||||
@@ -154,12 +158,12 @@ final class DockerPs implements ContainerPs {
|
|||||||
/// CONTAINER ID NAMES IMAGE STATUS
|
/// CONTAINER ID NAMES IMAGE STATUS
|
||||||
/// a049d689e7a1 aria2-pro p3terx/aria2-pro Up 3 weeks
|
/// a049d689e7a1 aria2-pro p3terx/aria2-pro Up 3 weeks
|
||||||
factory DockerPs.parse(String raw) {
|
factory DockerPs.parse(String raw) {
|
||||||
final parts = raw.split(RegExp(r'\s{2,}'));
|
final parts = raw.split(Miscs.multiBlankreg);
|
||||||
return DockerPs(
|
return DockerPs(
|
||||||
id: parts[0],
|
id: parts[0],
|
||||||
names: parts[1],
|
state: parts[1],
|
||||||
image: parts[2],
|
names: parts[2],
|
||||||
state: parts[3],
|
image: parts[3].trim(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:toolbox/data/model/container/image.dart';
|
import 'package:server_box/data/model/container/image.dart';
|
||||||
import 'package:toolbox/data/model/container/ps.dart';
|
import 'package:server_box/data/model/container/ps.dart';
|
||||||
|
|
||||||
enum ContainerType {
|
enum ContainerType {
|
||||||
docker,
|
docker,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import 'package:toolbox/data/model/server/dist.dart';
|
import 'package:server_box/data/model/server/dist.dart';
|
||||||
|
|
||||||
enum PkgManager {
|
enum PkgManager {
|
||||||
apt,
|
apt,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import 'package:toolbox/data/model/pkg/manager.dart';
|
import 'package:server_box/data/model/pkg/manager.dart';
|
||||||
|
|
||||||
class UpgradePkgInfo {
|
class UpgradePkgInfo {
|
||||||
final PkgManager? _mgr;
|
final PkgManager? _mgr;
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
import 'dart:collection';
|
|
||||||
|
|
||||||
import 'package:fl_chart/fl_chart.dart';
|
import 'package:fl_chart/fl_chart.dart';
|
||||||
import 'package:toolbox/data/model/server/time_seq.dart';
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
import 'package:toolbox/data/res/status.dart';
|
import 'package:server_box/data/model/server/time_seq.dart';
|
||||||
|
import 'package:server_box/data/res/status.dart';
|
||||||
|
|
||||||
|
/// Capacity of the FIFO queue
|
||||||
const _kCap = 30;
|
const _kCap = 30;
|
||||||
|
|
||||||
class Cpus extends TimeSeq<List<SingleCpuCore>> {
|
class Cpus extends TimeSeq<List<SingleCpuCore>> {
|
||||||
Cpus(super.init1, super.init2);
|
Cpus(super.init1, super.init2);
|
||||||
|
|
||||||
|
final Map<String, int> brand = {};
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onUpdate() {
|
void onUpdate() {
|
||||||
_coresCount = now.length;
|
_coresCount = now.length;
|
||||||
@@ -23,10 +25,16 @@ class Cpus extends TimeSeq<List<SingleCpuCore>> {
|
|||||||
|
|
||||||
double usedPercent({int coreIdx = 0}) {
|
double usedPercent({int coreIdx = 0}) {
|
||||||
if (now.length != pre.length) return 0;
|
if (now.length != pre.length) return 0;
|
||||||
final idleDelta = now[coreIdx].idle - pre[coreIdx].idle;
|
if (now.isEmpty) return 0;
|
||||||
final totalDelta = now[coreIdx].total - pre[coreIdx].total;
|
try {
|
||||||
final used = idleDelta / totalDelta;
|
final idleDelta = now[coreIdx].idle - pre[coreIdx].idle;
|
||||||
return used.isNaN ? 0 : 100 - used * 100;
|
final totalDelta = now[coreIdx].total - pre[coreIdx].total;
|
||||||
|
final used = idleDelta / totalDelta;
|
||||||
|
return used.isNaN ? 0 : 100 - used * 100;
|
||||||
|
} catch (e, s) {
|
||||||
|
Loggers.app.warning('Cpus.usedPercent()', e, s);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int _coresCount = 0;
|
int _coresCount = 0;
|
||||||
@@ -175,6 +183,22 @@ class SingleCpuCore extends TimeSeqIface<SingleCpuCore> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final class CpuBrand {
|
||||||
|
static Map<String, int> parse(String raw) {
|
||||||
|
final lines = raw.split('\n');
|
||||||
|
// {brand: count}
|
||||||
|
final brands = <String, int>{};
|
||||||
|
for (var line in lines) {
|
||||||
|
if (line.contains('model name')) {
|
||||||
|
final model = line.split(':').last.trim();
|
||||||
|
final count = brands[model] ?? 0;
|
||||||
|
brands[model] = count + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return brands;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final _bsdCpuPercentReg = RegExp(r'(\d+\.\d+)%');
|
final _bsdCpuPercentReg = RegExp(r'(\d+\.\d+)%');
|
||||||
|
|
||||||
/// TODO: Change this implementation to parse cpu status on BSD system
|
/// TODO: Change this implementation to parse cpu status on BSD system
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:fl_lib/fl_lib.dart';
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
import 'package:toolbox/data/model/server/time_seq.dart';
|
import 'package:server_box/data/model/server/time_seq.dart';
|
||||||
|
|
||||||
import '../../res/misc.dart';
|
import '../../res/misc.dart';
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:fl_lib/fl_lib.dart';
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
import 'package:toolbox/core/extension/context/locale.dart';
|
import 'package:server_box/core/extension/context/locale.dart';
|
||||||
|
|
||||||
enum PveResType {
|
enum PveResType {
|
||||||
lxc,
|
lxc,
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import 'package:fl_lib/fl_lib.dart';
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
import 'package:toolbox/core/extension/context/locale.dart';
|
|
||||||
|
|
||||||
final class SensorAdaptor {
|
final class SensorAdaptor {
|
||||||
final String raw;
|
final String raw;
|
||||||
@@ -37,7 +36,7 @@ final class SensorItem {
|
|||||||
|
|
||||||
String get toMarkdown {
|
String get toMarkdown {
|
||||||
final sb = StringBuffer();
|
final sb = StringBuffer();
|
||||||
sb.writeln('| ${l10n.name} | ${l10n.content} |');
|
sb.writeln('| ${libL10n.name} | ${libL10n.content} |');
|
||||||
sb.writeln('| --- | --- |');
|
sb.writeln('| --- | --- |');
|
||||||
for (final entry in details.entries) {
|
for (final entry in details.entries) {
|
||||||
sb.writeln('| ${entry.key} | ${entry.value} |');
|
sb.writeln('| ${entry.key} | ${entry.value} |');
|
||||||
@@ -80,9 +79,7 @@ final class SensorItem {
|
|||||||
for (var idx = 2; idx < len; idx++) {
|
for (var idx = 2; idx < len; idx++) {
|
||||||
final part = sensorLines[idx];
|
final part = sensorLines[idx];
|
||||||
final detailParts = part.split(':');
|
final detailParts = part.split(':');
|
||||||
if (detailParts.length < 2) {
|
if (detailParts.length < 2) continue;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
final key = detailParts[0].trim();
|
final key = detailParts[0].trim();
|
||||||
final value = detailParts[1].trim();
|
final value = detailParts[1].trim();
|
||||||
details[key] = value;
|
details[key] = value;
|
||||||
|
|||||||
@@ -1,23 +1,18 @@
|
|||||||
import 'package:dartssh2/dartssh2.dart';
|
import 'package:dartssh2/dartssh2.dart';
|
||||||
import 'package:fl_lib/fl_lib.dart';
|
import 'package:server_box/data/model/app/error.dart';
|
||||||
import 'package:toolbox/core/extension/context/locale.dart';
|
import 'package:server_box/data/model/app/shell_func.dart';
|
||||||
import 'package:toolbox/data/model/app/error.dart';
|
import 'package:server_box/data/model/app/tag_pickable.dart';
|
||||||
import 'package:toolbox/data/model/app/shell_func.dart';
|
import 'package:server_box/data/model/server/battery.dart';
|
||||||
import 'package:toolbox/data/model/server/battery.dart';
|
import 'package:server_box/data/model/server/conn.dart';
|
||||||
import 'package:toolbox/data/model/server/conn.dart';
|
import 'package:server_box/data/model/server/cpu.dart';
|
||||||
import 'package:toolbox/data/model/server/cpu.dart';
|
import 'package:server_box/data/model/server/disk.dart';
|
||||||
import 'package:toolbox/data/model/server/disk.dart';
|
import 'package:server_box/data/model/server/memory.dart';
|
||||||
import 'package:toolbox/data/model/server/memory.dart';
|
import 'package:server_box/data/model/server/net_speed.dart';
|
||||||
import 'package:toolbox/data/model/server/net_speed.dart';
|
import 'package:server_box/data/model/server/nvdia.dart';
|
||||||
import 'package:toolbox/data/model/server/nvdia.dart';
|
import 'package:server_box/data/model/server/sensors.dart';
|
||||||
import 'package:toolbox/data/model/server/sensors.dart';
|
import 'package:server_box/data/model/server/server_private_info.dart';
|
||||||
import 'package:toolbox/data/model/server/server_private_info.dart';
|
import 'package:server_box/data/model/server/system.dart';
|
||||||
import 'package:toolbox/data/model/server/system.dart';
|
import 'package:server_box/data/model/server/temp.dart';
|
||||||
import 'package:toolbox/data/model/server/temp.dart';
|
|
||||||
|
|
||||||
import '../app/tag_pickable.dart';
|
|
||||||
|
|
||||||
part 'server.ext.dart';
|
|
||||||
|
|
||||||
class Server implements TagPickable {
|
class Server implements TagPickable {
|
||||||
ServerPrivateInfo spi;
|
ServerPrivateInfo spi;
|
||||||
|
|||||||
@@ -1,58 +0,0 @@
|
|||||||
part of 'server.dart';
|
|
||||||
|
|
||||||
extension ServerX on Server {
|
|
||||||
String getTopRightStr(ServerPrivateInfo spi) {
|
|
||||||
switch (conn) {
|
|
||||||
case ServerConn.disconnected:
|
|
||||||
return l10n.disconnected;
|
|
||||||
case ServerConn.finished:
|
|
||||||
// Highest priority of temperature display
|
|
||||||
final cmdTemp = () {
|
|
||||||
final val = status.customCmds['server_card_top_right'];
|
|
||||||
if (val == null) return null;
|
|
||||||
// This returned value is used on server card top right, so it should
|
|
||||||
// be a single line string.
|
|
||||||
return val.split('\n').lastOrNull;
|
|
||||||
}();
|
|
||||||
final temperatureVal = () {
|
|
||||||
// Second priority
|
|
||||||
final preferTempDev = spi.custom?.preferTempDev;
|
|
||||||
if (preferTempDev != null) {
|
|
||||||
final preferTemp = status.sensors
|
|
||||||
.firstWhereOrNull((e) => e.device == preferTempDev)
|
|
||||||
?.summary
|
|
||||||
?.split(' ')
|
|
||||||
.firstOrNull;
|
|
||||||
if (preferTemp != null) {
|
|
||||||
return double.tryParse(preferTemp.replaceFirst('°C', ''));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Last priority
|
|
||||||
final temp = status.temps.first;
|
|
||||||
if (temp != null) {
|
|
||||||
return temp;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}();
|
|
||||||
final upTime = status.more[StatusCmdType.uptime];
|
|
||||||
final items = [
|
|
||||||
cmdTemp ??
|
|
||||||
(temperatureVal != null
|
|
||||||
? '${temperatureVal.toStringAsFixed(1)}°C'
|
|
||||||
: null),
|
|
||||||
upTime
|
|
||||||
];
|
|
||||||
final str = items.where((e) => e != null && e.isNotEmpty).join(' | ');
|
|
||||||
if (str.isEmpty) return l10n.noResult;
|
|
||||||
return str;
|
|
||||||
case ServerConn.loading:
|
|
||||||
return l10n.serverTabLoading;
|
|
||||||
case ServerConn.connected:
|
|
||||||
return l10n.connected;
|
|
||||||
case ServerConn.connecting:
|
|
||||||
return l10n.serverTabConnecting;
|
|
||||||
case ServerConn.failed:
|
|
||||||
return status.err != null ? l10n.viewErr : l10n.serverTabFailed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import 'package:hive_flutter/hive_flutter.dart';
|
import 'package:hive_flutter/hive_flutter.dart';
|
||||||
import 'package:toolbox/data/model/server/custom.dart';
|
import 'package:server_box/data/model/server/custom.dart';
|
||||||
import 'package:toolbox/data/model/server/server.dart';
|
import 'package:server_box/data/model/server/server.dart';
|
||||||
import 'package:toolbox/data/model/server/wol_cfg.dart';
|
import 'package:server_box/data/model/server/wol_cfg.dart';
|
||||||
import 'package:toolbox/data/res/provider.dart';
|
import 'package:server_box/data/res/provider.dart';
|
||||||
|
|
||||||
import '../app/error.dart';
|
import '../app/error.dart';
|
||||||
|
|
||||||
@@ -42,6 +42,10 @@ class ServerPrivateInfo {
|
|||||||
@HiveField(11)
|
@HiveField(11)
|
||||||
final WakeOnLanCfg? wolCfg;
|
final WakeOnLanCfg? wolCfg;
|
||||||
|
|
||||||
|
/// It only applies to SSH terminal.
|
||||||
|
@HiveField(12)
|
||||||
|
final Map<String, String>? envs;
|
||||||
|
|
||||||
final String id;
|
final String id;
|
||||||
|
|
||||||
const ServerPrivateInfo({
|
const ServerPrivateInfo({
|
||||||
@@ -57,6 +61,7 @@ class ServerPrivateInfo {
|
|||||||
this.jumpId,
|
this.jumpId,
|
||||||
this.custom,
|
this.custom,
|
||||||
this.wolCfg,
|
this.wolCfg,
|
||||||
|
this.envs,
|
||||||
}) : id = '$user@$ip:$port';
|
}) : id = '$user@$ip:$port';
|
||||||
|
|
||||||
static ServerPrivateInfo fromJson(Map<String, dynamic> json) {
|
static ServerPrivateInfo fromJson(Map<String, dynamic> json) {
|
||||||
@@ -76,6 +81,15 @@ class ServerPrivateInfo {
|
|||||||
final wolCfg = json["wolCfg"] == null
|
final wolCfg = json["wolCfg"] == null
|
||||||
? null
|
? null
|
||||||
: WakeOnLanCfg.fromJson(json["wolCfg"].cast<String, dynamic>());
|
: WakeOnLanCfg.fromJson(json["wolCfg"].cast<String, dynamic>());
|
||||||
|
final envs_ = json["envs"] as Map<String, dynamic>?;
|
||||||
|
final envs = <String, String>{};
|
||||||
|
if (envs_ != null) {
|
||||||
|
envs_.forEach((key, value) {
|
||||||
|
if (value is String) {
|
||||||
|
envs[key] = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return ServerPrivateInfo(
|
return ServerPrivateInfo(
|
||||||
name: name,
|
name: name,
|
||||||
@@ -90,6 +104,7 @@ class ServerPrivateInfo {
|
|||||||
jumpId: jumpId,
|
jumpId: jumpId,
|
||||||
custom: custom,
|
custom: custom,
|
||||||
wolCfg: wolCfg,
|
wolCfg: wolCfg,
|
||||||
|
envs: envs.isEmpty ? null : envs,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,6 +138,9 @@ class ServerPrivateInfo {
|
|||||||
if (wolCfg != null) {
|
if (wolCfg != null) {
|
||||||
data["wolCfg"] = wolCfg?.toJson();
|
data["wolCfg"] = wolCfg?.toJson();
|
||||||
}
|
}
|
||||||
|
if (envs != null) {
|
||||||
|
data["envs"] = envs;
|
||||||
|
}
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,6 +180,28 @@ class ServerPrivateInfo {
|
|||||||
String toString() {
|
String toString() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const example = ServerPrivateInfo(
|
||||||
|
name: 'name',
|
||||||
|
ip: 'ip',
|
||||||
|
port: 22,
|
||||||
|
user: 'root',
|
||||||
|
pwd: 'pwd',
|
||||||
|
keyId: 'private_key_id',
|
||||||
|
tags: ['tag1', 'tag2'],
|
||||||
|
alterUrl: 'user@ip:port',
|
||||||
|
autoConnect: true,
|
||||||
|
jumpId: 'jump_server_id',
|
||||||
|
custom: ServerCustom(
|
||||||
|
pveAddr: 'http://localhost:8006',
|
||||||
|
pveIgnoreCert: false,
|
||||||
|
cmds: {
|
||||||
|
'echo': 'echo hello',
|
||||||
|
},
|
||||||
|
preferTempDev: 'nvme-pci-0400',
|
||||||
|
logoUrl: 'https://example.com/logo.png',
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class _IpPort {
|
class _IpPort {
|
||||||
|
|||||||
@@ -29,13 +29,14 @@ class ServerPrivateInfoAdapter extends TypeAdapter<ServerPrivateInfo> {
|
|||||||
jumpId: fields[9] as String?,
|
jumpId: fields[9] as String?,
|
||||||
custom: fields[10] as ServerCustom?,
|
custom: fields[10] as ServerCustom?,
|
||||||
wolCfg: fields[11] as WakeOnLanCfg?,
|
wolCfg: fields[11] as WakeOnLanCfg?,
|
||||||
|
envs: (fields[12] as Map?)?.cast<String, String>(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void write(BinaryWriter writer, ServerPrivateInfo obj) {
|
void write(BinaryWriter writer, ServerPrivateInfo obj) {
|
||||||
writer
|
writer
|
||||||
..writeByte(12)
|
..writeByte(13)
|
||||||
..writeByte(0)
|
..writeByte(0)
|
||||||
..write(obj.name)
|
..write(obj.name)
|
||||||
..writeByte(1)
|
..writeByte(1)
|
||||||
@@ -59,7 +60,9 @@ class ServerPrivateInfoAdapter extends TypeAdapter<ServerPrivateInfo> {
|
|||||||
..writeByte(10)
|
..writeByte(10)
|
||||||
..write(obj.custom)
|
..write(obj.custom)
|
||||||
..writeByte(11)
|
..writeByte(11)
|
||||||
..write(obj.wolCfg);
|
..write(obj.wolCfg)
|
||||||
|
..writeByte(12)
|
||||||
|
..write(obj.envs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import 'package:fl_lib/fl_lib.dart';
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
import 'package:toolbox/data/model/server/battery.dart';
|
import 'package:server_box/data/model/server/battery.dart';
|
||||||
import 'package:toolbox/data/model/server/nvdia.dart';
|
import 'package:server_box/data/model/server/nvdia.dart';
|
||||||
import 'package:toolbox/data/model/server/sensors.dart';
|
import 'package:server_box/data/model/server/sensors.dart';
|
||||||
import 'package:toolbox/data/model/server/server.dart';
|
import 'package:server_box/data/model/server/server.dart';
|
||||||
import 'package:toolbox/data/model/server/system.dart';
|
import 'package:server_box/data/model/server/system.dart';
|
||||||
|
|
||||||
import '../app/shell_func.dart';
|
import '../app/shell_func.dart';
|
||||||
import 'cpu.dart';
|
import 'cpu.dart';
|
||||||
@@ -71,6 +71,9 @@ Future<ServerStatus> _getLinuxStatus(ServerStatusUpdateReq req) async {
|
|||||||
try {
|
try {
|
||||||
final cpus = SingleCpuCore.parse(StatusCmdType.cpu.find(segments));
|
final cpus = SingleCpuCore.parse(StatusCmdType.cpu.find(segments));
|
||||||
req.ss.cpu.update(cpus);
|
req.ss.cpu.update(cpus);
|
||||||
|
final brand = CpuBrand.parse(StatusCmdType.cpuBrand.find(segments));
|
||||||
|
req.ss.cpu.brand.clear();
|
||||||
|
req.ss.cpu.brand.addAll(brand);
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Loggers.app.warning(e, s);
|
Loggers.app.warning(e, s);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
import 'package:hive_flutter/hive_flutter.dart';
|
import 'package:hive_flutter/hive_flutter.dart';
|
||||||
import 'package:toolbox/data/model/server/server_private_info.dart';
|
import 'package:server_box/data/model/server/server_private_info.dart';
|
||||||
|
import 'package:xterm/core.dart';
|
||||||
|
|
||||||
import '../app/tag_pickable.dart';
|
import '../app/tag_pickable.dart';
|
||||||
|
|
||||||
@@ -53,19 +57,116 @@ class Snippet implements TagPickable {
|
|||||||
@override
|
@override
|
||||||
String get tagName => name;
|
String get tagName => name;
|
||||||
|
|
||||||
String fmtWith(ServerPrivateInfo spi) {
|
static final fmtFinder = RegExp(r'\$\{[^{}]+\}');
|
||||||
final fmted = script.replaceAllMapped(
|
|
||||||
RegExp(r'\${.+?}'),
|
String fmtWithSpi(ServerPrivateInfo spi) {
|
||||||
|
return script.replaceAllMapped(
|
||||||
|
fmtFinder,
|
||||||
(match) {
|
(match) {
|
||||||
final key = match.group(0);
|
final key = match.group(0);
|
||||||
final func = fmtArgs[key];
|
final func = fmtArgs[key];
|
||||||
if (func == null) {
|
if (func != null) return func(spi);
|
||||||
return key!;
|
// If not found, return the original content for further processing
|
||||||
}
|
return key ?? '';
|
||||||
return func(spi);
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
return fmted;
|
}
|
||||||
|
|
||||||
|
Future<void> runInTerm(
|
||||||
|
Terminal terminal,
|
||||||
|
ServerPrivateInfo spi, {
|
||||||
|
bool autoEnter = false,
|
||||||
|
}) async {
|
||||||
|
final argsFmted = fmtWithSpi(spi);
|
||||||
|
final matches = fmtFinder.allMatches(argsFmted);
|
||||||
|
|
||||||
|
/// There is no [TerminalKey] in the script
|
||||||
|
if (matches.isEmpty) {
|
||||||
|
terminal.textInput(argsFmted);
|
||||||
|
if (autoEnter) terminal.keyInput(TerminalKey.enter);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Records all start and end indexes of the matches
|
||||||
|
final (starts, ends) = matches.fold((<int>[], <int>[]), (pre, e) {
|
||||||
|
pre.$1.add(e.start);
|
||||||
|
pre.$2.add(e.end);
|
||||||
|
return pre;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check all indexes, `(idx + 1).start` must >= `idx.end`
|
||||||
|
for (var i = 0; i < starts.length - 1; i++) {
|
||||||
|
final lastEnd = ends[i];
|
||||||
|
final nextStart = starts[i + 1];
|
||||||
|
if (nextStart < lastEnd) {
|
||||||
|
throw 'Invalid format: $nextStart < $lastEnd';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start term input
|
||||||
|
if (starts.first > 0) {
|
||||||
|
terminal.textInput(argsFmted.substring(0, starts.first));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process matched
|
||||||
|
for (var idx = 0; idx < starts.length; idx++) {
|
||||||
|
final start = starts[idx];
|
||||||
|
final end = ends[idx];
|
||||||
|
final key = argsFmted.substring(start, end).toLowerCase();
|
||||||
|
|
||||||
|
// Special funcs
|
||||||
|
final special = _find(SnippetFuncs.specialCtrl, key);
|
||||||
|
if (special != null) {
|
||||||
|
final raw = key.substring(special.key.length + 1, key.length - 1);
|
||||||
|
await special.value((term: terminal, raw: raw));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Term keys
|
||||||
|
final termKey = _find(fmtTermKeys, key);
|
||||||
|
if (termKey != null) await _doTermKeys(terminal, termKey, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// End term input
|
||||||
|
if (ends.last < argsFmted.length) {
|
||||||
|
terminal.textInput(argsFmted.substring(ends.last));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (autoEnter) terminal.keyInput(TerminalKey.enter);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _doTermKeys(
|
||||||
|
Terminal terminal,
|
||||||
|
MapEntry<String, TerminalKey> termKey,
|
||||||
|
String key,
|
||||||
|
) async {
|
||||||
|
if (termKey.value == TerminalKey.enter) {
|
||||||
|
terminal.keyInput(TerminalKey.enter);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final ctrlAlt = switch (termKey.value) {
|
||||||
|
TerminalKey.control => (ctrl: true, alt: false),
|
||||||
|
TerminalKey.alt => (ctrl: false, alt: true),
|
||||||
|
_ => (ctrl: false, alt: false),
|
||||||
|
};
|
||||||
|
|
||||||
|
// `${ctrl+ad}` -> `ctrla + d`
|
||||||
|
final chars = key.substring(termKey.key.length + 1, key.length - 1);
|
||||||
|
if (chars.isEmpty) return;
|
||||||
|
final ok = terminal.charInput(
|
||||||
|
chars.codeUnitAt(0),
|
||||||
|
ctrl: ctrlAlt.ctrl,
|
||||||
|
alt: ctrlAlt.alt,
|
||||||
|
);
|
||||||
|
if (!ok) {
|
||||||
|
Loggers.app.warning('Failed to input: $key');
|
||||||
|
}
|
||||||
|
|
||||||
|
terminal.textInput(chars.substring(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
MapEntry<String, T>? _find<T>(Map<String, T> map, String key) {
|
||||||
|
return map.entries.firstWhereOrNull((e) => key.startsWith(e.key));
|
||||||
}
|
}
|
||||||
|
|
||||||
static final fmtArgs = {
|
static final fmtArgs = {
|
||||||
@@ -76,6 +177,20 @@ class Snippet implements TagPickable {
|
|||||||
r'${id}': (ServerPrivateInfo spi) => spi.id,
|
r'${id}': (ServerPrivateInfo spi) => spi.id,
|
||||||
r'${name}': (ServerPrivateInfo spi) => spi.name,
|
r'${name}': (ServerPrivateInfo spi) => spi.name,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// r'${ctrl+ad}' -> TerminalKey.control, a, d
|
||||||
|
static final fmtTermKeys = {
|
||||||
|
r'${ctrl': TerminalKey.control,
|
||||||
|
r'${alt': TerminalKey.alt,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const example = Snippet(
|
||||||
|
name: 'example',
|
||||||
|
script: 'echo hello',
|
||||||
|
tags: ['tag'],
|
||||||
|
note: 'note',
|
||||||
|
autoRunOn: ['server_id'],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class SnippetResult {
|
class SnippetResult {
|
||||||
@@ -89,3 +204,32 @@ class SnippetResult {
|
|||||||
required this.time,
|
required this.time,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef SnippetFuncCtx = ({Terminal term, String raw});
|
||||||
|
|
||||||
|
abstract final class SnippetFuncs {
|
||||||
|
static final specialCtrl = {
|
||||||
|
// `${sleep 3}` -> sleep 3 seconds
|
||||||
|
r'${sleep': SnippetFuncs.sleep,
|
||||||
|
r'${enter': SnippetFuncs.enter,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const help = {
|
||||||
|
'sleep': 'Sleep for a few seconds',
|
||||||
|
'enter': 'Enter a few times',
|
||||||
|
};
|
||||||
|
|
||||||
|
static FutureOr<void> sleep(SnippetFuncCtx ctx) async {
|
||||||
|
final seconds = int.tryParse(ctx.raw);
|
||||||
|
if (seconds == null) return;
|
||||||
|
final duration = Duration(seconds: seconds);
|
||||||
|
await Future.delayed(duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
static FutureOr<void> enter(SnippetFuncCtx ctx) async {
|
||||||
|
final times = int.tryParse(ctx.raw) ?? 1;
|
||||||
|
for (var i = 0; i < times; i++) {
|
||||||
|
ctx.term.keyInput(TerminalKey.enter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import 'package:toolbox/data/model/app/shell_func.dart';
|
import 'package:server_box/data/model/app/shell_func.dart';
|
||||||
|
|
||||||
enum SystemType {
|
enum SystemType {
|
||||||
linux._(linuxSign),
|
linux._(linuxSign),
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import 'package:toolbox/data/res/store.dart';
|
import 'package:server_box/data/res/store.dart';
|
||||||
|
|
||||||
class TryLimiter {
|
class TryLimiter {
|
||||||
final Map<String, int> _triedTimes = {};
|
final Map<String, int> _triedTimes = {};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:dartssh2/dartssh2.dart';
|
import 'package:dartssh2/dartssh2.dart';
|
||||||
import 'package:toolbox/data/model/sftp/absolute_path.dart';
|
import 'package:server_box/data/model/sftp/absolute_path.dart';
|
||||||
|
|
||||||
class SftpBrowserStatus {
|
class SftpBrowserStatus {
|
||||||
List<SftpName>? files;
|
List<SftpName>? files;
|
||||||
|
|||||||
@@ -1,11 +1,4 @@
|
|||||||
import 'dart:async';
|
part of 'worker.dart';
|
||||||
|
|
||||||
import 'package:fl_lib/fl_lib.dart';
|
|
||||||
import 'package:toolbox/data/res/store.dart';
|
|
||||||
|
|
||||||
import '../../../core/utils/server.dart';
|
|
||||||
import '../server/server_private_info.dart';
|
|
||||||
import 'worker.dart';
|
|
||||||
|
|
||||||
class SftpReq {
|
class SftpReq {
|
||||||
final ServerPrivateInfo spi;
|
final ServerPrivateInfo spi;
|
||||||
@@ -69,9 +62,8 @@ class SftpReqStatus {
|
|||||||
int get hashCode => id ^ super.hashCode;
|
int get hashCode => id ^ super.hashCode;
|
||||||
|
|
||||||
void dispose() {
|
void dispose() {
|
||||||
// ignore: deprecated_member_use_from_same_package
|
worker._dispose();
|
||||||
worker.dispose();
|
completer?.complete(true);
|
||||||
completer?.complete();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void onNotify(dynamic event) {
|
void onNotify(dynamic event) {
|
||||||
|
|||||||
@@ -5,9 +5,12 @@ import 'dart:typed_data';
|
|||||||
|
|
||||||
import 'package:dartssh2/dartssh2.dart';
|
import 'package:dartssh2/dartssh2.dart';
|
||||||
import 'package:easy_isolate/easy_isolate.dart';
|
import 'package:easy_isolate/easy_isolate.dart';
|
||||||
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
|
import 'package:server_box/core/utils/server.dart';
|
||||||
|
import 'package:server_box/data/model/server/server_private_info.dart';
|
||||||
|
import 'package:server_box/data/res/store.dart';
|
||||||
|
|
||||||
import '../../../core/utils/server.dart';
|
part 'req.dart';
|
||||||
import 'req.dart';
|
|
||||||
|
|
||||||
class SftpWorker {
|
class SftpWorker {
|
||||||
final Function(Object event) onNotify;
|
final Function(Object event) onNotify;
|
||||||
@@ -20,14 +23,7 @@ class SftpWorker {
|
|||||||
required this.req,
|
required this.req,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Use [@Deprecated] to prevent calling [SftpWorker.dispose] directly
|
void _dispose() {
|
||||||
///
|
|
||||||
/// Don't delete this method
|
|
||||||
@Deprecated(
|
|
||||||
"Use [SftpWorkerStatus.dispose] to dispose the worker, "
|
|
||||||
"instead of [SftpWorker.dispose]",
|
|
||||||
)
|
|
||||||
void dispose() {
|
|
||||||
worker.dispose();
|
worker.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hive_flutter/hive_flutter.dart';
|
import 'package:hive_flutter/hive_flutter.dart';
|
||||||
import 'package:toolbox/core/extension/context/locale.dart';
|
import 'package:server_box/core/extension/context/locale.dart';
|
||||||
import 'package:xterm/core.dart';
|
import 'package:xterm/core.dart';
|
||||||
|
|
||||||
part 'virtual_key.g.dart';
|
part 'virtual_key.g.dart';
|
||||||
|
|||||||
@@ -3,13 +3,6 @@ import 'package:fl_lib/fl_lib.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class AppProvider extends ChangeNotifier {
|
class AppProvider extends ChangeNotifier {
|
||||||
int? _newestBuild;
|
|
||||||
int? get newestBuild => _newestBuild;
|
|
||||||
set newestBuild(int? build) {
|
|
||||||
_newestBuild = build;
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
BuildContext? ctx;
|
BuildContext? ctx;
|
||||||
|
|
||||||
bool isWearOS = false;
|
bool isWearOS = false;
|
||||||
|
|||||||
@@ -4,13 +4,13 @@ import 'dart:convert';
|
|||||||
import 'package:dartssh2/dartssh2.dart';
|
import 'package:dartssh2/dartssh2.dart';
|
||||||
import 'package:fl_lib/fl_lib.dart';
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:toolbox/core/extension/ssh_client.dart';
|
import 'package:server_box/core/extension/ssh_client.dart';
|
||||||
import 'package:toolbox/data/model/app/shell_func.dart';
|
import 'package:server_box/data/model/app/shell_func.dart';
|
||||||
import 'package:toolbox/data/model/container/image.dart';
|
import 'package:server_box/data/model/container/image.dart';
|
||||||
import 'package:toolbox/data/model/container/ps.dart';
|
import 'package:server_box/data/model/container/ps.dart';
|
||||||
import 'package:toolbox/data/model/app/error.dart';
|
import 'package:server_box/data/model/app/error.dart';
|
||||||
import 'package:toolbox/data/model/container/type.dart';
|
import 'package:server_box/data/model/container/type.dart';
|
||||||
import 'package:toolbox/data/res/store.dart';
|
import 'package:server_box/data/res/store.dart';
|
||||||
|
|
||||||
final _dockerNotFound =
|
final _dockerNotFound =
|
||||||
RegExp(r"command not found|Unknown command|Command '\w+' not found");
|
RegExp(r"command not found|Unknown command|Command '\w+' not found");
|
||||||
@@ -160,11 +160,18 @@ class ContainerProvider extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Parse images
|
// Parse images
|
||||||
final imageRaw = ContainerCmdType.images.find(segments);
|
final imageRaw = ContainerCmdType.images.find(segments).trim();
|
||||||
|
final isEntireJson = imageRaw.startsWith('[') && imageRaw.endsWith(']');
|
||||||
try {
|
try {
|
||||||
final imgLines = imageRaw.split('\n');
|
if (isEntireJson) {
|
||||||
imgLines.removeWhere((element) => element.isEmpty);
|
images = (json.decode(imageRaw) as List)
|
||||||
images = imgLines.map((e) => ContainerImg.fromRawJson(e, type)).toList();
|
.map((e) => ContainerImg.fromRawJson(json.encode(e), type))
|
||||||
|
.toList();
|
||||||
|
} else {
|
||||||
|
final lines = imageRaw.split('\n');
|
||||||
|
lines.removeWhere((element) => element.isEmpty);
|
||||||
|
images = lines.map((e) => ContainerImg.fromRawJson(e, type)).toList();
|
||||||
|
}
|
||||||
} catch (e, trace) {
|
} catch (e, trace) {
|
||||||
error = ContainerErr(
|
error = ContainerErr(
|
||||||
type: ContainerErrType.parseImages,
|
type: ContainerErrType.parseImages,
|
||||||
@@ -276,9 +283,13 @@ enum ContainerCmdType {
|
|||||||
return switch (this) {
|
return switch (this) {
|
||||||
ContainerCmdType.version => '$prefix version $_jsonFmt',
|
ContainerCmdType.version => '$prefix version $_jsonFmt',
|
||||||
ContainerCmdType.ps => switch (type) {
|
ContainerCmdType.ps => switch (type) {
|
||||||
|
/// TODO: Rollback to json format when permformance recovers.
|
||||||
/// Use [_jsonFmt] in Docker will cause the operation to slow down.
|
/// Use [_jsonFmt] in Docker will cause the operation to slow down.
|
||||||
ContainerType.docker => '$prefix ps -a --format "table '
|
ContainerType.docker => '$prefix ps -a --format "table {{printf \\"'
|
||||||
'{{printf \\"${"%-30.30s " * 4}\\" .ID .Names .Image .Status}}"',
|
'%-15.15s '
|
||||||
|
'%-30.30s '
|
||||||
|
'${"%-50.50s " * 2}\\"'
|
||||||
|
' .ID .Status .Names .Image}}"',
|
||||||
ContainerType.podman => '$prefix ps -a $_jsonFmt',
|
ContainerType.podman => '$prefix ps -a $_jsonFmt',
|
||||||
},
|
},
|
||||||
ContainerCmdType.stats =>
|
ContainerCmdType.stats =>
|
||||||
@@ -294,6 +305,6 @@ enum ContainerCmdType {
|
|||||||
}) {
|
}) {
|
||||||
return ContainerCmdType.values
|
return ContainerCmdType.values
|
||||||
.map((e) => e.exec(type, sudo: sudo, includeStats: includeStats))
|
.map((e) => e.exec(type, sudo: sudo, includeStats: includeStats))
|
||||||
.join(' && echo ${ShellFunc.seperator} && ');
|
.join('\necho ${ShellFunc.seperator}\n');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:toolbox/data/model/server/private_key_info.dart';
|
import 'package:server_box/data/model/server/private_key_info.dart';
|
||||||
import 'package:toolbox/data/res/store.dart';
|
import 'package:server_box/data/res/store.dart';
|
||||||
|
|
||||||
class PrivateKeyProvider extends ChangeNotifier {
|
class PrivateKeyProvider extends ChangeNotifier {
|
||||||
List<PrivateKeyInfo> get pkis => _pkis;
|
List<PrivateKeyInfo> get pkis => _pkis;
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ import 'package:dio/dio.dart';
|
|||||||
import 'package:dio/io.dart';
|
import 'package:dio/io.dart';
|
||||||
import 'package:fl_lib/fl_lib.dart';
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:toolbox/core/extension/context/locale.dart';
|
import 'package:server_box/core/extension/context/locale.dart';
|
||||||
import 'package:toolbox/data/model/app/error.dart';
|
import 'package:server_box/data/model/app/error.dart';
|
||||||
import 'package:toolbox/data/model/server/pve.dart';
|
import 'package:server_box/data/model/server/pve.dart';
|
||||||
import 'package:toolbox/data/model/server/server_private_info.dart';
|
import 'package:server_box/data/model/server/server_private_info.dart';
|
||||||
import 'package:dartssh2/dartssh2.dart';
|
import 'package:dartssh2/dartssh2.dart';
|
||||||
|
|
||||||
typedef PveCtrlFunc = Future<bool> Function(String node, String id);
|
typedef PveCtrlFunc = Future<bool> Function(String node, String id);
|
||||||
|
|||||||
@@ -1,24 +1,23 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
// import 'dart:io';
|
||||||
|
|
||||||
import 'package:computer/computer.dart';
|
import 'package:computer/computer.dart';
|
||||||
import 'package:dartssh2/dartssh2.dart';
|
import 'package:dartssh2/dartssh2.dart';
|
||||||
import 'package:fl_lib/fl_lib.dart';
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:toolbox/core/extension/ssh_client.dart';
|
import 'package:server_box/core/extension/ssh_client.dart';
|
||||||
import 'package:toolbox/core/utils/ssh_auth.dart';
|
import 'package:server_box/core/utils/ssh_auth.dart';
|
||||||
import 'package:toolbox/data/model/app/error.dart';
|
import 'package:server_box/data/model/app/error.dart';
|
||||||
import 'package:toolbox/data/model/app/shell_func.dart';
|
import 'package:server_box/data/model/app/shell_func.dart';
|
||||||
import 'package:toolbox/data/model/server/system.dart';
|
import 'package:server_box/data/model/server/system.dart';
|
||||||
import 'package:toolbox/data/model/sftp/req.dart';
|
// import 'package:server_box/data/model/sftp/req.dart';
|
||||||
import 'package:toolbox/data/res/provider.dart';
|
// import 'package:server_box/data/res/provider.dart';
|
||||||
import 'package:toolbox/data/res/store.dart';
|
import 'package:server_box/data/res/store.dart';
|
||||||
|
|
||||||
import '../../core/utils/server.dart';
|
import '../../core/utils/server.dart';
|
||||||
import '../model/server/server.dart';
|
import '../model/server/server.dart';
|
||||||
import '../model/server/server_private_info.dart';
|
import '../model/server/server_private_info.dart';
|
||||||
import '../model/server/server_status_update_req.dart';
|
import '../model/server/server_status_update_req.dart';
|
||||||
import '../model/server/snippet.dart';
|
|
||||||
import '../model/server/try_limiter.dart';
|
import '../model/server/try_limiter.dart';
|
||||||
import '../res/status.dart';
|
import '../res/status.dart';
|
||||||
|
|
||||||
@@ -27,8 +26,8 @@ class ServerProvider extends ChangeNotifier {
|
|||||||
Iterable<Server> get servers => _servers.values;
|
Iterable<Server> get servers => _servers.values;
|
||||||
final List<String> _serverOrder = [];
|
final List<String> _serverOrder = [];
|
||||||
List<String> get serverOrder => _serverOrder;
|
List<String> get serverOrder => _serverOrder;
|
||||||
final _tags = ValueNotifier(<String>[]);
|
final _tags = ValueNotifier(<String>{});
|
||||||
ValueNotifier<List<String>> get tags => _tags;
|
ValueNotifier<Set<String>> get tags => _tags;
|
||||||
|
|
||||||
Timer? _timer;
|
Timer? _timer;
|
||||||
|
|
||||||
@@ -86,7 +85,6 @@ class ServerProvider extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _updateTags() {
|
void _updateTags() {
|
||||||
_tags.value.clear();
|
|
||||||
for (final s in _servers.values) {
|
for (final s in _servers.values) {
|
||||||
if (s.spi.tags == null) continue;
|
if (s.spi.tags == null) continue;
|
||||||
for (final t in s.spi.tags!) {
|
for (final t in s.spi.tags!) {
|
||||||
@@ -95,21 +93,7 @@ class ServerProvider extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_tags.value.sort();
|
_tags.value = (_tags.value.toList()..sort()).toSet();
|
||||||
_tags.notifyListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
void renameTag(String old, String new_) {
|
|
||||||
for (final s in _servers.values) {
|
|
||||||
if (s.spi.tags == null) continue;
|
|
||||||
for (var i = 0; i < s.spi.tags!.length; i++) {
|
|
||||||
if (s.spi.tags![i] == old) {
|
|
||||||
s.spi.tags![i] = new_;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Stores.server.update(s.spi, s.spi);
|
|
||||||
}
|
|
||||||
_updateTags();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Server genServer(ServerPrivateInfo spi) {
|
Server genServer(ServerPrivateInfo spi) {
|
||||||
@@ -314,62 +298,41 @@ class ServerProvider extends ChangeNotifier {
|
|||||||
|
|
||||||
_setServerState(s, ServerConn.connected);
|
_setServerState(s, ServerConn.connected);
|
||||||
|
|
||||||
// Write script to server
|
|
||||||
// by ssh
|
|
||||||
final scriptRaw = ShellFunc.allScript(spi.custom?.cmds).uint8List;
|
final scriptRaw = ShellFunc.allScript(spi.custom?.cmds).uint8List;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await s.client?.runForOutput(
|
final writeScriptResult = await s.client!.runForOutput(
|
||||||
ShellFunc.installShellCmd,
|
ShellFunc.getInstallShellCmd(spi.id),
|
||||||
action: (session) async {
|
action: (session) async {
|
||||||
session.stdin.add(scriptRaw);
|
session.stdin.add(scriptRaw);
|
||||||
session.stdin.close();
|
session.stdin.close();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
if (writeScriptResult.isNotEmpty) {
|
||||||
|
ShellFunc.switchScriptDir(spi.id);
|
||||||
|
throw String.fromCharCodes(writeScriptResult);
|
||||||
|
}
|
||||||
} on SSHAuthAbortError catch (e) {
|
} on SSHAuthAbortError catch (e) {
|
||||||
TryLimiter.inc(sid);
|
TryLimiter.inc(sid);
|
||||||
s.status.err = SSHErr(type: SSHErrType.auth, message: e.toString());
|
final err = SSHErr(type: SSHErrType.auth, message: e.toString());
|
||||||
|
s.status.err = err;
|
||||||
|
Loggers.app.warning(err);
|
||||||
_setServerState(s, ServerConn.failed);
|
_setServerState(s, ServerConn.failed);
|
||||||
return;
|
return;
|
||||||
} on SSHAuthFailError catch (e) {
|
} on SSHAuthFailError catch (e) {
|
||||||
TryLimiter.inc(sid);
|
TryLimiter.inc(sid);
|
||||||
s.status.err = SSHErr(type: SSHErrType.auth, message: e.toString());
|
final err = SSHErr(type: SSHErrType.auth, message: e.toString());
|
||||||
|
s.status.err = err;
|
||||||
|
Loggers.app.warning(err);
|
||||||
_setServerState(s, ServerConn.failed);
|
_setServerState(s, ServerConn.failed);
|
||||||
return;
|
return;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Loggers.app.warning('Write script to ${spi.name} by shell', e);
|
// If max try times < 2 and can't write script, this will stop the status getting and etc.
|
||||||
|
// TryLimiter.inc(sid);
|
||||||
/// by sftp
|
final err = SSHErr(type: SSHErrType.writeScript, message: e.toString());
|
||||||
final localPath = Paths.doc.joinPath('install.sh');
|
s.status.err = err;
|
||||||
final file = File(localPath);
|
Loggers.app.warning(err);
|
||||||
try {
|
_setServerState(s, ServerConn.failed);
|
||||||
file.writeAsBytes(scriptRaw);
|
|
||||||
final completer = Completer();
|
|
||||||
final homePath = (await s.client?.run('echo \$HOME').string)?.trim();
|
|
||||||
if (homePath == null || homePath.isEmpty) {
|
|
||||||
throw Exception('Got empty home path');
|
|
||||||
}
|
|
||||||
final remotePath = ShellFunc.getShellPath(homePath);
|
|
||||||
final reqId = Pros.sftp.add(
|
|
||||||
SftpReq(spi, remotePath, localPath, SftpReqType.upload),
|
|
||||||
completer: completer,
|
|
||||||
);
|
|
||||||
await completer.future;
|
|
||||||
final err = Pros.sftp.get(reqId)?.error;
|
|
||||||
if (err != null) {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
} catch (ee) {
|
|
||||||
TryLimiter.inc(sid);
|
|
||||||
s.status.err = SSHErr(
|
|
||||||
type: SSHErrType.writeScript,
|
|
||||||
message: '$e\n\n$ee',
|
|
||||||
);
|
|
||||||
_setServerState(s, ServerConn.failed);
|
|
||||||
Loggers.app.warning('Write script to ${spi.name} by sftp', ee);
|
|
||||||
return;
|
|
||||||
} finally {
|
|
||||||
if (await file.exists()) await file.delete();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -386,7 +349,7 @@ class ServerProvider extends ChangeNotifier {
|
|||||||
String? raw;
|
String? raw;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
raw = await s.client?.run(ShellFunc.status.exec).string;
|
raw = await s.client?.run(ShellFunc.status.exec(spi.id)).string;
|
||||||
segments = raw?.split(ShellFunc.seperator).map((e) => e.trim()).toList();
|
segments = raw?.split(ShellFunc.seperator).map((e) => e.trim()).toList();
|
||||||
if (raw == null || raw.isEmpty || segments == null || segments.isEmpty) {
|
if (raw == null || raw.isEmpty || segments == null || segments.isEmpty) {
|
||||||
if (Stores.setting.keepStatusWhenErr.fetch()) {
|
if (Stores.setting.keepStatusWhenErr.fetch()) {
|
||||||
@@ -459,26 +422,4 @@ class ServerProvider extends ChangeNotifier {
|
|||||||
// reset try times only after prepared successfully
|
// reset try times only after prepared successfully
|
||||||
TryLimiter.reset(sid);
|
TryLimiter.reset(sid);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<SnippetResult?> runSnippet(String id, Snippet snippet) async {
|
|
||||||
final server = _servers[id];
|
|
||||||
if (server == null) return null;
|
|
||||||
final watch = Stopwatch()..start();
|
|
||||||
final result = await server.client?.run(snippet.fmtWith(server.spi)).string;
|
|
||||||
final time = watch.elapsed;
|
|
||||||
watch.stop();
|
|
||||||
if (result == null) return null;
|
|
||||||
return SnippetResult(
|
|
||||||
dest: _servers[id]?.spi.name,
|
|
||||||
result: result,
|
|
||||||
time: time,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Future<List<SnippetResult?>> runSnippetsMulti(
|
|
||||||
// List<String> ids,
|
|
||||||
// Snippet snippet,
|
|
||||||
// ) async {
|
|
||||||
// return await Future.wait(ids.map((id) async => runSnippet(id, snippet)));
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:server_box/data/model/sftp/worker.dart';
|
||||||
import '../model/sftp/req.dart';
|
|
||||||
|
|
||||||
class SftpProvider extends ChangeNotifier {
|
class SftpProvider extends ChangeNotifier {
|
||||||
final List<SftpReqStatus> _status = [];
|
final List<SftpReqStatus> _status = [];
|
||||||
|
|||||||
@@ -2,15 +2,14 @@ import 'dart:convert';
|
|||||||
|
|
||||||
import 'package:fl_lib/fl_lib.dart';
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:toolbox/data/model/server/snippet.dart';
|
import 'package:server_box/data/model/server/snippet.dart';
|
||||||
import 'package:toolbox/data/res/store.dart';
|
import 'package:server_box/data/res/store.dart';
|
||||||
|
|
||||||
class SnippetProvider extends ChangeNotifier {
|
class SnippetProvider extends ChangeNotifier {
|
||||||
late List<Snippet> _snippets;
|
late List<Snippet> _snippets;
|
||||||
List<Snippet> get snippets => _snippets;
|
List<Snippet> get snippets => _snippets;
|
||||||
|
|
||||||
final _tags = ValueNotifier(<String>[]);
|
final tags = ValueNotifier(<String>{});
|
||||||
ValueNotifier<List<String>> get tags => _tags;
|
|
||||||
|
|
||||||
void load() {
|
void load() {
|
||||||
_snippets = Stores.snippet.fetch();
|
_snippets = Stores.snippet.fetch();
|
||||||
@@ -29,16 +28,14 @@ class SnippetProvider extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _updateTags() {
|
void _updateTags() {
|
||||||
_tags.value.clear();
|
final tags_ = <String>{};
|
||||||
final tags = <String>{};
|
|
||||||
for (final s in _snippets) {
|
for (final s in _snippets) {
|
||||||
if (s.tags?.isEmpty ?? true) {
|
final t = s.tags;
|
||||||
continue;
|
if (t != null) {
|
||||||
|
tags_.addAll(t);
|
||||||
}
|
}
|
||||||
tags.addAll(s.tags!);
|
|
||||||
}
|
}
|
||||||
_tags.value.addAll(tags);
|
tags.value = tags_;
|
||||||
_tags.notifyListeners();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void add(Snippet snippet) {
|
void add(Snippet snippet) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:toolbox/data/res/store.dart';
|
import 'package:server_box/data/res/store.dart';
|
||||||
import 'package:xterm/core.dart';
|
import 'package:xterm/core.dart';
|
||||||
|
|
||||||
class VirtKeyProvider extends TerminalInputHandler with ChangeNotifier {
|
class VirtKeyProvider extends TerminalInputHandler with ChangeNotifier {
|
||||||
|
|||||||
@@ -2,9 +2,6 @@
|
|||||||
|
|
||||||
class BuildData {
|
class BuildData {
|
||||||
static const String name = "ServerBox";
|
static const String name = "ServerBox";
|
||||||
static const int build = 923;
|
static const int build = 1051;
|
||||||
static const String engine = "3.22.1";
|
static const int script = 56;
|
||||||
static const String buildAt = "2024-06-01 22:31:14";
|
|
||||||
static const int modifications = 4;
|
|
||||||
static const int script = 48;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,28 @@
|
|||||||
import 'package:toolbox/data/model/app/github_id.dart';
|
|
||||||
|
|
||||||
abstract final class GithubIds {
|
abstract final class GithubIds {
|
||||||
// Thanks
|
// Thanks
|
||||||
// If you want to change your Github ID, please open an issue.
|
// If you want to change your Github ID, please open an issue.
|
||||||
static const contributors = <GhId>{
|
static const contributors = <GhId>{
|
||||||
'PaperCube',
|
'PaperCube',
|
||||||
|
'Integral-Tech',
|
||||||
'its-tom',
|
'its-tom',
|
||||||
|
'leganck',
|
||||||
'azkadev',
|
'azkadev',
|
||||||
'kalashnikov',
|
'kalashnikov',
|
||||||
'FrancXPT',
|
|
||||||
'RainSunMe',
|
|
||||||
'calvinweb',
|
'calvinweb',
|
||||||
|
'No06',
|
||||||
|
'QazCetelic',
|
||||||
|
'RainSunMe',
|
||||||
|
'FrancXPT',
|
||||||
'Liloupar',
|
'Liloupar',
|
||||||
'dccif',
|
'dccif',
|
||||||
'QazCetelic',
|
'mikropsoft',
|
||||||
};
|
};
|
||||||
|
|
||||||
static const participants = <GhId>{
|
static const participants = <GhId>{
|
||||||
'jaychoubaby',
|
'jaychoubaby',
|
||||||
'fecture',
|
'fecture',
|
||||||
'Tao173',
|
'Tao173',
|
||||||
|
'Jasonzhu1207',
|
||||||
'QingAnLe',
|
'QingAnLe',
|
||||||
'wxdjs',
|
'wxdjs',
|
||||||
'Aeorq',
|
'Aeorq',
|
||||||
@@ -72,6 +75,33 @@ abstract final class GithubIds {
|
|||||||
'pgs666',
|
'pgs666',
|
||||||
'FHU-yezi',
|
'FHU-yezi',
|
||||||
'ZRY233',
|
'ZRY233',
|
||||||
'Jasonzhu1207',
|
'sakuraanzu',
|
||||||
|
'licaon-kter',
|
||||||
|
'77160860',
|
||||||
|
'mijjjj',
|
||||||
|
'muyunil',
|
||||||
|
'Hua159',
|
||||||
|
'jaydong2016',
|
||||||
|
'geol',
|
||||||
|
'Mooling0602',
|
||||||
|
'IllTamer',
|
||||||
|
'marlkiller',
|
||||||
|
'hlarc',
|
||||||
|
'itsandrewpao',
|
||||||
|
'StudyingLover',
|
||||||
|
'QJAG1024',
|
||||||
|
'Wuming-HUST',
|
||||||
|
'WolfCanglong',
|
||||||
|
'liwenjie119',
|
||||||
|
'logce',
|
||||||
|
'h-lyf',
|
||||||
|
'88484396',
|
||||||
|
'honggeigei',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef GhId = String;
|
||||||
|
|
||||||
|
extension GhIdX on GhId {
|
||||||
|
String get url => 'https://github.com/$this';
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'dart:convert';
|
|||||||
|
|
||||||
abstract final class Miscs {
|
abstract final class Miscs {
|
||||||
static final blankReg = RegExp(r'\s+');
|
static final blankReg = RegExp(r'\s+');
|
||||||
|
static final multiBlankreg = RegExp(r'\s{2,}');
|
||||||
|
|
||||||
/// RegExp for password request
|
/// RegExp for password request
|
||||||
static final pwdRequestWithUserReg = RegExp(r'\[sudo\] password for (.+):');
|
static final pwdRequestWithUserReg = RegExp(r'\[sudo\] password for (.+):');
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
import 'package:fl_lib/fl_lib.dart';
|
import 'package:server_box/data/provider/app.dart';
|
||||||
import 'package:toolbox/data/provider/app.dart';
|
import 'package:server_box/data/provider/private_key.dart';
|
||||||
import 'package:toolbox/data/provider/private_key.dart';
|
import 'package:server_box/data/provider/server.dart';
|
||||||
import 'package:toolbox/data/provider/server.dart';
|
import 'package:server_box/data/provider/sftp.dart';
|
||||||
import 'package:toolbox/data/provider/sftp.dart';
|
import 'package:server_box/data/provider/snippet.dart';
|
||||||
import 'package:toolbox/data/provider/snippet.dart';
|
|
||||||
|
|
||||||
abstract final class Pros {
|
abstract final class Pros {
|
||||||
static final app = AppProvider();
|
static final app = AppProvider();
|
||||||
static final debug = DebugProvider(maxLines: 177);
|
|
||||||
static final key = PrivateKeyProvider();
|
static final key = PrivateKeyProvider();
|
||||||
static final server = ServerProvider();
|
static final server = ServerProvider();
|
||||||
static final sftp = SftpProvider();
|
static final sftp = SftpProvider();
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:toolbox/data/model/app/rebuild.dart';
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
|
|
||||||
abstract final class RebuildNodes {
|
abstract final class RNodes {
|
||||||
static final app = RebuildNode();
|
static final app = RNode();
|
||||||
|
static final dark = false.vn;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:toolbox/data/model/server/server.dart';
|
import 'package:server_box/data/model/server/server.dart';
|
||||||
import 'package:toolbox/data/model/server/temp.dart';
|
import 'package:server_box/data/model/server/temp.dart';
|
||||||
|
|
||||||
import '../model/server/cpu.dart';
|
import '../model/server/cpu.dart';
|
||||||
import '../model/server/disk.dart';
|
import '../model/server/disk.dart';
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import 'package:fl_lib/fl_lib.dart';
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
import 'package:toolbox/data/store/container.dart';
|
import 'package:server_box/data/store/container.dart';
|
||||||
import 'package:toolbox/data/store/history.dart';
|
import 'package:server_box/data/store/history.dart';
|
||||||
import 'package:toolbox/data/store/private_key.dart';
|
import 'package:server_box/data/store/private_key.dart';
|
||||||
import 'package:toolbox/data/store/server.dart';
|
import 'package:server_box/data/store/server.dart';
|
||||||
import 'package:toolbox/data/store/setting.dart';
|
import 'package:server_box/data/store/setting.dart';
|
||||||
import 'package:toolbox/data/store/snippet.dart';
|
import 'package:server_box/data/store/snippet.dart';
|
||||||
|
|
||||||
abstract final class Stores {
|
abstract final class Stores {
|
||||||
static final setting = SettingStore();
|
static final setting = SettingStore();
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
abstract final class Urls {
|
abstract final class Urls {
|
||||||
static const cdnBase = 'https://cdn.lolli.tech/serverbox';
|
static const cdnBase = 'https://cdn.lolli.tech/serverbox';
|
||||||
static const updateCfg = '$cdnBase/update.json';
|
static const updateCfg = '$cdnBase/update2.json';
|
||||||
static const myGithub = 'https://github.com/lollipopkit';
|
static const myGithub = 'https://github.com/lollipopkit';
|
||||||
static const appHelp = '$myGithub/flutter_server_box#-help';
|
static const thisRepo = '$myGithub/flutter_server_box';
|
||||||
static const appWiki = '$myGithub/flutter_server_box/wiki';
|
static const appHelp = '$thisRepo#-help';
|
||||||
|
static const appWiki = '$thisRepo/wiki';
|
||||||
static const analysis = 'https://countly.lolli.tech';
|
static const analysis = 'https://countly.lolli.tech';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:fl_lib/fl_lib.dart';
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
import 'package:toolbox/data/model/container/type.dart';
|
import 'package:server_box/data/model/container/type.dart';
|
||||||
import 'package:toolbox/data/res/store.dart';
|
import 'package:server_box/data/res/store.dart';
|
||||||
|
|
||||||
const _keyConfig = 'providerConfig';
|
const _keyConfig = 'providerConfig';
|
||||||
|
|
||||||
|
|||||||
@@ -54,4 +54,7 @@ class HistoryStore extends PersistentStore {
|
|||||||
late final sftpLastPath = _MapHistory(box: box, name: 'sftpLastPath');
|
late final sftpLastPath = _MapHistory(box: box, name: 'sftpLastPath');
|
||||||
|
|
||||||
late final sshCmds = _ListHistory(box: box, name: 'sshCmds');
|
late final sshCmds = _ListHistory(box: box, name: 'sshCmds');
|
||||||
|
|
||||||
|
/// Notify users that this app will write script to server to works properly
|
||||||
|
late final writeScriptTipShown = property('writeScriptTipShown', false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:fl_lib/fl_lib.dart';
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
import 'package:toolbox/data/model/app/menu/server_func.dart';
|
import 'package:server_box/data/model/app/menu/server_func.dart';
|
||||||
import 'package:toolbox/data/model/app/server_detail_card.dart';
|
import 'package:server_box/data/model/app/server_detail_card.dart';
|
||||||
import 'package:toolbox/data/model/ssh/virtual_key.dart';
|
import 'package:server_box/data/model/ssh/virtual_key.dart';
|
||||||
|
|
||||||
import '../model/app/net_view.dart';
|
import '../model/app/net_view.dart';
|
||||||
import '../res/default.dart';
|
import '../res/default.dart';
|
||||||
@@ -16,28 +16,16 @@ class SettingStore extends PersistentStore {
|
|||||||
// item in the drawer of the home page)
|
// item in the drawer of the home page)
|
||||||
|
|
||||||
/// Discussion #146
|
/// Discussion #146
|
||||||
late final serverTabUseOldUI = property(
|
late final serverTabUseOldUI = property('serverTabUseOldUI', false);
|
||||||
'serverTabUseOldUI',
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Time out for server connect and more...
|
/// Time out for server connect and more...
|
||||||
late final timeout = property(
|
late final timeout = property('timeOut', 5);
|
||||||
'timeOut',
|
|
||||||
5,
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Record history of SFTP path and etc.
|
/// Record history of SFTP path and etc.
|
||||||
late final recordHistory = property(
|
late final recordHistory = property('recordHistory', true);
|
||||||
'recordHistory',
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Lanch page idx
|
/// Lanch page idx
|
||||||
late final launchPage = property(
|
// late final launchPage = property('launchPage', Defaults.launchPageIdx);
|
||||||
'launchPage',
|
|
||||||
Defaults.launchPageIdx,
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Disk view: amount / IO
|
/// Disk view: amount / IO
|
||||||
late final serverTabPreferDiskAmount = property(
|
late final serverTabPreferDiskAmount = property(
|
||||||
@@ -50,15 +38,10 @@ class SettingStore extends PersistentStore {
|
|||||||
/// Bigger for bigger font size
|
/// Bigger for bigger font size
|
||||||
/// 1.0 means 100%
|
/// 1.0 means 100%
|
||||||
/// Warning: This may cause some UI issues
|
/// Warning: This may cause some UI issues
|
||||||
late final textFactor = property(
|
late final textFactor = property('textFactor', 1.0);
|
||||||
'textFactor',
|
|
||||||
1.0,
|
|
||||||
);
|
|
||||||
|
|
||||||
late final primaryColor = property(
|
/// The seed of color scheme
|
||||||
'primaryColor',
|
late final colorSeed = property('primaryColor', 4287106639);
|
||||||
4287106639,
|
|
||||||
);
|
|
||||||
|
|
||||||
late final serverStatusUpdateInterval = property(
|
late final serverStatusUpdateInterval = property(
|
||||||
'serverStatusUpdateInterval',
|
'serverStatusUpdateInterval',
|
||||||
@@ -100,25 +83,14 @@ class SettingStore extends PersistentStore {
|
|||||||
late final editorFontSize = property('editorFontSize', 12.5);
|
late final editorFontSize = property('editorFontSize', 12.5);
|
||||||
|
|
||||||
// Editor theme
|
// Editor theme
|
||||||
late final editorTheme = property(
|
late final editorTheme = property('editorTheme', Defaults.editorTheme);
|
||||||
'editorTheme',
|
|
||||||
Defaults.editorTheme,
|
|
||||||
);
|
|
||||||
|
|
||||||
late final editorDarkTheme = property(
|
late final editorDarkTheme =
|
||||||
'editorDarkTheme',
|
property('editorDarkTheme', Defaults.editorDarkTheme);
|
||||||
Defaults.editorDarkTheme,
|
|
||||||
);
|
|
||||||
|
|
||||||
late final fullScreen = property(
|
late final fullScreen = property('fullScreen', false);
|
||||||
'fullScreen',
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
late final fullScreenJitter = property(
|
late final fullScreenJitter = property('fullScreenJitter', true);
|
||||||
'fullScreenJitter',
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
|
|
||||||
// late final fullScreenRotateQuarter = property(
|
// late final fullScreenRotateQuarter = property(
|
||||||
// 'fullScreenRotateQuarter',
|
// 'fullScreenRotateQuarter',
|
||||||
@@ -135,53 +107,29 @@ class SettingStore extends PersistentStore {
|
|||||||
VirtKey.defaultOrder.map((e) => e.index).toList(),
|
VirtKey.defaultOrder.map((e) => e.index).toList(),
|
||||||
);
|
);
|
||||||
|
|
||||||
late final netViewType = property(
|
late final netViewType = property('netViewType', NetViewType.speed);
|
||||||
'netViewType',
|
|
||||||
NetViewType.speed,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Only valid on iOS
|
// Only valid on iOS
|
||||||
late final autoUpdateHomeWidget = property(
|
late final autoUpdateHomeWidget = property('autoUpdateHomeWidget', isIOS);
|
||||||
'autoUpdateHomeWidget',
|
|
||||||
isIOS,
|
|
||||||
);
|
|
||||||
|
|
||||||
late final autoCheckAppUpdate = property(
|
late final autoCheckAppUpdate = property('autoCheckAppUpdate', true);
|
||||||
'autoCheckAppUpdate',
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Display server tab function buttons on the bottom of each server card if [true]
|
/// Display server tab function buttons on the bottom of each server card if [true]
|
||||||
///
|
///
|
||||||
/// Otherwise, display them on the top of server detail page
|
/// Otherwise, display them on the top of server detail page
|
||||||
late final moveOutServerTabFuncBtns = property(
|
late final moveServerFuncs = property('moveOutServerTabFuncBtns', false);
|
||||||
'moveOutServerTabFuncBtns',
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Whether use `rm -r` to delete directory on SFTP
|
/// Whether use `rm -r` to delete directory on SFTP
|
||||||
late final sftpRmrDir = property(
|
late final sftpRmrDir = property('sftpRmrDir', false);
|
||||||
'sftpRmrDir',
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Whether use system's primary color as the app's primary color
|
/// Whether use system's primary color as the app's primary color
|
||||||
late final useSystemPrimaryColor = property(
|
late final useSystemPrimaryColor = property('useSystemPrimaryColor', false);
|
||||||
'useSystemPrimaryColor',
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Only valid on iOS and macOS
|
/// Only valid on iOS and macOS
|
||||||
late final icloudSync = property(
|
late final icloudSync = property('icloudSync', false);
|
||||||
'icloudSync',
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Only valid on iOS / Android / Windows
|
/// Only valid on iOS / Android / Windows
|
||||||
late final useBioAuth = property(
|
late final useBioAuth = property('useBioAuth', false);
|
||||||
'useBioAuth',
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
/// The performance of highlight is bad
|
/// The performance of highlight is bad
|
||||||
late final editorHighlight = property('editorHighlight', true);
|
late final editorHighlight = property('editorHighlight', true);
|
||||||
@@ -265,8 +213,6 @@ class SettingStore extends PersistentStore {
|
|||||||
|
|
||||||
late final horizonVirtKey = property('horizonVirtKey', false);
|
late final horizonVirtKey = property('horizonVirtKey', false);
|
||||||
|
|
||||||
late final collectUsage = property('collectUsage', true);
|
|
||||||
|
|
||||||
/// general wake lock
|
/// general wake lock
|
||||||
late final generalWakeLock = property('generalWakeLock', false);
|
late final generalWakeLock = property('generalWakeLock', false);
|
||||||
|
|
||||||
@@ -276,6 +222,20 @@ class SettingStore extends PersistentStore {
|
|||||||
/// fmt: https://example.com/{DIST}-{BRIGHT}.png
|
/// fmt: https://example.com/{DIST}-{BRIGHT}.png
|
||||||
late final serverLogoUrl = property('serverLogoUrl', '');
|
late final serverLogoUrl = property('serverLogoUrl', '');
|
||||||
|
|
||||||
|
late final betaTest = property('betaTest', false);
|
||||||
|
|
||||||
|
/// If it's empty, skip change window size.
|
||||||
|
/// Format: {width}x{height}
|
||||||
|
late final windowSize = property('windowSize', '');
|
||||||
|
|
||||||
|
late final introVer = property('introVer', 0);
|
||||||
|
|
||||||
|
late final letterCache = property('letterCache', false);
|
||||||
|
|
||||||
|
/// Set it to `$EDITOR`, `vim` and etc. to use remote system editor in SSH terminal.
|
||||||
|
/// Set it empty to use local editor GUI.
|
||||||
|
late final sftpEditor = property('sftpEditor', '');
|
||||||
|
|
||||||
// Never show these settings for users
|
// Never show these settings for users
|
||||||
//
|
//
|
||||||
// ------BEGIN------
|
// ------BEGIN------
|
||||||
|
|||||||
102
lib/intro.dart
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
part of 'app.dart';
|
||||||
|
|
||||||
|
final class _IntroPage extends StatelessWidget {
|
||||||
|
final List<IntroPageBuilder> pages;
|
||||||
|
|
||||||
|
const _IntroPage(this.pages);
|
||||||
|
|
||||||
|
static const _builders = {
|
||||||
|
1: _buildAppSettings,
|
||||||
|
};
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return LayoutBuilder(
|
||||||
|
builder: (context, cons) {
|
||||||
|
final padTop = cons.maxHeight * .16;
|
||||||
|
final pages_ = pages.map((e) => e(context, padTop)).toList();
|
||||||
|
return IntroPage(
|
||||||
|
args: IntroPageArgs(
|
||||||
|
pages: pages_,
|
||||||
|
onDone: (ctx) {
|
||||||
|
Stores.setting.introVer.put(BuildData.build);
|
||||||
|
Navigator.of(ctx).pushReplacement(
|
||||||
|
MaterialPageRoute(builder: (_) => const HomePage()),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Widget _buildAppSettings(BuildContext ctx, double padTop) {
|
||||||
|
return ListView(
|
||||||
|
padding: _introListPad,
|
||||||
|
children: [
|
||||||
|
SizedBox(height: padTop),
|
||||||
|
IntroPage.title(text: l10n.init, big: true),
|
||||||
|
SizedBox(height: padTop),
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(IonIcons.language),
|
||||||
|
title: Text(libL10n.language),
|
||||||
|
onTap: () async {
|
||||||
|
final selected = await ctx.showPickSingleDialog(
|
||||||
|
title: libL10n.language,
|
||||||
|
items: AppLocalizations.supportedLocales,
|
||||||
|
name: (p0) => p0.nativeName,
|
||||||
|
initial: _setting.locale.fetch().toLocale,
|
||||||
|
);
|
||||||
|
if (selected != null) {
|
||||||
|
_setting.locale.put(selected.code);
|
||||||
|
RNodes.app.notify();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
trailing: Text(
|
||||||
|
ctx.localeNativeName,
|
||||||
|
style: const TextStyle(fontSize: 15, color: Colors.grey),
|
||||||
|
),
|
||||||
|
).cardx,
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(Icons.update),
|
||||||
|
title: Text(libL10n.autoCheckUpdate),
|
||||||
|
subtitle: Text(l10n.fdroidReleaseTip, style: UIs.textGrey),
|
||||||
|
trailing: StoreSwitch(prop: _setting.autoCheckAppUpdate),
|
||||||
|
).cardx,
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(MingCute.delete_2_fill),
|
||||||
|
title: TipText('rm -r', l10n.sftpRmrDirSummary),
|
||||||
|
trailing: StoreSwitch(prop: _setting.sftpRmrDir),
|
||||||
|
).cardx,
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(MingCute.chart_line_line, size: _kIconSize),
|
||||||
|
title: TipText(l10n.stat, l10n.parseContainerStatsTip),
|
||||||
|
trailing: StoreSwitch(prop: _setting.containerParseStat),
|
||||||
|
).cardx,
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(OctIcons.cpu),
|
||||||
|
title: TipText(l10n.noLineChartForCpu, l10n.cpuViewAsProgressTip),
|
||||||
|
trailing: StoreSwitch(prop: _setting.cpuViewAsProgress),
|
||||||
|
).cardx,
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(Bootstrap.alphabet),
|
||||||
|
title: TipText(l10n.letterCache, l10n.letterCacheTip),
|
||||||
|
trailing: StoreSwitch(prop: _setting.letterCache),
|
||||||
|
).cardx,
|
||||||
|
UIs.height77,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<IntroPageBuilder> get builders {
|
||||||
|
final storedVer = _setting.introVer.fetch();
|
||||||
|
return _builders.entries
|
||||||
|
.where((e) => e.key > storedVer)
|
||||||
|
.map((e) => e.value)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
static final _setting = Stores.setting;
|
||||||
|
static const _kIconSize = 23.0;
|
||||||
|
static const _introListPad = EdgeInsets.symmetric(horizontal: 17);
|
||||||
|
}
|
||||||
@@ -1,86 +1,42 @@
|
|||||||
{
|
{
|
||||||
"@@locale": "de",
|
"@@locale": "de",
|
||||||
"about": "Über",
|
|
||||||
"aboutThanks": "Vielen Dank an die folgenden Personen, die daran teilgenommen haben.\n",
|
"aboutThanks": "Vielen Dank an die folgenden Personen, die daran teilgenommen haben.\n",
|
||||||
"add": "Neu",
|
"acceptBeta": "Akzeptieren Sie Testversion-Updates",
|
||||||
"addAServer": "Server hinzufügen",
|
|
||||||
"addPrivateKey": "Private key hinzufügen",
|
|
||||||
"addSystemPrivateKeyTip": "Derzeit haben Sie keinen privaten Schlüssel, fügen Sie den Schlüssel hinzu, der mit dem System geliefert wird (~/.ssh/id_rsa)?",
|
"addSystemPrivateKeyTip": "Derzeit haben Sie keinen privaten Schlüssel, fügen Sie den Schlüssel hinzu, der mit dem System geliefert wird (~/.ssh/id_rsa)?",
|
||||||
"added2List": "Zur Aufgabenliste hinzugefügt",
|
"added2List": "Zur Aufgabenliste hinzugefügt",
|
||||||
"addr": "Adresse",
|
"addr": "Adresse",
|
||||||
"all": "Alle",
|
|
||||||
"alreadyLastDir": "Bereits im letzten Verzeichnis.",
|
"alreadyLastDir": "Bereits im letzten Verzeichnis.",
|
||||||
"alterUrl": "Url ändern",
|
|
||||||
"askContinue": "{msg}. Weiter?",
|
|
||||||
"attention": "Achtung",
|
|
||||||
"authFailTip": "Authentifizierung fehlgeschlagen, bitte überprüfen Sie, ob das Passwort/Schlüssel/Host/Benutzer usw. falsch sind.",
|
"authFailTip": "Authentifizierung fehlgeschlagen, bitte überprüfen Sie, ob das Passwort/Schlüssel/Host/Benutzer usw. falsch sind.",
|
||||||
"authRequired": "Autorisierung erforderlich",
|
|
||||||
"auto": "System folgen",
|
|
||||||
"autoBackupConflict": "Es kann nur eine automatische Sicherung gleichzeitig aktiviert werden.",
|
"autoBackupConflict": "Es kann nur eine automatische Sicherung gleichzeitig aktiviert werden.",
|
||||||
"autoCheckUpdate": "Aktualisierung automatisch prüfen",
|
|
||||||
"autoConnect": "Automatisch verbinden",
|
"autoConnect": "Automatisch verbinden",
|
||||||
"autoRun": "Automatischer Start",
|
"autoRun": "Automatischer Start",
|
||||||
"autoUpdateHomeWidget": "Home-Widget automatisch aktualisieren",
|
"autoUpdateHomeWidget": "Home-Widget automatisch aktualisieren",
|
||||||
"backup": "Backup",
|
|
||||||
"backupTip": "Das Backup wird nur einfach verschlüsselt.\nBitte bewahre die Datei sicher auf.",
|
"backupTip": "Das Backup wird nur einfach verschlüsselt.\nBitte bewahre die Datei sicher auf.",
|
||||||
"backupVersionNotMatch": "Die Backup-Version stimmt nicht überein.",
|
"backupVersionNotMatch": "Die Backup-Version stimmt nicht überein.",
|
||||||
"battery": "Batterie",
|
"battery": "Batterie",
|
||||||
"bgRun": "Hintergrundaktualisierung",
|
"bgRun": "Hintergrundaktualisierung",
|
||||||
"bgRunTip": "Dieser Schalter bedeutet nur, dass die App versuchen wird, im Hintergrund zu laufen. Ob sie im Hintergrund laufen kann, hängt davon ab, ob die Berechtigungen aktiviert sind oder nicht. Bei nativem Android deaktivieren Sie bitte \"Batterieoptimierung\" in dieser App, und bei miui ändern Sie bitte die Energiesparrichtlinie auf \"Unbegrenzt\".",
|
"bgRunTip": "Dieser Schalter bedeutet nur, dass die App versuchen wird, im Hintergrund zu laufen. Ob sie im Hintergrund laufen kann, hängt davon ab, ob die Berechtigungen aktiviert sind oder nicht. Bei nativem Android deaktivieren Sie bitte \"Batterieoptimierung\" in dieser App, und bei miui ändern Sie bitte die Energiesparrichtlinie auf \"Unbegrenzt\".",
|
||||||
"bioAuth": "Biozertifizierung",
|
|
||||||
"browser": "Browser",
|
|
||||||
"bulkImportServers": "Server im Batch importieren",
|
|
||||||
"bulkImportServersTip": "Sie können das [Format]({url}) hier finden.",
|
|
||||||
"canPullRefresh": "Danach: herunterziehen zum Aktualisieren",
|
|
||||||
"cancel": "Abbrechen",
|
|
||||||
"choose": "Auswählen",
|
|
||||||
"chooseFontFile": "Schriftart auswählen",
|
|
||||||
"choosePrivateKey": "Private key auswählen",
|
|
||||||
"clear": "Entfernen",
|
|
||||||
"clipboard": "Zwischenablage",
|
|
||||||
"close": "Schließen",
|
|
||||||
"cmd": "Command",
|
"cmd": "Command",
|
||||||
"cnKeyboardComp": "Kompatibilität mit chinesischem Android",
|
|
||||||
"cnKeyboardCompTip": "Wenn das Terminal ein sicheres Tastenfeld öffnet, können Sie es aktivieren.",
|
|
||||||
"collapseUI": "Zusammenbrechen",
|
|
||||||
"collapseUITip": "Ob lange Listen in der Benutzeroberfläche standardmäßig eingeklappt werden sollen oder nicht",
|
"collapseUITip": "Ob lange Listen in der Benutzeroberfläche standardmäßig eingeklappt werden sollen oder nicht",
|
||||||
"collectUsage": "Nutzungsinformationen sammeln (unabhängig von der Privatsphäre).",
|
|
||||||
"conn": "Verbindung",
|
"conn": "Verbindung",
|
||||||
"connected": "in Verbindung gebracht",
|
|
||||||
"container": "Container",
|
"container": "Container",
|
||||||
"containerName": "Container Name",
|
|
||||||
"containerStatus": "Container Status",
|
|
||||||
"containerTrySudoTip": "Zum Beispiel: In der App ist der Benutzer auf aaa eingestellt, aber Docker ist unter dem Root-Benutzer installiert. In diesem Fall müssen Sie diese Option aktivieren",
|
"containerTrySudoTip": "Zum Beispiel: In der App ist der Benutzer auf aaa eingestellt, aber Docker ist unter dem Root-Benutzer installiert. In diesem Fall müssen Sie diese Option aktivieren",
|
||||||
"content": "Inhalt",
|
|
||||||
"convert": "Konvertieren",
|
"convert": "Konvertieren",
|
||||||
"copy": "Kopieren",
|
|
||||||
"copyPath": "Pfad kopieren",
|
"copyPath": "Pfad kopieren",
|
||||||
"cpuViewAsProgressTip": "Zeigen Sie die Auslastung jedes CPUs in einem Fortschrittsbalken-Stil an (alter Stil)",
|
"cpuViewAsProgressTip": "Zeigen Sie die Auslastung jedes CPUs in einem Fortschrittsbalken-Stil an (alter Stil)",
|
||||||
"createFile": "Datei erstellen",
|
|
||||||
"createFolder": "Ordner erstellen",
|
|
||||||
"cursorType": "Cursor-Typ",
|
"cursorType": "Cursor-Typ",
|
||||||
"customCmd": "Benutzerdefinierte Befehle",
|
"customCmd": "Benutzerdefinierte Befehle",
|
||||||
"customCmdDocUrl": "https://github.com/lollipopkit/flutter_server_box/wiki#custom-commands",
|
"customCmdDocUrl": "https://github.com/lollipopkit/flutter_server_box/wiki#custom-commands",
|
||||||
"customCmdHint": "\"Befehlsname\": \"Befehl\"",
|
"customCmdHint": "\"Befehlsname\": \"Befehl\"",
|
||||||
"dark": "Dunkel",
|
|
||||||
"day": "Tag",
|
|
||||||
"debug": "Debug",
|
|
||||||
"decode": "Decode",
|
"decode": "Decode",
|
||||||
"decompress": "Dekomprimieren",
|
"decompress": "Dekomprimieren",
|
||||||
"delete": "Löschen",
|
|
||||||
"deleteScripts": "Gleichzeitiges Löschen von Server-Skripten",
|
|
||||||
"deleteServers": "Batch-Löschung von Servern",
|
"deleteServers": "Batch-Löschung von Servern",
|
||||||
"deviceName": "Gerätename",
|
|
||||||
"dirEmpty": "Stelle sicher, dass der Ordner leer ist.",
|
"dirEmpty": "Stelle sicher, dass der Ordner leer ist.",
|
||||||
"disabled": "Behinderte",
|
|
||||||
"disconnected": "Disconnected",
|
"disconnected": "Disconnected",
|
||||||
"disk": "Festplatte",
|
"disk": "Festplatte",
|
||||||
"diskIgnorePath": "Pfad für Datenträger ignorieren",
|
"diskIgnorePath": "Pfad für Datenträger ignorieren",
|
||||||
"displayCpuIndex": "Zeigen Sie den CPU-Index an",
|
"displayCpuIndex": "Zeigen Sie den CPU-Index an",
|
||||||
"displayName": "Name anzeigen",
|
|
||||||
"dl2Local": "Datei \"{fileName}\" herunterladen?",
|
"dl2Local": "Datei \"{fileName}\" herunterladen?",
|
||||||
"doc": "Dokumentation",
|
|
||||||
"dockerEditHost": "DOCKER_HOST bearbeiten",
|
|
||||||
"dockerEmptyRunningItems": "Es gibt keine laufenden Container.\nDas könnte daran liegen:\n- Der Docker-Installationsbenutzer ist nicht mit dem in der App konfigurierten Benutzernamen identisch.\n- Die Umgebungsvariable DOCKER_HOST wurde nicht korrekt gelesen. Sie können sie ermitteln, indem Sie `echo $DOCKER_HOST` im Terminal ausführen.",
|
"dockerEmptyRunningItems": "Es gibt keine laufenden Container.\nDas könnte daran liegen:\n- Der Docker-Installationsbenutzer ist nicht mit dem in der App konfigurierten Benutzernamen identisch.\n- Die Umgebungsvariable DOCKER_HOST wurde nicht korrekt gelesen. Sie können sie ermitteln, indem Sie `echo $DOCKER_HOST` im Terminal ausführen.",
|
||||||
"dockerImagesFmt": "{count} Image(s)",
|
"dockerImagesFmt": "{count} Image(s)",
|
||||||
"dockerNotInstalled": "Docker ist nicht installiert",
|
"dockerNotInstalled": "Docker ist nicht installiert",
|
||||||
@@ -88,36 +44,24 @@
|
|||||||
"dockerStatusRunningFmt": "{count} Container aktiv",
|
"dockerStatusRunningFmt": "{count} Container aktiv",
|
||||||
"doubleColumnMode": "Doppelspaltiger Modus",
|
"doubleColumnMode": "Doppelspaltiger Modus",
|
||||||
"doubleColumnTip": "Diese Option aktiviert nur die Funktion, ob sie tatsächlich aktiviert werden kann, hängt auch von der Breite des Geräts ab",
|
"doubleColumnTip": "Diese Option aktiviert nur die Funktion, ob sie tatsächlich aktiviert werden kann, hängt auch von der Breite des Geräts ab",
|
||||||
"download": "Download",
|
|
||||||
"edit": "Bearbeiten",
|
|
||||||
"editVirtKeys": "Virtuelle Tasten bearbeiten",
|
"editVirtKeys": "Virtuelle Tasten bearbeiten",
|
||||||
"editor": "Editor",
|
"editor": "Editor",
|
||||||
"editorHighlightTip": "Die Leistung der aktuellen Codehervorhebung ist schlechter und kann zur Verbesserung optional ausgeschaltet werden.",
|
"editorHighlightTip": "Die Leistung der aktuellen Codehervorhebung ist schlechter und kann zur Verbesserung optional ausgeschaltet werden.",
|
||||||
"encode": "Encode",
|
"encode": "Encode",
|
||||||
"error": "Fehler",
|
"envVars": "Umgebungsvariable",
|
||||||
"exampleName": "Servername",
|
|
||||||
"experimentalFeature": "Experimentelles Feature",
|
"experimentalFeature": "Experimentelles Feature",
|
||||||
"export": "Export",
|
|
||||||
"extraArgs": "Extra args",
|
"extraArgs": "Extra args",
|
||||||
"failed": "Failed",
|
"fallbackSshDest": "SSH-Fallback-Ziel",
|
||||||
"feedback": "Feedback",
|
"fdroidReleaseTip": "Wenn Sie diese App von F-Droid heruntergeladen haben, wird empfohlen, diese Option zu deaktivieren.",
|
||||||
"feedbackOnGithub": "Wenn du Fragen hast, stelle diese bitte auf Github.",
|
|
||||||
"fieldMustNotEmpty": "Die Eingabefelder dürfen nicht leer sein.",
|
|
||||||
"fileNotExist": "{file} existiert nicht",
|
|
||||||
"fileTooLarge": "Datei '{file}' ist zu groß {size}, max {sizeMax}",
|
"fileTooLarge": "Datei '{file}' ist zu groß {size}, max {sizeMax}",
|
||||||
"files": "Dateien",
|
|
||||||
"finished": "fertiggestellt",
|
|
||||||
"followSystem": "System verfolgen",
|
"followSystem": "System verfolgen",
|
||||||
"font": "Schriftarten",
|
"font": "Schriftarten",
|
||||||
"fontSize": "Schriftgröße",
|
"fontSize": "Schriftgröße",
|
||||||
"force": "freiwillig",
|
"force": "freiwillig",
|
||||||
"foundNUpdate": "Update {count} gefunden",
|
|
||||||
"fullScreen": "Vollbildmodus",
|
"fullScreen": "Vollbildmodus",
|
||||||
"fullScreenJitter": "Jitter im Vollbildmodus",
|
"fullScreenJitter": "Jitter im Vollbildmodus",
|
||||||
"fullScreenJitterHelp": "Einbrennen des Bildschirms verhindern",
|
"fullScreenJitterHelp": "Einbrennen des Bildschirms verhindern",
|
||||||
"fullScreenTip": "Soll der Vollbildmodus aktiviert werden, wenn das Gerät in den Quermodus gedreht wird? Diese Option gilt nur für die Server-Registerkarte.",
|
"fullScreenTip": "Soll der Vollbildmodus aktiviert werden, wenn das Gerät in den Quermodus gedreht wird? Diese Option gilt nur für die Server-Registerkarte.",
|
||||||
"getPushTokenFailed": "Push-Token kann nicht abgerufen werden",
|
|
||||||
"gettingToken": "Getting token...",
|
|
||||||
"goBackQ": "Zurückkommen?",
|
"goBackQ": "Zurückkommen?",
|
||||||
"goto": "Pfad öffnen",
|
"goto": "Pfad öffnen",
|
||||||
"hideTitleBar": "Titelleiste ausblenden",
|
"hideTitleBar": "Titelleiste ausblenden",
|
||||||
@@ -125,37 +69,24 @@
|
|||||||
"highlight": "Code highlight",
|
"highlight": "Code highlight",
|
||||||
"homeWidgetUrlConfig": "Home-Widget-Link konfigurieren",
|
"homeWidgetUrlConfig": "Home-Widget-Link konfigurieren",
|
||||||
"host": "Host",
|
"host": "Host",
|
||||||
"hour": "Stunde",
|
|
||||||
"httpFailedWithCode": "Anfrage fehlgeschlagen, Statuscode: {code}",
|
"httpFailedWithCode": "Anfrage fehlgeschlagen, Statuscode: {code}",
|
||||||
"icloudSynced": "iCloud wird synchronisiert und einige Einstellungen erfordern möglicherweise einen Neustart der App, um wirksam zu werden.",
|
|
||||||
"ignoreCert": "Zertifikat ignorieren",
|
"ignoreCert": "Zertifikat ignorieren",
|
||||||
"image": "Image",
|
"image": "Image",
|
||||||
"imagesList": "Images",
|
"imagesList": "Images",
|
||||||
"import": "Importieren",
|
"init": "Initialisieren",
|
||||||
"inAppUpdate": "Im App aktualisieren? Andernfalls mit einem Browser herunterladen.",
|
|
||||||
"inner": "Eingebaut",
|
"inner": "Eingebaut",
|
||||||
"inputDomainHere": "Domain eingeben",
|
|
||||||
"install": "install",
|
"install": "install",
|
||||||
"installDockerWithUrl": "Bitte installiere docker zuerst. https://docs.docker.com/engine/install",
|
"installDockerWithUrl": "Bitte installiere docker zuerst. https://docs.docker.com/engine/install",
|
||||||
"invalid": "Ungültig",
|
"invalid": "Ungültig",
|
||||||
"invalidJson": "Ungültige JSON",
|
|
||||||
"invalidVersion": "Ungültige Version",
|
|
||||||
"invalidVersionHelp": "Bitte stelle sicher, dass Docker korrekt installiert ist oder dass du eine nicht selbstkompilierte Version verwendest. Wenn du die oben genannten Probleme nicht hast, melde bitte einen Fehler auf {url}.",
|
|
||||||
"isBusy": "Is busy now",
|
|
||||||
"jumpServer": "Server springen",
|
"jumpServer": "Server springen",
|
||||||
"keepForeground": "Stelle sicher, dass die App geöffnet bleibt.",
|
"keepForeground": "Stelle sicher, dass die App geöffnet bleibt.",
|
||||||
"keepStatusWhenErr": "Den letzten Serverstatus beibehalten",
|
"keepStatusWhenErr": "Den letzten Serverstatus beibehalten",
|
||||||
"keepStatusWhenErrTip": "Nur im Fehlerfall während der Ausführung des Skripts",
|
"keepStatusWhenErrTip": "Nur im Fehlerfall während der Ausführung des Skripts",
|
||||||
"keyAuth": "Schlüsselauthentifzierung",
|
"keyAuth": "Schlüsselauthentifzierung",
|
||||||
"language": "Sprache",
|
"letterCache": "Buchstaben-Caching",
|
||||||
"languageName": "Deutsch",
|
"letterCacheTip": "Empfohlen, zu deaktivieren, aber nach dem Deaktivieren können keine CJK-Zeichen eingegeben werden.",
|
||||||
"lastTry": "Letzter Versuch",
|
|
||||||
"launchPage": "Startseite",
|
|
||||||
"license": "Lizenzen",
|
"license": "Lizenzen",
|
||||||
"light": "Hell",
|
|
||||||
"loadingFiles": "Lädt Dateien...",
|
|
||||||
"location": "Standort",
|
"location": "Standort",
|
||||||
"log": "Log",
|
|
||||||
"loss": "loss",
|
"loss": "loss",
|
||||||
"madeWithLove": "Erstellt mit ❤️ von {myGithub}",
|
"madeWithLove": "Erstellt mit ❤️ von {myGithub}",
|
||||||
"manual": "Handbuch",
|
"manual": "Handbuch",
|
||||||
@@ -163,60 +94,36 @@
|
|||||||
"maxRetryCount": "Anzahl an Verbindungsversuchen",
|
"maxRetryCount": "Anzahl an Verbindungsversuchen",
|
||||||
"maxRetryCountEqual0": "Unbegrenzte Verbindungsversuche zum Server",
|
"maxRetryCountEqual0": "Unbegrenzte Verbindungsversuche zum Server",
|
||||||
"min": "min",
|
"min": "min",
|
||||||
"minute": "Minute",
|
|
||||||
"mission": "Mission",
|
"mission": "Mission",
|
||||||
"more": "Mehr",
|
"more": "Mehr",
|
||||||
"moveOutServerFuncBtnsHelp": "Ein: kann unter jeder Karte auf der Registerkarte \"Server\" angezeigt werden. Aus: kann oben auf der Seite \"Serverdetails\" angezeigt werden.",
|
"moveOutServerFuncBtnsHelp": "Ein: kann unter jeder Karte auf der Registerkarte \"Server\" angezeigt werden. Aus: kann oben auf der Seite \"Serverdetails\" angezeigt werden.",
|
||||||
"ms": "ms",
|
"ms": "ms",
|
||||||
"name": "Name",
|
|
||||||
"needHomeDir": "Wenn Sie ein Synology-Benutzer sind, [sehen Sie hier](https://kb.synology.com/DSM/tutorial/user_enable_home_service). Benutzer anderer Systeme müssen suchen, wie man ein Home-Verzeichnis erstellt.",
|
"needHomeDir": "Wenn Sie ein Synology-Benutzer sind, [sehen Sie hier](https://kb.synology.com/DSM/tutorial/user_enable_home_service). Benutzer anderer Systeme müssen suchen, wie man ein Home-Verzeichnis erstellt.",
|
||||||
"needRestart": "App muss neugestartet werden",
|
"needRestart": "App muss neugestartet werden",
|
||||||
"net": "Netz",
|
"net": "Netzwerk",
|
||||||
"netViewType": "Netzwerkansicht Typ",
|
"netViewType": "Netzwerkansicht Typ",
|
||||||
"newContainer": "Neuer Container",
|
"newContainer": "Neuer Container",
|
||||||
"noClient": "Kein Client",
|
|
||||||
"noInterface": "Kein Interface",
|
|
||||||
"noLineChart": "Verwenden Sie keine Liniendiagramme",
|
"noLineChart": "Verwenden Sie keine Liniendiagramme",
|
||||||
"noNotiPerm": "Keine Benachrichtigungsrechte, möglicherweise keine Fortschrittsanzeige beim Herunterladen von App-Updates.",
|
"noLineChartForCpu": "Verwenden Sie keine Liniendiagramme für CPU",
|
||||||
"noOptions": "Keine Optionen verfügbar",
|
|
||||||
"noPrivateKeyTip": "Der private Schlüssel existiert nicht, möglicherweise wurde er gelöscht oder es liegt ein Konfigurationsfehler vor.",
|
"noPrivateKeyTip": "Der private Schlüssel existiert nicht, möglicherweise wurde er gelöscht oder es liegt ein Konfigurationsfehler vor.",
|
||||||
"noPromptAgain": "Nicht mehr nachfragen",
|
"noPromptAgain": "Nicht mehr nachfragen",
|
||||||
"noResult": "Kein Ergebnis",
|
|
||||||
"noSavedPrivateKey": "Keine gespeicherten Private Keys",
|
|
||||||
"noSavedSnippet": "Keine gespeicherten Snippets.",
|
|
||||||
"noServerAvailable": "Kein Server verfügbar.",
|
|
||||||
"noTask": "Nicht fragen",
|
|
||||||
"noUpdateAvailable": "Kein Update verfügbar",
|
|
||||||
"node": "Knoten",
|
"node": "Knoten",
|
||||||
"notAvailable": "Nicht verfügbar",
|
"notAvailable": "Nicht verfügbar",
|
||||||
"notSelected": "Nicht ausgewählt",
|
|
||||||
"note": "Hinweis",
|
|
||||||
"nullToken": "Null token",
|
|
||||||
"ok": "OK",
|
|
||||||
"onServerDetailPage": "in Detailansicht des Servers",
|
"onServerDetailPage": "in Detailansicht des Servers",
|
||||||
"onlyOneLine": "Nur als eine Zeile anzeigen (scrollbar)",
|
"onlyOneLine": "Nur als eine Zeile anzeigen (scrollbar)",
|
||||||
"onlyWhenCoreBiggerThan8": "Wirksam nur, wenn die Anzahl der Kerne > 8 ist.",
|
"onlyWhenCoreBiggerThan8": "Wirksam nur, wenn die Anzahl der Kerne > 8 ist.",
|
||||||
"open": "Öffnen",
|
|
||||||
"openLastPath": "Öffnen Sie den letzten Pfad",
|
"openLastPath": "Öffnen Sie den letzten Pfad",
|
||||||
"openLastPathTip": "Verschiedene Server haben unterschiedliche Einträge, und der Eintrag ist der Pfad zum Ausgang",
|
"openLastPathTip": "Verschiedene Server haben unterschiedliche Einträge, und der Eintrag ist der Pfad zum Ausgang",
|
||||||
"parseContainerStats": "Den Status der Container-Belegung analysieren",
|
|
||||||
"parseContainerStatsTip": "Das Analysieren des Belegungsstatus durch Docker ist relativ langsam",
|
"parseContainerStatsTip": "Das Analysieren des Belegungsstatus durch Docker ist relativ langsam",
|
||||||
"paste": "Einfügen",
|
|
||||||
"path": "Pfad",
|
|
||||||
"percentOfSize": "{percent}% von {size}",
|
"percentOfSize": "{percent}% von {size}",
|
||||||
"pickFile": "Datei wählen",
|
"permission": "Berechtigungen",
|
||||||
"pingAvg": "Avg:",
|
"pingAvg": "Avg:",
|
||||||
"pingInputIP": "Bitte gib eine Ziel-IP/Domain ein.",
|
"pingInputIP": "Bitte gib eine Ziel-IP/Domain ein.",
|
||||||
"pingNoServer": "Kein Server zum Anpingen.\nBitte füge einen Server hinzu.",
|
"pingNoServer": "Kein Server zum Anpingen.\nBitte füge einen Server hinzu.",
|
||||||
"pkg": "Pkg",
|
"pkg": "Pkg",
|
||||||
"pkgUpgradeTip": "Bitte sichern Sie Ihr System vor dem Update.",
|
|
||||||
"platformNotSupportUpdate": "Die aktuelle Plattform unterstützt keine In-App-Updates.\nBitte kompiliere vom Quellcode und installiere sie.",
|
|
||||||
"plugInType": "Einfügetyp",
|
"plugInType": "Einfügetyp",
|
||||||
"plzEnterHost": "Bitte Host eingeben.",
|
|
||||||
"plzSelectKey": "Wähle einen Key.",
|
|
||||||
"port": "Port",
|
"port": "Port",
|
||||||
"preview": "Vorschau",
|
"preview": "Vorschau",
|
||||||
"primaryColorSeed": "Farbschema",
|
|
||||||
"privateKey": "Private Key",
|
"privateKey": "Private Key",
|
||||||
"process": "Prozess",
|
"process": "Prozess",
|
||||||
"pushToken": "Push Token",
|
"pushToken": "Push Token",
|
||||||
@@ -226,15 +133,11 @@
|
|||||||
"pwd": "Passwort",
|
"pwd": "Passwort",
|
||||||
"read": "Lesen",
|
"read": "Lesen",
|
||||||
"reboot": "Neustart",
|
"reboot": "Neustart",
|
||||||
"rememberChoice": "Auswahl merken",
|
|
||||||
"rememberPwdInMem": "Passwort im Speicher behalten",
|
"rememberPwdInMem": "Passwort im Speicher behalten",
|
||||||
"rememberPwdInMemTip": "Für Container, Aufhängen usw.",
|
"rememberPwdInMemTip": "Für Container, Aufhängen usw.",
|
||||||
|
"rememberWindowSize": "Fenstergröße merken",
|
||||||
"remotePath": "Entfernte Pfade",
|
"remotePath": "Entfernte Pfade",
|
||||||
"rename": "Umbenennen",
|
|
||||||
"reportBugsOnGithubIssue": "Bitte Bugs auf {url} melden",
|
|
||||||
"restart": "Neustart",
|
"restart": "Neustart",
|
||||||
"restore": "Wiederherstellen",
|
|
||||||
"restoreSuccess": "Wiederherstellung erfolgreich. App neustarten um Änderungen anzuwenden.",
|
|
||||||
"result": "Result",
|
"result": "Result",
|
||||||
"rotateAngel": "Rotationswinkel",
|
"rotateAngel": "Rotationswinkel",
|
||||||
"route": "Routen",
|
"route": "Routen",
|
||||||
@@ -249,14 +152,8 @@
|
|||||||
"serverDetailOrder": "Reihenfolge der Widgets auf der Detailseite",
|
"serverDetailOrder": "Reihenfolge der Widgets auf der Detailseite",
|
||||||
"serverFuncBtns": "Server-Funktionsschaltflächen",
|
"serverFuncBtns": "Server-Funktionsschaltflächen",
|
||||||
"serverOrder": "Server-Bestellung",
|
"serverOrder": "Server-Bestellung",
|
||||||
"serverTabConnecting": "Verbinden...",
|
|
||||||
"serverTabEmpty": "Keine Server vorhanden.",
|
|
||||||
"serverTabFailed": "Fehlgeschlagen",
|
|
||||||
"serverTabLoading": "Lädt...",
|
|
||||||
"serverTabPlzSave": "Bitte 'speichere' diesen privaten Schlüssel erneut.",
|
|
||||||
"serverTabUnkown": "Unbekannter Status",
|
|
||||||
"setting": "Einstellungen",
|
|
||||||
"sftpDlPrepare": "Verbindung vorbereiten...",
|
"sftpDlPrepare": "Verbindung vorbereiten...",
|
||||||
|
"sftpEditorTip": "Wenn leer, verwenden Sie den im App integrierten Dateieditor. Wenn ein Wert vorhanden ist, wird der Editor des Remote-Servers verwendet, z.B. `vim` (es wird empfohlen, automatisch gemäß `EDITOR` zu ermitteln).",
|
||||||
"sftpRmrDirSummary": "Verwenden Sie \"rm -r\", um das Verzeichnis in SFTP zu löschen.",
|
"sftpRmrDirSummary": "Verwenden Sie \"rm -r\", um das Verzeichnis in SFTP zu löschen.",
|
||||||
"sftpSSHConnected": "SFTP Verbunden",
|
"sftpSSHConnected": "SFTP Verbunden",
|
||||||
"sftpShowFoldersFirst": "Ordner zuerst anzeigen",
|
"sftpShowFoldersFirst": "Ordner zuerst anzeigen",
|
||||||
@@ -271,11 +168,11 @@
|
|||||||
"sshTip": "Diese Funktion befindet sich jetzt in der Experimentierphase.\n\nBitte melde Bugs auf {url} oder mach mit bei der Entwicklung.",
|
"sshTip": "Diese Funktion befindet sich jetzt in der Experimentierphase.\n\nBitte melde Bugs auf {url} oder mach mit bei der Entwicklung.",
|
||||||
"sshVirtualKeyAutoOff": "Automatische Umschaltung der virtuellen Tasten",
|
"sshVirtualKeyAutoOff": "Automatische Umschaltung der virtuellen Tasten",
|
||||||
"start": "Start",
|
"start": "Start",
|
||||||
|
"stat": "Statistik",
|
||||||
"stats": "Statistik",
|
"stats": "Statistik",
|
||||||
"stop": "Stop",
|
"stop": "Stop",
|
||||||
"stopped": "Ausgelaufen",
|
"stopped": "Ausgelaufen",
|
||||||
"storage": "Speicher",
|
"storage": "Speicher",
|
||||||
"success": "Erfolgreich",
|
|
||||||
"supportFmtArgs": "Die folgenden Formatierungsparameter werden unterstützt:",
|
"supportFmtArgs": "Die folgenden Formatierungsparameter werden unterstützt:",
|
||||||
"suspend": "Suspend",
|
"suspend": "Suspend",
|
||||||
"suspendTip": "Die Suspend-Funktion erfordert Root-Rechte und systemd-Unterstützung.",
|
"suspendTip": "Die Suspend-Funktion erfordert Root-Rechte und systemd-Unterstützung.",
|
||||||
@@ -291,35 +188,25 @@
|
|||||||
"textScaler": "Skalierung der Schriftart",
|
"textScaler": "Skalierung der Schriftart",
|
||||||
"textScalerTip": "1.0 => 100% (Originalgröße), funktioniert nur auf der Serverseite Teil der Schrift, nicht empfohlen zu ändern.",
|
"textScalerTip": "1.0 => 100% (Originalgröße), funktioniert nur auf der Serverseite Teil der Schrift, nicht empfohlen zu ändern.",
|
||||||
"theme": "Themen",
|
"theme": "Themen",
|
||||||
"themeMode": "Themen-Modus",
|
|
||||||
"time": "Zeit",
|
"time": "Zeit",
|
||||||
"times": "x",
|
"times": "x",
|
||||||
"total": "Total",
|
"total": "Total",
|
||||||
"traffic": "Durchflussmenge",
|
"traffic": "Durchflussmenge",
|
||||||
"trySudo": "Versuche es mit sudo",
|
"trySudo": "Versuche es mit sudo",
|
||||||
"ttl": "ttl",
|
"ttl": "TTL",
|
||||||
"unknown": "Unbekannt",
|
"unknown": "Unbekannt",
|
||||||
"unknownError": "Unbekannter Fehler",
|
|
||||||
"unkownConvertMode": "Unbekannter Konvertierungsmodus",
|
"unkownConvertMode": "Unbekannter Konvertierungsmodus",
|
||||||
"update": "Update",
|
"update": "Update",
|
||||||
"updateAll": "Alle aktualisieren",
|
|
||||||
"updateIntervalEqual0": "Wenn du den Wert 0 einstellst, wird nicht automatisch aktualisiert.\nDer CPU-Status kann nicht berechnet werden.",
|
"updateIntervalEqual0": "Wenn du den Wert 0 einstellst, wird nicht automatisch aktualisiert.\nDer CPU-Status kann nicht berechnet werden.",
|
||||||
"updateServerStatusInterval": "Aktualisierungsintervall des Serverstatus",
|
"updateServerStatusInterval": "Aktualisierungsintervall des Serverstatus",
|
||||||
"updateTip": "Update: v1.0.{newest}",
|
|
||||||
"updateTipTooLow": "Aktuelle Version ist zu alt, bitte update auf v1.0.{newest}",
|
|
||||||
"upload": "Hochladen",
|
"upload": "Hochladen",
|
||||||
"upsideDown": "Upside Down",
|
"upsideDown": "Upside Down",
|
||||||
"uptime": "Betriebszeit",
|
"uptime": "Betriebszeit",
|
||||||
"urlOrJson": "URL oder JSON",
|
|
||||||
"useCdn": "Verwenden von CDN",
|
"useCdn": "Verwenden von CDN",
|
||||||
"useCdnTip": "Nicht-chinesischen Benutzern wird die Verwendung eines CDN empfohlen. Möchten Sie es verwenden?",
|
"useCdnTip": "Nicht-chinesischen Benutzern wird die Verwendung eines CDN empfohlen. Möchten Sie es verwenden?",
|
||||||
"useNoPwd": "Es wird kein Passwort verwendet",
|
"useNoPwd": "Es wird kein Passwort verwendet",
|
||||||
"usePodmanByDefault": "Standardmäßige Verwendung von Podman",
|
"usePodmanByDefault": "Standardmäßige Verwendung von Podman",
|
||||||
"used": "Gebraucht",
|
"used": "Gebraucht",
|
||||||
"user": "Benutzer",
|
|
||||||
"versionHaveUpdate": "Gefunden: v1.0.{build}, klicke zum Aktualisieren",
|
|
||||||
"versionUnknownUpdate": "Aktuell: v1.0.{build}. Klicken Sie hier, um nach Updates zu suchen",
|
|
||||||
"versionUpdated": "v1.0.{build} ist bereits die neueste Version",
|
|
||||||
"view": "Ansicht",
|
"view": "Ansicht",
|
||||||
"viewErr": "Fehler anzeigen",
|
"viewErr": "Fehler anzeigen",
|
||||||
"virtKeyHelpClipboard": "In die Zwischenablage kopieren, wenn das ausgewählte Terminal nicht leer ist, andernfalls den Inhalt der Zwischenablage in das Terminal einfügen.",
|
"virtKeyHelpClipboard": "In die Zwischenablage kopieren, wenn das ausgewählte Terminal nicht leer ist, andernfalls den Inhalt der Zwischenablage in das Terminal einfügen.",
|
||||||
@@ -330,8 +217,8 @@
|
|||||||
"watchNotPaired": "Keine gekoppelte Apple Watch",
|
"watchNotPaired": "Keine gekoppelte Apple Watch",
|
||||||
"webdavSettingEmpty": "Webdav-Einstellungen sind leer",
|
"webdavSettingEmpty": "Webdav-Einstellungen sind leer",
|
||||||
"whenOpenApp": "Beim Öffnen der App",
|
"whenOpenApp": "Beim Öffnen der App",
|
||||||
"willTakEeffectImmediately": "Wird sofort angewendet",
|
|
||||||
"wolTip": "Nach der Konfiguration von WOL (Wake-on-LAN) wird jedes Mal, wenn der Server verbunden wird, eine WOL-Anfrage gesendet.",
|
"wolTip": "Nach der Konfiguration von WOL (Wake-on-LAN) wird jedes Mal, wenn der Server verbunden wird, eine WOL-Anfrage gesendet.",
|
||||||
"write": "Schreiben",
|
"write": "Schreiben",
|
||||||
"writeScriptFailTip": "Das Schreiben des Skripts ist fehlgeschlagen, möglicherweise aufgrund fehlender Berechtigungen oder das Verzeichnis existiert nicht."
|
"writeScriptFailTip": "Das Schreiben des Skripts ist fehlgeschlagen, möglicherweise aufgrund fehlender Berechtigungen oder das Verzeichnis existiert nicht.",
|
||||||
|
"writeScriptTip": "Nach der Verbindung mit dem Server wird ein Skript in ~/.config/server_box geschrieben, um den Systemstatus zu überwachen. Sie können den Skriptinhalt überprüfen."
|
||||||
}
|
}
|
||||||
@@ -1,86 +1,42 @@
|
|||||||
{
|
{
|
||||||
"@@locale": "en",
|
"@@locale": "en",
|
||||||
"about": "About",
|
|
||||||
"aboutThanks": "Thanks to the following people who participated in.",
|
"aboutThanks": "Thanks to the following people who participated in.",
|
||||||
"add": "Add",
|
"acceptBeta": "Accept beta version updates",
|
||||||
"addAServer": "add a server",
|
"addSystemPrivateKeyTip": "Currently private keys don't exist, do you want to add the one that comes with the system (~/.ssh/id_rsa)?",
|
||||||
"addPrivateKey": "Add private key",
|
|
||||||
"addSystemPrivateKeyTip": "Currently don't have any private key, do you add the one that comes with the system (~/.ssh/id_rsa)?",
|
|
||||||
"added2List": "Added to task list",
|
"added2List": "Added to task list",
|
||||||
"addr": "Address",
|
"addr": "Address",
|
||||||
"all": "All",
|
|
||||||
"alreadyLastDir": "Already in last directory.",
|
"alreadyLastDir": "Already in last directory.",
|
||||||
"alterUrl": "Alter url",
|
"authFailTip": "Authentication failed, please check whether credentials are correct",
|
||||||
"askContinue": "{msg}. Continue?",
|
|
||||||
"attention": "Attention",
|
|
||||||
"authFailTip": "Authentication failed, please check if the password/key/host/user, etc., are incorrect.",
|
|
||||||
"authRequired": "Auth required",
|
|
||||||
"auto": "Auto",
|
|
||||||
"autoBackupConflict": "Only one automatic backup can be turned on at the same time.",
|
"autoBackupConflict": "Only one automatic backup can be turned on at the same time.",
|
||||||
"autoCheckUpdate": "Auto check update",
|
|
||||||
"autoConnect": "Auto connect",
|
"autoConnect": "Auto connect",
|
||||||
"autoRun": "Automatic Run",
|
"autoRun": "Auto run",
|
||||||
"autoUpdateHomeWidget": "Auto update home widget",
|
"autoUpdateHomeWidget": "Automatic home widget update",
|
||||||
"backup": "Backup",
|
"backupTip": "The exported data is weakly encrypted. \nPlease keep it safe.",
|
||||||
"backupTip": "The exported data is simply encrypted. \nPlease keep it safe.",
|
|
||||||
"backupVersionNotMatch": "Backup version is not match.",
|
"backupVersionNotMatch": "Backup version is not match.",
|
||||||
"battery": "Battery",
|
"battery": "Battery",
|
||||||
"bgRun": "Run in backgroud",
|
"bgRun": "Run in background",
|
||||||
"bgRunTip": "This switch only means the program will try to run in the background, whether it can run in the background depends on whether the permission is enabled or not. For native Android, please disable \"Battery Optimization\" in this app, and for miui, please change the power saving policy to \"Unlimited\".",
|
"bgRunTip": "This switch only means the program will try to run in the background. Whether it can run in the background depends on whether the permission is enabled or not. For AOSP-based Android ROMs, please disable \"Battery Optimization\" in this app. For MIUI / HyperOS, please change the power saving policy to \"Unlimited\".",
|
||||||
"bioAuth": "Biometric auth",
|
|
||||||
"browser": "Browser",
|
|
||||||
"bulkImportServers": "Batch import servers",
|
|
||||||
"bulkImportServersTip": "You can find the [format]({url}) here.",
|
|
||||||
"canPullRefresh": "You can pull to refresh.",
|
|
||||||
"cancel": "Cancel",
|
|
||||||
"choose": "Choose",
|
|
||||||
"chooseFontFile": "Choose a font file",
|
|
||||||
"choosePrivateKey": "Choose private key",
|
|
||||||
"clear": "Clear",
|
|
||||||
"clipboard": "Clipboard",
|
|
||||||
"close": "Close",
|
|
||||||
"cmd": "Command",
|
"cmd": "Command",
|
||||||
"cnKeyboardComp": "Compatibility with Chinese Android",
|
|
||||||
"cnKeyboardCompTip": "If the terminal pops up a secure keyboard, you can enable it.",
|
|
||||||
"collapseUI": "Collapse",
|
|
||||||
"collapseUITip": "Whether to collapse long lists present in the UI by default",
|
"collapseUITip": "Whether to collapse long lists present in the UI by default",
|
||||||
"collectUsage": "Collect usage information (unrelated to privacy).",
|
|
||||||
"conn": "Connection",
|
"conn": "Connection",
|
||||||
"connected": "Connected",
|
|
||||||
"container": "Container",
|
"container": "Container",
|
||||||
"containerName": "Container name",
|
|
||||||
"containerStatus": "Container status",
|
|
||||||
"containerTrySudoTip": "For example: In the app, the user is set to aaa, but Docker is installed under the root user. In this case, you need to enable this option.",
|
"containerTrySudoTip": "For example: In the app, the user is set to aaa, but Docker is installed under the root user. In this case, you need to enable this option.",
|
||||||
"content": "Content",
|
|
||||||
"convert": "Convert",
|
"convert": "Convert",
|
||||||
"copy": "Copy",
|
|
||||||
"copyPath": "Copy path",
|
"copyPath": "Copy path",
|
||||||
"cpuViewAsProgressTip": "Display the usage rate of each CPU in a progress bar style (old style)",
|
"cpuViewAsProgressTip": "Display the usage of each CPU in a progress bar style (old style)",
|
||||||
"createFile": "Create file",
|
|
||||||
"createFolder": "Create folder",
|
|
||||||
"cursorType": "Cursor type",
|
"cursorType": "Cursor type",
|
||||||
"customCmd": "Custom commands",
|
"customCmd": "Custom commands",
|
||||||
"customCmdDocUrl": "https://github.com/lollipopkit/flutter_server_box/wiki#custom-commands",
|
"customCmdDocUrl": "https://github.com/lollipopkit/flutter_server_box/wiki#custom-commands",
|
||||||
"customCmdHint": "\"Command Name\": \"Command\"",
|
"customCmdHint": "\"Command Name\": \"Command\"",
|
||||||
"dark": "Dark",
|
|
||||||
"day": "Day",
|
|
||||||
"debug": "Debug",
|
|
||||||
"decode": "Decode",
|
"decode": "Decode",
|
||||||
"decompress": "Decompress",
|
"decompress": "Decompress",
|
||||||
"delete": "Delete",
|
|
||||||
"deleteScripts": "Delete server scripts at the same time",
|
|
||||||
"deleteServers": "Batch delete servers",
|
"deleteServers": "Batch delete servers",
|
||||||
"deviceName": "Device name",
|
"dirEmpty": "Make sure the folder is empty.",
|
||||||
"dirEmpty": "Make sure dir is empty.",
|
|
||||||
"disabled": "Disabled",
|
|
||||||
"disconnected": "Disconnected",
|
"disconnected": "Disconnected",
|
||||||
"disk": "Disk",
|
"disk": "Disk",
|
||||||
"diskIgnorePath": "Ignore path for disk",
|
"diskIgnorePath": "Ignore path for disk",
|
||||||
"displayCpuIndex": "Display CPU index",
|
"displayCpuIndex": "Display CPU index",
|
||||||
"displayName": "Display name",
|
|
||||||
"dl2Local": "Download {fileName} to local?",
|
"dl2Local": "Download {fileName} to local?",
|
||||||
"doc": "Documentation",
|
|
||||||
"dockerEditHost": "Edit DOCKER_HOST",
|
|
||||||
"dockerEmptyRunningItems": "There are no running containers.\nThis could be because:\n- The Docker installation user is not the same as the username configured within the App.\n- The environment variable DOCKER_HOST was not read correctly. You can get it by running `echo $DOCKER_HOST` in the terminal.",
|
"dockerEmptyRunningItems": "There are no running containers.\nThis could be because:\n- The Docker installation user is not the same as the username configured within the App.\n- The environment variable DOCKER_HOST was not read correctly. You can get it by running `echo $DOCKER_HOST` in the terminal.",
|
||||||
"dockerImagesFmt": "{count} images",
|
"dockerImagesFmt": "{count} images",
|
||||||
"dockerNotInstalled": "Docker not installed",
|
"dockerNotInstalled": "Docker not installed",
|
||||||
@@ -88,135 +44,86 @@
|
|||||||
"dockerStatusRunningFmt": "{count} container running.",
|
"dockerStatusRunningFmt": "{count} container running.",
|
||||||
"doubleColumnMode": "Double column mode",
|
"doubleColumnMode": "Double column mode",
|
||||||
"doubleColumnTip": "This option only enables the feature, whether it can actually be enabled depends on the width of the device",
|
"doubleColumnTip": "This option only enables the feature, whether it can actually be enabled depends on the width of the device",
|
||||||
"download": "Download",
|
|
||||||
"edit": "Edit",
|
|
||||||
"editVirtKeys": "Edit virtual keys",
|
"editVirtKeys": "Edit virtual keys",
|
||||||
"editor": "Editor",
|
"editor": "Editor",
|
||||||
"editorHighlightTip": "The current code highlighting performance is worse and can be optionally turned off to improve.",
|
"editorHighlightTip": "The current code highlighting performance is not ideal and can be optionally turned off to improve.",
|
||||||
"encode": "Encode",
|
"encode": "Encode",
|
||||||
"error": "Error",
|
"envVars": "Environment variable",
|
||||||
"exampleName": "Example name",
|
|
||||||
"experimentalFeature": "Experimental feature",
|
"experimentalFeature": "Experimental feature",
|
||||||
"export": "Export",
|
"extraArgs": "Extra arguments",
|
||||||
"extraArgs": "Extra args",
|
"fallbackSshDest": "Fallback SSH destination",
|
||||||
"failed": "Failed",
|
"fdroidReleaseTip": "If you downloaded this app from F-Droid, it is recommended to turn off this option.",
|
||||||
"feedback": "Feedback",
|
|
||||||
"feedbackOnGithub": "If you have any questions, please feedback on Github.",
|
|
||||||
"fieldMustNotEmpty": "These fields must not be empty.",
|
|
||||||
"fileNotExist": "{file} not exist",
|
|
||||||
"fileTooLarge": "File '{file}' too large {size}, max {sizeMax}",
|
"fileTooLarge": "File '{file}' too large {size}, max {sizeMax}",
|
||||||
"files": "Files",
|
|
||||||
"finished": "Finished",
|
|
||||||
"followSystem": "Follow system",
|
"followSystem": "Follow system",
|
||||||
"font": "Font",
|
"font": "Font",
|
||||||
"fontSize": "Font size",
|
"fontSize": "Font size",
|
||||||
"force": "Force",
|
"force": "Force",
|
||||||
"foundNUpdate": "Found {count} update",
|
|
||||||
"fullScreen": "Full screen mode",
|
"fullScreen": "Full screen mode",
|
||||||
"fullScreenJitter": "Full screen jitter",
|
"fullScreenJitter": "Full screen jitter",
|
||||||
"fullScreenJitterHelp": "To avoid screen burn-in",
|
"fullScreenJitterHelp": "To avoid screen burn-in",
|
||||||
"fullScreenTip": "Should full-screen mode be enabled when the device is rotated to landscape mode? This option only applies to the server tab.",
|
"fullScreenTip": "Should full-screen mode be enabled when the device is rotated to landscape mode? This option only applies to the server tab.",
|
||||||
"getPushTokenFailed": "Can't fetch push token",
|
|
||||||
"gettingToken": "Getting token...",
|
|
||||||
"goBackQ": "Go back?",
|
"goBackQ": "Go back?",
|
||||||
"goto": "Go to",
|
"goto": "Go to",
|
||||||
"hideTitleBar": "Hide title bar",
|
"hideTitleBar": "Hide title bar",
|
||||||
"hideTitleBarTip": "After turning it on, please hold down the three buttons in the top right corner to drag.",
|
"hideTitleBarTip": "After turning it on, please hold down the three buttons in the top right corner to drag.",
|
||||||
"highlight": "Code highlight",
|
"highlight": "Code highlighting",
|
||||||
"homeWidgetUrlConfig": "Config home widget url",
|
"homeWidgetUrlConfig": "Config home widget url",
|
||||||
"host": "Host",
|
"host": "Host",
|
||||||
"hour": "Hour",
|
|
||||||
"httpFailedWithCode": "request failed, status code: {code}",
|
"httpFailedWithCode": "request failed, status code: {code}",
|
||||||
"icloudSynced": "iCloud wird synchronisiert und einige Einstellungen erfordern möglicherweise einen Neustart der App, um wirksam zu werden.",
|
|
||||||
"ignoreCert": "Ignore certificate",
|
"ignoreCert": "Ignore certificate",
|
||||||
"image": "Image",
|
"image": "Image",
|
||||||
"imagesList": "Images list",
|
"imagesList": "Images list",
|
||||||
"import": "Import",
|
"init": "Initialize",
|
||||||
"inAppUpdate": "Update within the app? Otherwise, download using a browser.",
|
|
||||||
"inner": "Inner",
|
"inner": "Inner",
|
||||||
"inputDomainHere": "Input Domain here",
|
|
||||||
"install": "install",
|
"install": "install",
|
||||||
"installDockerWithUrl": "Please https://docs.docker.com/engine/install docker first.",
|
"installDockerWithUrl": "Please https://docs.docker.com/engine/install docker first.",
|
||||||
"invalid": "Invalid",
|
"invalid": "Invalid",
|
||||||
"invalidJson": "Invalid JSON",
|
|
||||||
"invalidVersion": "Invalid version",
|
|
||||||
"invalidVersionHelp": "Please make sure that docker is installed correctly, or that you are using a non-self-compiled version. If you don't have the above issues, please submit an issue on {url}.",
|
|
||||||
"isBusy": "Is busy now",
|
|
||||||
"jumpServer": "Jump server",
|
"jumpServer": "Jump server",
|
||||||
"keepForeground": "Keep app foreground!",
|
"keepForeground": "Keep app foreground!",
|
||||||
"keepStatusWhenErr": "Preserve the last server state",
|
"keepStatusWhenErr": "Preserve the last server state",
|
||||||
"keepStatusWhenErrTip": "Only in the event of an error during script execution",
|
"keepStatusWhenErrTip": "Only in the event of an error during script execution",
|
||||||
"keyAuth": "Key Auth",
|
"keyAuth": "Key Auth",
|
||||||
"language": "Language",
|
"letterCache": "Letter caching",
|
||||||
"languageName": "English",
|
"letterCacheTip": "Recommended to disable, but after disabling, it will be impossible to input CJK characters.",
|
||||||
"lastTry": "Last try",
|
|
||||||
"launchPage": "Launch page",
|
|
||||||
"license": "License",
|
"license": "License",
|
||||||
"light": "Light",
|
|
||||||
"loadingFiles": "Loading files...",
|
|
||||||
"location": "Location",
|
"location": "Location",
|
||||||
"log": "Log",
|
|
||||||
"loss": "loss",
|
"loss": "loss",
|
||||||
"madeWithLove": "Made with ❤️ by {myGithub}",
|
"madeWithLove": "Made with ❤️ by {myGithub}",
|
||||||
"manual": "Manual",
|
"manual": "Manual",
|
||||||
"max": "max",
|
"max": "max",
|
||||||
"maxRetryCount": "Number of server reconnection",
|
"maxRetryCount": "Number of server reconnections",
|
||||||
"maxRetryCountEqual0": "Will retry again and again.",
|
"maxRetryCountEqual0": "Will retry again and again.",
|
||||||
"min": "min",
|
"min": "min",
|
||||||
"minute": "Minute",
|
|
||||||
"mission": "Mission",
|
"mission": "Mission",
|
||||||
"more": "More",
|
"more": "More",
|
||||||
"moveOutServerFuncBtnsHelp": "On: can be displayed below each card on the Server Tab page. Off: can be displayed at the top of the Server Details page.",
|
"moveOutServerFuncBtnsHelp": "On: can be displayed below each card on the Server Tab page. Off: can be displayed at the top of the Server Details page.",
|
||||||
"ms": "ms",
|
"ms": "ms",
|
||||||
"name": "Name",
|
|
||||||
"needHomeDir": "If you are a Synology user, [see here](https://kb.synology.com/DSM/tutorial/user_enable_home_service). Users of other systems need to search for how to create a home directory.",
|
"needHomeDir": "If you are a Synology user, [see here](https://kb.synology.com/DSM/tutorial/user_enable_home_service). Users of other systems need to search for how to create a home directory.",
|
||||||
"needRestart": "Need to restart app",
|
"needRestart": "App needs to be restarted",
|
||||||
"net": "Net",
|
"net": "Network",
|
||||||
"netViewType": "Net view type",
|
"netViewType": "Network view type",
|
||||||
"newContainer": "New container",
|
"newContainer": "New container",
|
||||||
"noClient": "No client",
|
|
||||||
"noInterface": "No interface",
|
|
||||||
"noLineChart": "Do not use line charts",
|
"noLineChart": "Do not use line charts",
|
||||||
"noNotiPerm": "No notification permissions, possibly no progress indication when downloading app updates.",
|
"noLineChartForCpu": "Do not use line charts for CPU",
|
||||||
"noOptions": "No options",
|
|
||||||
"noPrivateKeyTip": "The private key does not exist, it may have been deleted or there is a configuration error.",
|
"noPrivateKeyTip": "The private key does not exist, it may have been deleted or there is a configuration error.",
|
||||||
"noPromptAgain": "Do not prompt again",
|
"noPromptAgain": "Do not prompt again",
|
||||||
"noResult": "No result",
|
|
||||||
"noSavedPrivateKey": "No saved private keys.",
|
|
||||||
"noSavedSnippet": "No saved snippets.",
|
|
||||||
"noServerAvailable": "No server available.",
|
|
||||||
"noTask": "No task",
|
|
||||||
"noUpdateAvailable": "No update available",
|
|
||||||
"node": "Node",
|
"node": "Node",
|
||||||
"notAvailable": "Unavailable",
|
"notAvailable": "Unavailable",
|
||||||
"notSelected": "Not selected",
|
|
||||||
"note": "Note",
|
|
||||||
"nullToken": "Null token",
|
|
||||||
"ok": "OK",
|
|
||||||
"onServerDetailPage": "On server detail page",
|
"onServerDetailPage": "On server detail page",
|
||||||
"onlyOneLine": "Only display as one line (scrollable)",
|
"onlyOneLine": "Only display as one line (scrollable)",
|
||||||
"onlyWhenCoreBiggerThan8": "Works only when the number of cores > 8",
|
"onlyWhenCoreBiggerThan8": "Works only when the number of cores is greater than 8",
|
||||||
"open": "Open",
|
|
||||||
"openLastPath": "Open the last path",
|
"openLastPath": "Open the last path",
|
||||||
"openLastPathTip": "Different servers will have different logs, and the log is the path to the exit",
|
"openLastPathTip": "Different servers will have different logs, and the log is the path to the exit",
|
||||||
"parseContainerStats": "Parse the container occupancy status",
|
"parseContainerStatsTip": "Parsing the occupancy status of Docker is relatively slow.",
|
||||||
"parseContainerStatsTip": "Docker parsing the occupancy status is relatively slow.",
|
|
||||||
"paste": "Paste",
|
|
||||||
"path": "Path",
|
|
||||||
"percentOfSize": "{percent}% of {size}",
|
"percentOfSize": "{percent}% of {size}",
|
||||||
"pickFile": "Pick file",
|
"permission": "Permissions",
|
||||||
"pingAvg": "Avg:",
|
"pingAvg": "Avg:",
|
||||||
"pingInputIP": "Please input a target IP / domain.",
|
"pingInputIP": "Please input a target IP / domain.",
|
||||||
"pingNoServer": "No server to ping.\nPlease add a server in server tab.",
|
"pingNoServer": "No server to ping.\nPlease add a server in server tab.",
|
||||||
"pkg": "Pkg",
|
"pkg": "Pkg",
|
||||||
"pkgUpgradeTip": "Please backup your system before updating.",
|
|
||||||
"platformNotSupportUpdate": "Current platform does not support in app update.\nPlease build from source and install it.",
|
|
||||||
"plugInType": "Insertion Type",
|
"plugInType": "Insertion Type",
|
||||||
"plzEnterHost": "Please enter host.",
|
|
||||||
"plzSelectKey": "Please select a key.",
|
|
||||||
"port": "Port",
|
"port": "Port",
|
||||||
"preview": "Preview",
|
"preview": "Preview",
|
||||||
"primaryColorSeed": "Primary color seed",
|
|
||||||
"privateKey": "Private Key",
|
"privateKey": "Private Key",
|
||||||
"process": "Process",
|
"process": "Process",
|
||||||
"pushToken": "Push token",
|
"pushToken": "Push token",
|
||||||
@@ -226,15 +133,11 @@
|
|||||||
"pwd": "Password",
|
"pwd": "Password",
|
||||||
"read": "Read",
|
"read": "Read",
|
||||||
"reboot": "Reboot",
|
"reboot": "Reboot",
|
||||||
"rememberChoice": "Remember the selection",
|
|
||||||
"rememberPwdInMem": "Remember password in memory",
|
"rememberPwdInMem": "Remember password in memory",
|
||||||
"rememberPwdInMemTip": "Used for containers, suspending, etc.",
|
"rememberPwdInMemTip": "Used for containers, suspending, etc.",
|
||||||
|
"rememberWindowSize": "Remember window size",
|
||||||
"remotePath": "Remote path",
|
"remotePath": "Remote path",
|
||||||
"rename": "Rename",
|
|
||||||
"reportBugsOnGithubIssue": "Please report bugs on {url}",
|
|
||||||
"restart": "Restart",
|
"restart": "Restart",
|
||||||
"restore": "Restore",
|
|
||||||
"restoreSuccess": "Restore success. Restart app to apply.",
|
|
||||||
"result": "Result",
|
"result": "Result",
|
||||||
"rotateAngel": "Rotation angle",
|
"rotateAngel": "Rotation angle",
|
||||||
"route": "Routing",
|
"route": "Routing",
|
||||||
@@ -247,16 +150,10 @@
|
|||||||
"sequence": "Sequence",
|
"sequence": "Sequence",
|
||||||
"server": "Server",
|
"server": "Server",
|
||||||
"serverDetailOrder": "Detail page widget order",
|
"serverDetailOrder": "Detail page widget order",
|
||||||
"serverFuncBtns": "Server func buttons",
|
"serverFuncBtns": "Server function buttons",
|
||||||
"serverOrder": "Server order",
|
"serverOrder": "Server order",
|
||||||
"serverTabConnecting": "Connecting...",
|
|
||||||
"serverTabEmpty": "There is no server.\nClick the fab to add one.",
|
|
||||||
"serverTabFailed": "Failed",
|
|
||||||
"serverTabLoading": "Loading...",
|
|
||||||
"serverTabPlzSave": "Please 'save' this private key again.",
|
|
||||||
"serverTabUnkown": "Unknown state",
|
|
||||||
"setting": "Settings",
|
|
||||||
"sftpDlPrepare": "Preparing to connect...",
|
"sftpDlPrepare": "Preparing to connect...",
|
||||||
|
"sftpEditorTip": "If empty, use the built-in file editor of the app. If a value is present, use the remote server’s editor, e.g., `vim` (recommended to automatically detect according to `EDITOR`).",
|
||||||
"sftpRmrDirSummary": "Use `rm -r` to delete a folder in SFTP.",
|
"sftpRmrDirSummary": "Use `rm -r` to delete a folder in SFTP.",
|
||||||
"sftpSSHConnected": "SFTP Connected",
|
"sftpSSHConnected": "SFTP Connected",
|
||||||
"sftpShowFoldersFirst": "Display folders first",
|
"sftpShowFoldersFirst": "Display folders first",
|
||||||
@@ -271,14 +168,14 @@
|
|||||||
"sshTip": "This function is now in the experimental stage.\n\nPlease report bugs on {url} or join our development.",
|
"sshTip": "This function is now in the experimental stage.\n\nPlease report bugs on {url} or join our development.",
|
||||||
"sshVirtualKeyAutoOff": "Auto switching of virtual keys",
|
"sshVirtualKeyAutoOff": "Auto switching of virtual keys",
|
||||||
"start": "Start",
|
"start": "Start",
|
||||||
"stats": "Stats",
|
"stat": "Statistics",
|
||||||
|
"stats": "Statistics",
|
||||||
"stop": "Stop",
|
"stop": "Stop",
|
||||||
"stopped": "Stopped",
|
"stopped": "Stopped",
|
||||||
"storage": "Storage",
|
"storage": "Storage",
|
||||||
"success": "Success",
|
|
||||||
"supportFmtArgs": "The following formatting parameters are supported:",
|
"supportFmtArgs": "The following formatting parameters are supported:",
|
||||||
"suspend": "Suspend",
|
"suspend": "Suspend",
|
||||||
"suspendTip": "The suspend function requires root privileges and systemd support.",
|
"suspendTip": "The suspend function requires root permission and systemd support.",
|
||||||
"switchTo": "Switch to {val}",
|
"switchTo": "Switch to {val}",
|
||||||
"sync": "Sync",
|
"sync": "Sync",
|
||||||
"syncTip": "A restart may be required for some changes to take effect.",
|
"syncTip": "A restart may be required for some changes to take effect.",
|
||||||
@@ -291,47 +188,37 @@
|
|||||||
"textScaler": "Text scaler",
|
"textScaler": "Text scaler",
|
||||||
"textScalerTip": "1.0 => 100% (original size), only works on server page part of the font, not recommended to change.",
|
"textScalerTip": "1.0 => 100% (original size), only works on server page part of the font, not recommended to change.",
|
||||||
"theme": "Theme",
|
"theme": "Theme",
|
||||||
"themeMode": "Theme mode",
|
|
||||||
"time": "Time",
|
"time": "Time",
|
||||||
"times": "Times",
|
"times": "Times",
|
||||||
"total": "Total",
|
"total": "Total",
|
||||||
"traffic": "Traffic",
|
"traffic": "Traffic",
|
||||||
"trySudo": "Try using sudo",
|
"trySudo": "Try using sudo",
|
||||||
"ttl": "ttl",
|
"ttl": "TTL",
|
||||||
"unknown": "Unknown",
|
"unknown": "Unknown",
|
||||||
"unknownError": "Unknown error",
|
"unkownConvertMode": "Unknown conversion mode",
|
||||||
"unkownConvertMode": "Unknown convert mode",
|
|
||||||
"update": "Update",
|
"update": "Update",
|
||||||
"updateAll": "Update all",
|
|
||||||
"updateIntervalEqual0": "You set to 0, will not update automatically.\nCan't calculate CPU status.",
|
"updateIntervalEqual0": "You set to 0, will not update automatically.\nCan't calculate CPU status.",
|
||||||
"updateServerStatusInterval": "Server status update interval",
|
"updateServerStatusInterval": "Server status update interval",
|
||||||
"updateTip": "Update: v1.0.{newest}",
|
|
||||||
"updateTipTooLow": "Current version is too low, please update to v1.0.{newest}",
|
|
||||||
"upload": "Upload",
|
"upload": "Upload",
|
||||||
"upsideDown": "Upside Down",
|
"upsideDown": "Upside Down",
|
||||||
"uptime": "Uptime",
|
"uptime": "Uptime",
|
||||||
"urlOrJson": "URL or JSON",
|
|
||||||
"useCdn": "Using CDN",
|
"useCdn": "Using CDN",
|
||||||
"useCdnTip": "Non-Chinese users are recommended to use CDN. Would you like to use it?",
|
"useCdnTip": "Non-Chinese users are recommended to use CDN. Would you like to use it?",
|
||||||
"useNoPwd": "No password will be used",
|
"useNoPwd": "No password will be used",
|
||||||
"usePodmanByDefault": "Defaulting to Podman",
|
"usePodmanByDefault": "Use Podman by default",
|
||||||
"used": "Used",
|
"used": "Used",
|
||||||
"user": "User",
|
|
||||||
"versionHaveUpdate": "Found: v1.0.{build}, click to update",
|
|
||||||
"versionUnknownUpdate": "Current: v1.0.{build}, click to check updates",
|
|
||||||
"versionUpdated": "Current: v1.0.{build}, is up to date",
|
|
||||||
"view": "View",
|
"view": "View",
|
||||||
"viewErr": "See error",
|
"viewErr": "See error",
|
||||||
"virtKeyHelpClipboard": "Copy to the clipboard if terminal selected is not empty, otherwise paste the contents of the clipboard to the terminal.",
|
"virtKeyHelpClipboard": "Copy to the clipboard if the selected terminal is not empty, otherwise paste the content of the clipboard to the terminal.",
|
||||||
"virtKeyHelpIME": "Turn on/off the keyboard",
|
"virtKeyHelpIME": "Turn on/off the keyboard",
|
||||||
"virtKeyHelpSFTP": "Open current directory in SFTP.",
|
"virtKeyHelpSFTP": "Open current directory in SFTP.",
|
||||||
"waitConnection": "Please wait for the connection to be established.",
|
"waitConnection": "Please wait for the connection to be established.",
|
||||||
"wakeLock": "Keep awake",
|
"wakeLock": "Keep awake",
|
||||||
"watchNotPaired": "No paired Apple Watch",
|
"watchNotPaired": "No paired Apple Watch",
|
||||||
"webdavSettingEmpty": "Webdav setting is empty",
|
"webdavSettingEmpty": "WebDav setting is empty",
|
||||||
"whenOpenApp": "When opening the app",
|
"whenOpenApp": "When opening the app",
|
||||||
"willTakEeffectImmediately": "Will take effect immediately",
|
|
||||||
"wolTip": "After configuring WOL (Wake-on-LAN), a WOL request is sent each time the server is connected.",
|
"wolTip": "After configuring WOL (Wake-on-LAN), a WOL request is sent each time the server is connected.",
|
||||||
"write": "Write",
|
"write": "Write",
|
||||||
"writeScriptFailTip": "Writing to the script failed, possibly due to lack of permissions or the directory does not exist."
|
"writeScriptFailTip": "Writing to the script failed, possibly due to lack of permissions or the directory does not exist.",
|
||||||
|
"writeScriptTip": "After connecting to the server, a script will be written to ~/.config/server_box to monitor the system status. You can review the script content."
|
||||||
}
|
}
|
||||||
@@ -1,86 +1,42 @@
|
|||||||
{
|
{
|
||||||
"@@locale": "es",
|
"@@locale": "es",
|
||||||
"about": "Acerca de",
|
|
||||||
"aboutThanks": "Gracias a los siguientes participantes.",
|
"aboutThanks": "Gracias a los siguientes participantes.",
|
||||||
"add": "Añadir",
|
"acceptBeta": "Aceptar actualizaciones de la versión de prueba",
|
||||||
"addAServer": "Agregar un servidor",
|
|
||||||
"addPrivateKey": "Agregar una llave privada",
|
|
||||||
"addSystemPrivateKeyTip": "Actualmente no hay ninguna llave privada, ¿quieres agregar la que viene por defecto en el sistema (~/.ssh/id_rsa)?",
|
"addSystemPrivateKeyTip": "Actualmente no hay ninguna llave privada, ¿quieres agregar la que viene por defecto en el sistema (~/.ssh/id_rsa)?",
|
||||||
"added2List": "Añadido a la lista de tareas",
|
"added2List": "Añadido a la lista de tareas",
|
||||||
"addr": "Dirección",
|
"addr": "Dirección",
|
||||||
"all": "Todos",
|
|
||||||
"alreadyLastDir": "Ya estás en el directorio superior",
|
"alreadyLastDir": "Ya estás en el directorio superior",
|
||||||
"alterUrl": "URL alternativa",
|
|
||||||
"askContinue": "{msg}, ¿continuar?",
|
|
||||||
"attention": "Atención",
|
|
||||||
"authFailTip": "La autenticación ha fallado, por favor verifica si la contraseña/llave/host/usuario, etc., son incorrectos.",
|
"authFailTip": "La autenticación ha fallado, por favor verifica si la contraseña/llave/host/usuario, etc., son incorrectos.",
|
||||||
"authRequired": "Autenticación requerida",
|
|
||||||
"auto": "Automático",
|
|
||||||
"autoBackupConflict": "Solo se puede activar una copia de seguridad automática a la vez",
|
"autoBackupConflict": "Solo se puede activar una copia de seguridad automática a la vez",
|
||||||
"autoCheckUpdate": "Verificación automática de actualizaciones",
|
|
||||||
"autoConnect": "Conexión automática",
|
"autoConnect": "Conexión automática",
|
||||||
"autoRun": "Ejecución automática",
|
"autoRun": "Ejecución automática",
|
||||||
"autoUpdateHomeWidget": "Actualizar automáticamente el widget del escritorio",
|
"autoUpdateHomeWidget": "Actualizar automáticamente el widget del escritorio",
|
||||||
"backup": "Respaldo",
|
|
||||||
"backupTip": "Los datos exportados solo están encriptados de manera básica, por favor guárdalos en un lugar seguro.",
|
"backupTip": "Los datos exportados solo están encriptados de manera básica, por favor guárdalos en un lugar seguro.",
|
||||||
"backupVersionNotMatch": "La versión de la copia de seguridad no coincide, no se puede restaurar",
|
"backupVersionNotMatch": "La versión de la copia de seguridad no coincide, no se puede restaurar",
|
||||||
"battery": "Batería",
|
"battery": "Batería",
|
||||||
"bgRun": "Ejecución en segundo plano",
|
"bgRun": "Ejecución en segundo plano",
|
||||||
"bgRunTip": "Este interruptor solo indica que la aplicación intentará correr en segundo plano, si puede hacerlo o no depende de si tiene el permiso correspondiente. En Android puro, por favor desactiva la “optimización de batería” para esta app, en MIUI por favor cambia la estrategia de ahorro de energía a “Sin restricciones”.",
|
"bgRunTip": "Este interruptor solo indica que la aplicación intentará correr en segundo plano, si puede hacerlo o no depende de si tiene el permiso correspondiente. En Android puro, por favor desactiva la “optimización de batería” para esta app, en MIUI por favor cambia la estrategia de ahorro de energía a “Sin restricciones”.",
|
||||||
"bioAuth": "Autenticación biométrica",
|
|
||||||
"browser": "Navegador",
|
|
||||||
"bulkImportServers": "Importar servidores en masa",
|
|
||||||
"bulkImportServersTip": "Puede encontrar el [formato]]({url}) aquí.",
|
|
||||||
"canPullRefresh": "Se puede deslizar hacia abajo para refrescar",
|
|
||||||
"cancel": "Cancelar",
|
|
||||||
"choose": "Elegir",
|
|
||||||
"chooseFontFile": "Seleccionar archivo de fuente",
|
|
||||||
"choosePrivateKey": "Elegir llave privada",
|
|
||||||
"clear": "Limpiar",
|
|
||||||
"clipboard": "Portapapeles",
|
|
||||||
"close": "Cerrar",
|
|
||||||
"cmd": "Comando",
|
"cmd": "Comando",
|
||||||
"cnKeyboardComp": "Compatibilidad con Android chino",
|
|
||||||
"cnKeyboardCompTip": "Si el terminal muestra un teclado seguro, puedes activarlo.",
|
|
||||||
"collapseUI": "Colapsar",
|
|
||||||
"collapseUITip": "¿Colapsar por defecto las listas largas en la UI?",
|
"collapseUITip": "¿Colapsar por defecto las listas largas en la UI?",
|
||||||
"collectUsage": "Recopilar información de uso (no relacionada con la privacidad).",
|
|
||||||
"conn": "Conectar",
|
"conn": "Conectar",
|
||||||
"connected": "Conectado",
|
|
||||||
"container": "Contenedor",
|
"container": "Contenedor",
|
||||||
"containerName": "Nombre del contenedor",
|
|
||||||
"containerStatus": "Estado del contenedor",
|
|
||||||
"containerTrySudoTip": "Por ejemplo: si configuras el usuario dentro de la app como aaa, pero Docker está instalado bajo el usuario root, entonces necesitarás habilitar esta opción",
|
"containerTrySudoTip": "Por ejemplo: si configuras el usuario dentro de la app como aaa, pero Docker está instalado bajo el usuario root, entonces necesitarás habilitar esta opción",
|
||||||
"content": "Contenido",
|
|
||||||
"convert": "Convertir",
|
"convert": "Convertir",
|
||||||
"copy": "Copiar",
|
|
||||||
"copyPath": "Copiar ruta",
|
"copyPath": "Copiar ruta",
|
||||||
"cpuViewAsProgressTip": "Muestre la tasa de uso de cada CPU en estilo de barra de progreso (estilo antiguo)",
|
"cpuViewAsProgressTip": "Muestre la tasa de uso de cada CPU en estilo de barra de progreso (estilo antiguo)",
|
||||||
"createFile": "Crear archivo",
|
|
||||||
"createFolder": "Crear carpeta",
|
|
||||||
"cursorType": "Tipo de cursor",
|
"cursorType": "Tipo de cursor",
|
||||||
"customCmd": "Comandos personalizados",
|
"customCmd": "Comandos personalizados",
|
||||||
"customCmdDocUrl": "https://github.com/lollipopkit/flutter_server_box/wiki#custom-commands",
|
"customCmdDocUrl": "https://github.com/lollipopkit/flutter_server_box/wiki#custom-commands",
|
||||||
"customCmdHint": "\"Nombre del comando\": \"Comando\"",
|
"customCmdHint": "\"Nombre del comando\": \"Comando\"",
|
||||||
"dark": "Oscuro",
|
|
||||||
"day": "Día",
|
|
||||||
"debug": "Depurar",
|
|
||||||
"decode": "Decodificar",
|
"decode": "Decodificar",
|
||||||
"decompress": "Descomprimir",
|
"decompress": "Descomprimir",
|
||||||
"delete": "Eliminar",
|
|
||||||
"deleteScripts": "Eliminar scripts del servidor simultáneamente",
|
|
||||||
"deleteServers": "Eliminar servidores en lote",
|
"deleteServers": "Eliminar servidores en lote",
|
||||||
"deviceName": "Nombre del dispositivo",
|
|
||||||
"dirEmpty": "Asegúrate de que el directorio esté vacío",
|
"dirEmpty": "Asegúrate de que el directorio esté vacío",
|
||||||
"disabled": "Deshabilitado",
|
|
||||||
"disconnected": "Desconectado",
|
"disconnected": "Desconectado",
|
||||||
"disk": "Disco",
|
"disk": "Disco",
|
||||||
"diskIgnorePath": "Rutas de disco ignoradas",
|
"diskIgnorePath": "Rutas de disco ignoradas",
|
||||||
"displayCpuIndex": "Muestre el índice de CPU",
|
"displayCpuIndex": "Muestre el índice de CPU",
|
||||||
"displayName": "Nombre a mostrar",
|
|
||||||
"dl2Local": "¿Descargar {fileName} a local?",
|
"dl2Local": "¿Descargar {fileName} a local?",
|
||||||
"doc": "Documentación",
|
|
||||||
"dockerEditHost": "Editar DOCKER_HOST",
|
|
||||||
"dockerEmptyRunningItems": "No hay contenedores en ejecución.\nEsto podría deberse a que:\n- El usuario con el que se instaló Docker es diferente al configurado en la app\n- La variable de entorno DOCKER_HOST no se ha leído correctamente. Puedes obtenerla ejecutando `echo $DOCKER_HOST` en el terminal.",
|
"dockerEmptyRunningItems": "No hay contenedores en ejecución.\nEsto podría deberse a que:\n- El usuario con el que se instaló Docker es diferente al configurado en la app\n- La variable de entorno DOCKER_HOST no se ha leído correctamente. Puedes obtenerla ejecutando `echo $DOCKER_HOST` en el terminal.",
|
||||||
"dockerImagesFmt": "Total de {count} imágenes",
|
"dockerImagesFmt": "Total de {count} imágenes",
|
||||||
"dockerNotInstalled": "Docker no está instalado",
|
"dockerNotInstalled": "Docker no está instalado",
|
||||||
@@ -88,36 +44,24 @@
|
|||||||
"dockerStatusRunningFmt": "{count} contenedores en ejecución",
|
"dockerStatusRunningFmt": "{count} contenedores en ejecución",
|
||||||
"doubleColumnMode": "Modo de doble columna",
|
"doubleColumnMode": "Modo de doble columna",
|
||||||
"doubleColumnTip": "Esta opción solo habilita la función, si se puede activar o no depende del ancho del dispositivo",
|
"doubleColumnTip": "Esta opción solo habilita la función, si se puede activar o no depende del ancho del dispositivo",
|
||||||
"download": "Descargar",
|
|
||||||
"edit": "Editar",
|
|
||||||
"editVirtKeys": "Editar teclas virtuales",
|
"editVirtKeys": "Editar teclas virtuales",
|
||||||
"editor": "Editor",
|
"editor": "Editor",
|
||||||
"editorHighlightTip": "El rendimiento del resaltado de código es bastante pobre actualmente, puedes elegir desactivarlo para mejorar.",
|
"editorHighlightTip": "El rendimiento del resaltado de código es bastante pobre actualmente, puedes elegir desactivarlo para mejorar.",
|
||||||
"encode": "Codificar",
|
"encode": "Codificar",
|
||||||
"error": "Error",
|
"envVars": "Variable de entorno",
|
||||||
"exampleName": "Ejemplo de nombre",
|
|
||||||
"experimentalFeature": "Función experimental",
|
"experimentalFeature": "Función experimental",
|
||||||
"export": "Exportar",
|
|
||||||
"extraArgs": "Argumentos extra",
|
"extraArgs": "Argumentos extra",
|
||||||
"failed": "Fallido",
|
"fallbackSshDest": "Destino SSH alternativo",
|
||||||
"feedback": "Retroalimentación",
|
"fdroidReleaseTip": "Si descargaste esta aplicación desde F-Droid, se recomienda desactivar esta opción.",
|
||||||
"feedbackOnGithub": "Si tienes algún problema, por favor informa en GitHub",
|
|
||||||
"fieldMustNotEmpty": "Estos campos no pueden estar vacíos.",
|
|
||||||
"fileNotExist": "{file} no existe",
|
|
||||||
"fileTooLarge": "El archivo '{file}' es demasiado grande '{size}', supera el {sizeMax}",
|
"fileTooLarge": "El archivo '{file}' es demasiado grande '{size}', supera el {sizeMax}",
|
||||||
"files": "Archivos",
|
|
||||||
"finished": "Finalizado",
|
|
||||||
"followSystem": "Seguir al sistema",
|
"followSystem": "Seguir al sistema",
|
||||||
"font": "Fuente",
|
"font": "Fuente",
|
||||||
"fontSize": "Tamaño de fuente",
|
"fontSize": "Tamaño de fuente",
|
||||||
"force": "Forzar",
|
"force": "Forzar",
|
||||||
"foundNUpdate": "Encontradas {count} actualizaciones",
|
|
||||||
"fullScreen": "Modo pantalla completa",
|
"fullScreen": "Modo pantalla completa",
|
||||||
"fullScreenJitter": "Temblores en modo pantalla completa",
|
"fullScreenJitter": "Temblores en modo pantalla completa",
|
||||||
"fullScreenJitterHelp": "Prevención de quemaduras de pantalla",
|
"fullScreenJitterHelp": "Prevención de quemaduras de pantalla",
|
||||||
"fullScreenTip": "¿Debe habilitarse el modo de pantalla completa cuando el dispositivo se rote al modo horizontal? Esta opción solo se aplica a la pestaña del servidor.",
|
"fullScreenTip": "¿Debe habilitarse el modo de pantalla completa cuando el dispositivo se rote al modo horizontal? Esta opción solo se aplica a la pestaña del servidor.",
|
||||||
"getPushTokenFailed": "No se pudo obtener el token de notificación",
|
|
||||||
"gettingToken": "Obteniendo Token...",
|
|
||||||
"goBackQ": "¿Regresar?",
|
"goBackQ": "¿Regresar?",
|
||||||
"goto": "Ir a",
|
"goto": "Ir a",
|
||||||
"hideTitleBar": "Ocultar barra de título",
|
"hideTitleBar": "Ocultar barra de título",
|
||||||
@@ -125,37 +69,24 @@
|
|||||||
"highlight": "Resaltar código",
|
"highlight": "Resaltar código",
|
||||||
"homeWidgetUrlConfig": "Configuración de URL del widget de inicio",
|
"homeWidgetUrlConfig": "Configuración de URL del widget de inicio",
|
||||||
"host": "Anfitrión",
|
"host": "Anfitrión",
|
||||||
"hour": "Hora",
|
|
||||||
"httpFailedWithCode": "Fallo en la solicitud, código de estado: {code}",
|
"httpFailedWithCode": "Fallo en la solicitud, código de estado: {code}",
|
||||||
"icloudSynced": "iCloud sincronizado, algunos ajustes pueden requerir reiniciar para tomar efecto.",
|
|
||||||
"ignoreCert": "Ignorar certificado",
|
"ignoreCert": "Ignorar certificado",
|
||||||
"image": "Imagen",
|
"image": "Imagen",
|
||||||
"imagesList": "Lista de imágenes",
|
"imagesList": "Lista de imágenes",
|
||||||
"import": "Importar",
|
"init": "Inicializar",
|
||||||
"inAppUpdate": "¿Actualizar dentro de la app? De lo contrario, descargar usando un navegador.",
|
|
||||||
"inner": "Interno",
|
"inner": "Interno",
|
||||||
"inputDomainHere": "Introduce el dominio aquí",
|
|
||||||
"install": "Instalar",
|
"install": "Instalar",
|
||||||
"installDockerWithUrl": "Por favor instala Docker primero desde https://docs.docker.com/engine/install",
|
"installDockerWithUrl": "Por favor instala Docker primero desde https://docs.docker.com/engine/install",
|
||||||
"invalid": "Inválido",
|
"invalid": "Inválido",
|
||||||
"invalidJson": "JSON inválido",
|
|
||||||
"invalidVersion": "Versión no soportada",
|
|
||||||
"invalidVersionHelp": "Por favor asegúrate de haber instalado Docker correctamente o que no estés usando una versión compilada por ti mismo. Si no tienes estos problemas, informa en {url}.",
|
|
||||||
"isBusy": "Está ocupado",
|
|
||||||
"jumpServer": "Servidor de salto",
|
"jumpServer": "Servidor de salto",
|
||||||
"keepForeground": "¡Por favor, mantén la app en primer plano!",
|
"keepForeground": "¡Por favor, mantén la app en primer plano!",
|
||||||
"keepStatusWhenErr": "Mantener el estado anterior del servidor",
|
"keepStatusWhenErr": "Mantener el estado anterior del servidor",
|
||||||
"keepStatusWhenErrTip": "Solo aplica cuando hay errores al ejecutar scripts",
|
"keepStatusWhenErrTip": "Solo aplica cuando hay errores al ejecutar scripts",
|
||||||
"keyAuth": "Autenticación con llave",
|
"keyAuth": "Autenticación con llave",
|
||||||
"language": "Idioma",
|
"letterCache": "Caché de letras",
|
||||||
"languageName": "Español",
|
"letterCacheTip": "Recomendado desactivar, pero después de desactivarlo, no se podrán ingresar caracteres CJK.",
|
||||||
"lastTry": "Último intento",
|
|
||||||
"launchPage": "Página de lanzamiento",
|
|
||||||
"license": "Licencia de código abierto",
|
"license": "Licencia de código abierto",
|
||||||
"light": "Claro",
|
|
||||||
"loadingFiles": "Cargando directorio...",
|
|
||||||
"location": "Ubicación",
|
"location": "Ubicación",
|
||||||
"log": "Registro",
|
|
||||||
"loss": "Tasa de pérdida",
|
"loss": "Tasa de pérdida",
|
||||||
"madeWithLove": "Hecho con ❤️ por {myGithub}",
|
"madeWithLove": "Hecho con ❤️ por {myGithub}",
|
||||||
"manual": "Manual",
|
"manual": "Manual",
|
||||||
@@ -163,60 +94,36 @@
|
|||||||
"maxRetryCount": "Número máximo de reintentos de conexión al servidor",
|
"maxRetryCount": "Número máximo de reintentos de conexión al servidor",
|
||||||
"maxRetryCountEqual0": "Reintentará infinitamente",
|
"maxRetryCountEqual0": "Reintentará infinitamente",
|
||||||
"min": "Mínimo",
|
"min": "Mínimo",
|
||||||
"minute": "Minuto",
|
|
||||||
"mission": "Misión",
|
"mission": "Misión",
|
||||||
"more": "Más",
|
"more": "Más",
|
||||||
"moveOutServerFuncBtnsHelp": "Activado: se mostrará debajo de cada tarjeta en la página de servidores. Desactivado: se mostrará en la parte superior de los detalles del servidor.",
|
"moveOutServerFuncBtnsHelp": "Activado: se mostrará debajo de cada tarjeta en la página de servidores. Desactivado: se mostrará en la parte superior de los detalles del servidor.",
|
||||||
"ms": "milisegundos",
|
"ms": "milisegundos",
|
||||||
"name": "Nombre",
|
|
||||||
"needHomeDir": "Si eres usuario de Synology, [consulta aquí](https://kb.synology.com/DSM/tutorial/user_enable_home_service). Los usuarios de otros sistemas deben buscar cómo crear un directorio home.",
|
"needHomeDir": "Si eres usuario de Synology, [consulta aquí](https://kb.synology.com/DSM/tutorial/user_enable_home_service). Los usuarios de otros sistemas deben buscar cómo crear un directorio home.",
|
||||||
"needRestart": "Necesita reiniciar la app",
|
"needRestart": "Necesita reiniciar la app",
|
||||||
"net": "Red",
|
"net": "Red",
|
||||||
"netViewType": "Tipo de vista de red",
|
"netViewType": "Tipo de vista de red",
|
||||||
"newContainer": "Crear contenedor nuevo",
|
"newContainer": "Crear contenedor nuevo",
|
||||||
"noClient": "No hay conexión SSH",
|
|
||||||
"noInterface": "No hay interfaz disponible",
|
|
||||||
"noLineChart": "No utilice gráficos de líneas",
|
"noLineChart": "No utilice gráficos de líneas",
|
||||||
"noNotiPerm": "Sin permisos de notificación, posiblemente sin indicación de progreso al descargar actualizaciones de la aplicación.",
|
"noLineChartForCpu": "No utilice gráficos lineales para la CPU",
|
||||||
"noOptions": "Sin opciones disponibles",
|
|
||||||
"noPrivateKeyTip": "La clave privada no existe, puede haber sido eliminada o hay un error de configuración.",
|
"noPrivateKeyTip": "La clave privada no existe, puede haber sido eliminada o hay un error de configuración.",
|
||||||
"noPromptAgain": "No volver a preguntar",
|
"noPromptAgain": "No volver a preguntar",
|
||||||
"noResult": "Sin resultados",
|
|
||||||
"noSavedPrivateKey": "No hay llaves privadas guardadas.",
|
|
||||||
"noSavedSnippet": "No hay fragmentos de código guardados.",
|
|
||||||
"noServerAvailable": "No hay servidores disponibles.",
|
|
||||||
"noTask": "Sin tareas",
|
|
||||||
"noUpdateAvailable": "No hay actualizaciones disponibles",
|
|
||||||
"node": "Nodo",
|
"node": "Nodo",
|
||||||
"notAvailable": "No disponible",
|
"notAvailable": "No disponible",
|
||||||
"notSelected": "No seleccionado",
|
|
||||||
"note": "Nota",
|
|
||||||
"nullToken": "Token nulo",
|
|
||||||
"ok": "Ok",
|
|
||||||
"onServerDetailPage": "En la página de detalles del servidor",
|
"onServerDetailPage": "En la página de detalles del servidor",
|
||||||
"onlyOneLine": "Mostrar solo en una línea (desplazable)",
|
"onlyOneLine": "Mostrar solo en una línea (desplazable)",
|
||||||
"onlyWhenCoreBiggerThan8": "Efectivo solo cuando el número de núcleos > 8",
|
"onlyWhenCoreBiggerThan8": "Efectivo solo cuando el número de núcleos > 8",
|
||||||
"open": "Abrir",
|
|
||||||
"openLastPath": "Abrir el último camino",
|
"openLastPath": "Abrir el último camino",
|
||||||
"openLastPathTip": "Los diferentes servidores tendrán diferentes registros, y lo que se registra es la ruta de salida",
|
"openLastPathTip": "Los diferentes servidores tendrán diferentes registros, y lo que se registra es la ruta de salida",
|
||||||
"parseContainerStats": "Analizar estado de uso del contenedor",
|
|
||||||
"parseContainerStatsTip": "El análisis del estado de uso de Docker es bastante lento",
|
"parseContainerStatsTip": "El análisis del estado de uso de Docker es bastante lento",
|
||||||
"paste": "Pegar",
|
|
||||||
"path": "Ruta",
|
|
||||||
"percentOfSize": "El {percent}% de {size}",
|
"percentOfSize": "El {percent}% de {size}",
|
||||||
"pickFile": "Seleccionar archivo",
|
"permission": "Permisos",
|
||||||
"pingAvg": "Promedio:",
|
"pingAvg": "Promedio:",
|
||||||
"pingInputIP": "Por favor, introduce la IP de destino o el dominio",
|
"pingInputIP": "Por favor, introduce la IP de destino o el dominio",
|
||||||
"pingNoServer": "No hay servidores disponibles para hacer Ping\nPor favor, añade un servidor en la pestaña de servidores y vuelve a intentarlo",
|
"pingNoServer": "No hay servidores disponibles para hacer Ping\nPor favor, añade un servidor en la pestaña de servidores y vuelve a intentarlo",
|
||||||
"pkg": "Gestión de paquetes",
|
"pkg": "Gestión de paquetes",
|
||||||
"pkgUpgradeTip": "Realice una copia de seguridad de su sistema antes de actualizar.",
|
|
||||||
"platformNotSupportUpdate": "La plataforma actual no soporta actualizaciones, por favor instala manualmente la última versión del código fuente",
|
|
||||||
"plugInType": "Tipo de inserción",
|
"plugInType": "Tipo de inserción",
|
||||||
"plzEnterHost": "Por favor, introduce el host",
|
|
||||||
"plzSelectKey": "Por favor, selecciona una llave privada",
|
|
||||||
"port": "Puerto",
|
"port": "Puerto",
|
||||||
"preview": "Vista previa",
|
"preview": "Vista previa",
|
||||||
"primaryColorSeed": "Semilla de color primario",
|
|
||||||
"privateKey": "Llave privada",
|
"privateKey": "Llave privada",
|
||||||
"process": "Proceso",
|
"process": "Proceso",
|
||||||
"pushToken": "Token de notificaciones",
|
"pushToken": "Token de notificaciones",
|
||||||
@@ -226,15 +133,11 @@
|
|||||||
"pwd": "Contraseña",
|
"pwd": "Contraseña",
|
||||||
"read": "Leer",
|
"read": "Leer",
|
||||||
"reboot": "Reiniciar",
|
"reboot": "Reiniciar",
|
||||||
"rememberChoice": "Recordar la selección",
|
|
||||||
"rememberPwdInMem": "Recordar contraseña en la memoria",
|
"rememberPwdInMem": "Recordar contraseña en la memoria",
|
||||||
"rememberPwdInMemTip": "Utilizado para contenedores, suspensión, etc.",
|
"rememberPwdInMemTip": "Utilizado para contenedores, suspensión, etc.",
|
||||||
|
"rememberWindowSize": "Recordar el tamaño de la ventana",
|
||||||
"remotePath": "Ruta remota",
|
"remotePath": "Ruta remota",
|
||||||
"rename": "Renombrar",
|
|
||||||
"reportBugsOnGithubIssue": "Por favor, informa los problemas en {url}",
|
|
||||||
"restart": "Reiniciar",
|
"restart": "Reiniciar",
|
||||||
"restore": "Restaurar",
|
|
||||||
"restoreSuccess": "Restauración exitosa, necesitas reiniciar la App para aplicar cambios",
|
|
||||||
"result": "Resultado",
|
"result": "Resultado",
|
||||||
"rotateAngel": "Ángulo de rotación",
|
"rotateAngel": "Ángulo de rotación",
|
||||||
"route": "Enrutamiento",
|
"route": "Enrutamiento",
|
||||||
@@ -249,14 +152,8 @@
|
|||||||
"serverDetailOrder": "Orden de los componentes en la página de detalles del servidor",
|
"serverDetailOrder": "Orden de los componentes en la página de detalles del servidor",
|
||||||
"serverFuncBtns": "Botones de función del servidor",
|
"serverFuncBtns": "Botones de función del servidor",
|
||||||
"serverOrder": "Orden del servidor",
|
"serverOrder": "Orden del servidor",
|
||||||
"serverTabConnecting": "Conectando...",
|
|
||||||
"serverTabEmpty": "No hay servidores por ahora.\nHaz clic en el botón de la esquina inferior derecha para añadir uno.",
|
|
||||||
"serverTabFailed": "Fallo",
|
|
||||||
"serverTabLoading": "Cargando...",
|
|
||||||
"serverTabPlzSave": "Por favor, guarda la llave privada nuevamente",
|
|
||||||
"serverTabUnkown": "Estado desconocido",
|
|
||||||
"setting": "Configuración",
|
|
||||||
"sftpDlPrepare": "Preparando para conectar al servidor...",
|
"sftpDlPrepare": "Preparando para conectar al servidor...",
|
||||||
|
"sftpEditorTip": "Si está vacío, use el editor de archivos incorporado de la aplicación. Si hay un valor, use el editor del servidor remoto, por ejemplo, `vim` (se recomienda detectar automáticamente según `EDITOR`).",
|
||||||
"sftpRmrDirSummary": "Usar `rm -r` en SFTP para eliminar directorios",
|
"sftpRmrDirSummary": "Usar `rm -r` en SFTP para eliminar directorios",
|
||||||
"sftpSSHConnected": "SFTP conectado...",
|
"sftpSSHConnected": "SFTP conectado...",
|
||||||
"sftpShowFoldersFirst": "Mostrar carpetas primero",
|
"sftpShowFoldersFirst": "Mostrar carpetas primero",
|
||||||
@@ -271,11 +168,11 @@
|
|||||||
"sshTip": "Esta función está en fase de pruebas.\n\nPor favor, informa los problemas en {url}, o únete a nuestro desarrollo.",
|
"sshTip": "Esta función está en fase de pruebas.\n\nPor favor, informa los problemas en {url}, o únete a nuestro desarrollo.",
|
||||||
"sshVirtualKeyAutoOff": "Desactivación automática de teclas virtuales",
|
"sshVirtualKeyAutoOff": "Desactivación automática de teclas virtuales",
|
||||||
"start": "Iniciar",
|
"start": "Iniciar",
|
||||||
|
"stat": "Estadísticas",
|
||||||
"stats": "Estadísticas",
|
"stats": "Estadísticas",
|
||||||
"stop": "Detener",
|
"stop": "Detener",
|
||||||
"stopped": "Detenido",
|
"stopped": "Detenido",
|
||||||
"storage": "Almacenamiento",
|
"storage": "Almacenamiento",
|
||||||
"success": "Éxito",
|
|
||||||
"supportFmtArgs": "Soporta los siguientes argumentos de formato:",
|
"supportFmtArgs": "Soporta los siguientes argumentos de formato:",
|
||||||
"suspend": "Suspender",
|
"suspend": "Suspender",
|
||||||
"suspendTip": "La función de suspender necesita permisos de root y soporte de systemd.",
|
"suspendTip": "La función de suspender necesita permisos de root y soporte de systemd.",
|
||||||
@@ -291,35 +188,25 @@
|
|||||||
"textScaler": "Escalar texto",
|
"textScaler": "Escalar texto",
|
||||||
"textScalerTip": "1.0 => 100% (tamaño original), solo afecta a ciertas fuentes en la página del servidor, no se recomienda modificar.",
|
"textScalerTip": "1.0 => 100% (tamaño original), solo afecta a ciertas fuentes en la página del servidor, no se recomienda modificar.",
|
||||||
"theme": "Tema",
|
"theme": "Tema",
|
||||||
"themeMode": "Modo de tema",
|
|
||||||
"time": "Tiempo",
|
"time": "Tiempo",
|
||||||
"times": "Veces",
|
"times": "Veces",
|
||||||
"total": "Total",
|
"total": "Total",
|
||||||
"traffic": "Tráfico",
|
"traffic": "Tráfico",
|
||||||
"trySudo": "Intentar con sudo",
|
"trySudo": "Intentar con sudo",
|
||||||
"ttl": "Tiempo de vida (TTL)",
|
"ttl": "TTL",
|
||||||
"unknown": "Desconocido",
|
"unknown": "Desconocido",
|
||||||
"unknownError": "Error desconocido",
|
|
||||||
"unkownConvertMode": "Modo de conversión desconocido",
|
"unkownConvertMode": "Modo de conversión desconocido",
|
||||||
"update": "Actualizar",
|
"update": "Actualizar",
|
||||||
"updateAll": "Actualizar todo",
|
|
||||||
"updateIntervalEqual0": "Si configuras esto a 0, el estado del servidor no se refrescará automáticamente.\nY no se podrá calcular el uso de CPU.",
|
"updateIntervalEqual0": "Si configuras esto a 0, el estado del servidor no se refrescará automáticamente.\nY no se podrá calcular el uso de CPU.",
|
||||||
"updateServerStatusInterval": "Intervalo de actualización del estado del servidor",
|
"updateServerStatusInterval": "Intervalo de actualización del estado del servidor",
|
||||||
"updateTip": "Nueva versión: v1.0.{newest}",
|
|
||||||
"updateTipTooLow": "La versión actual es demasiado baja, por favor actualiza a v1.0.{newest}",
|
|
||||||
"upload": "Subir",
|
"upload": "Subir",
|
||||||
"upsideDown": "Invertir arriba por abajo",
|
"upsideDown": "Invertir arriba por abajo",
|
||||||
"uptime": "Tiempo de actividad",
|
"uptime": "Tiempo de actividad",
|
||||||
"urlOrJson": "URL o JSON",
|
|
||||||
"useCdn": "Usando CDN",
|
"useCdn": "Usando CDN",
|
||||||
"useCdnTip": "Se recomienda a los usuarios no chinos utilizar CDN. ¿Le gustaría utilizarlo?",
|
"useCdnTip": "Se recomienda a los usuarios no chinos utilizar CDN. ¿Le gustaría utilizarlo?",
|
||||||
"useNoPwd": "Se usará sin contraseña",
|
"useNoPwd": "Se usará sin contraseña",
|
||||||
"usePodmanByDefault": "Usar Podman por defecto",
|
"usePodmanByDefault": "Usar Podman por defecto",
|
||||||
"used": "Usado",
|
"used": "Usado",
|
||||||
"user": "Usuario",
|
|
||||||
"versionHaveUpdate": "Nueva versión encontrada: v1.0.{build}, haz clic para actualizar",
|
|
||||||
"versionUnknownUpdate": "Actual: v1.0.{build}, haz clic para verificar actualizaciones",
|
|
||||||
"versionUpdated": "Actual: v1.0.{build}, ya estás en la última versión",
|
|
||||||
"view": "Vista",
|
"view": "Vista",
|
||||||
"viewErr": "Ver error",
|
"viewErr": "Ver error",
|
||||||
"virtKeyHelpClipboard": "Si el terminal tiene caracteres seleccionados, entonces copiará los caracteres seleccionados al portapapeles, de lo contrario, pegará el contenido del portapapeles al terminal.",
|
"virtKeyHelpClipboard": "Si el terminal tiene caracteres seleccionados, entonces copiará los caracteres seleccionados al portapapeles, de lo contrario, pegará el contenido del portapapeles al terminal.",
|
||||||
@@ -330,8 +217,8 @@
|
|||||||
"watchNotPaired": "No hay un Apple Watch emparejado",
|
"watchNotPaired": "No hay un Apple Watch emparejado",
|
||||||
"webdavSettingEmpty": "La configuración de Webdav está vacía",
|
"webdavSettingEmpty": "La configuración de Webdav está vacía",
|
||||||
"whenOpenApp": "Al abrir la App",
|
"whenOpenApp": "Al abrir la App",
|
||||||
"willTakEeffectImmediately": "Los cambios tendrán efecto inmediatamente",
|
|
||||||
"wolTip": "Después de configurar WOL (Wake-on-LAN), se envía una solicitud de WOL cada vez que se conecta el servidor.",
|
"wolTip": "Después de configurar WOL (Wake-on-LAN), se envía una solicitud de WOL cada vez que se conecta el servidor.",
|
||||||
"write": "Escribir",
|
"write": "Escribir",
|
||||||
"writeScriptFailTip": "La escritura en el script falló, posiblemente por falta de permisos o porque el directorio no existe."
|
"writeScriptFailTip": "La escritura en el script falló, posiblemente por falta de permisos o porque el directorio no existe.",
|
||||||
|
"writeScriptTip": "Después de conectarse al servidor, se escribirá un script en ~/.config/server_box para monitorear el estado del sistema. Puedes revisar el contenido del script."
|
||||||
}
|
}
|
||||||