Compare commits
78 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
966a60a82d | ||
|
|
76e98c6468 | ||
|
|
d7ae8b75b8 | ||
|
|
b5329e2692 | ||
|
|
ef297673f3 | ||
|
|
7558b4806d | ||
|
|
f7ef8a3915 | ||
|
|
38366a2ef3 | ||
|
|
7e5bb54c98 | ||
|
|
7ce3854392 | ||
|
|
195ddd2bcc | ||
|
|
267b0b0a69 | ||
|
|
41e3fcb23a | ||
|
|
46d5840276 | ||
|
|
fe566e97ca | ||
|
|
ddd1524d63 | ||
|
|
4d8268c614 | ||
|
|
568b97606a | ||
|
|
42cc2416a1 | ||
|
|
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 |
1
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
custom: ['https://cdn.lpkt.cn/donate']
|
||||||
30
.github/workflows/release.yml
vendored
@@ -9,19 +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:
|
with:
|
||||||
channel: 'stable'
|
channel: 'stable'
|
||||||
flutter-version: '3.22.2'
|
flutter-version: '3.24.0'
|
||||||
- uses: actions/setup-java@v4
|
- uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
distribution: 'zulu'
|
distribution: 'zulu'
|
||||||
@@ -31,7 +29,7 @@ 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
|
- name: Rename for fdroid
|
||||||
run: |
|
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 }}_arm64.apk build/app/outputs/flutter-apk/${{ env.APP_NAME }}_v1.0.${{ env.BUILD_NUMBER }}_arm64.apk
|
||||||
@@ -44,6 +42,24 @@ jobs:
|
|||||||
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 }}_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 }}_arm.apk
|
||||||
build/app/outputs/flutter-apk/${{ env.APP_NAME }}_v1.0.${{ env.BUILD_NUMBER }}_amd64.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
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
with:
|
||||||
|
files: |
|
||||||
${{ 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 }}
|
||||||
@@ -54,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
|
||||||
|
|||||||
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"
|
||||||
// ]
|
// ]
|
||||||
|
|||||||
71
README.md
@@ -2,10 +2,11 @@ English | [简体中文](README_zh.md)
|
|||||||
|
|
||||||
<h2 align="center">Flutter Server Box</h2>
|
<h2 align="center">Flutter Server Box</h2>
|
||||||
|
|
||||||
<p align="center">
|
<div align="center">
|
||||||
<img alt="lang" src="https://img.shields.io/badge/lang-dart-pink">
|
<a href="https://cdn.lpkt.cn/donate"><img alt="donate" src="https://img.shields.io/badge/donate-me-pink"></a>
|
||||||
<img alt="license" src="https://img.shields.io/badge/license-GPLv3-pink">
|
<img alt="lang" src="https://img.shields.io/badge/lang-dart-cyan">
|
||||||
</p>
|
<img alt="license" src="https://img.shields.io/badge/license-GPLv3-yellow">
|
||||||
|
</div>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
A Flutter project which provide charts to display <a href="../../issues/43">Linux</a> server status and tools to manage server.
|
A Flutter project which provide charts to display <a href="../../issues/43">Linux</a> server status and tools to manage server.
|
||||||
@@ -14,39 +15,41 @@ Especially thanks to <a href="https://github.com/TerminalStudio/dartssh2">dartss
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
||||||
## ⬇️ Download
|
## 🏙️ Screenshots
|
||||||
🎉 **The `Android / Linux / Windows` version are now built via GitHub Actions**
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td><img width="200px" src="https://cdn.lpkt.cn/serverbox/screenshot/1.jpg"></td>
|
||||||
|
<td><img width="200px" src="https://cdn.lpkt.cn/serverbox/screenshot/2.jpg"></td>
|
||||||
|
<td><img width="200px" src="https://cdn.lpkt.cn/serverbox/screenshot/3.jpg"></td>
|
||||||
|
<td><img width="200px" src="https://cdn.lpkt.cn/serverbox/screenshot/4.jpg"></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
[iOS & macOS](https://apps.apple.com/app/id1586449703) / [Android & Linux & Windows](https://github.com/lollipopkit/flutter_server_box/releases)
|
|
||||||
|
|
||||||
- All deprecated versions before `v930` can be found in [here](https://cdn.lolli.tech/serverbox/?sort=time&order=desc&layout=grid).
|
## 📥 Install
|
||||||
- To prevent injection attacks and etc., please don't download from untrusted sources. eg: Gitee release is not related to this project.
|
|
||||||
|
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**!
|
||||||
|
|
||||||
|
|
||||||
## 🔖 Feature
|
## 🔖 Feature
|
||||||
- `Status chart` (CPU, Sensors, GPU...), `SSH` Term, `SFTP`, `Docker & Pkg & Process`...
|
- `Status chart` (CPU, Sensors, GPU...), `SSH` Term, `SFTP`, `Docker & Process & Systemd`...
|
||||||
- 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
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<td><img width="277px" src="imgs/server.png"></td>
|
|
||||||
<td><img width="277px" src="imgs/detail.png"></td>
|
|
||||||
<td><img width="277px" src="imgs/sftp.png"></td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<td><img width="277px" src="imgs/editor.png"> </td>
|
|
||||||
<td><img width="277px" src="imgs/ssh.png"></td>
|
|
||||||
<td><img width="277px" src="imgs/docker.png"></td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
|
|
||||||
## 🆘 Help
|
## 🆘 Help
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
<a href="https://t.me/lpktg"><img alt="donate" src="https://img.shields.io/badge/Telegram-lpktg-green"></a>
|
||||||
|
<a href="https://discord.gg/SsVNbRhK7w"><img alt="discord" src="https://img.shields.io/badge/Discord-lpkt-purple"></a>
|
||||||
|
</div>
|
||||||
|
|
||||||
- In order to push server status to your portable device without opening ServerBox app (Such as **message push** and **home widget**), you need to install [ServerBoxMonitor](https://github.com/lollipopkit/server_box_monitor) on your servers, and config it correctly. See [wiki](https://github.com/lollipopkit/server_box_monitor/wiki) for more details.
|
- In order to push server status to your portable device without opening ServerBox app (Such as **message push** and **home widget**), you need to install [ServerBoxMonitor](https://github.com/lollipopkit/server_box_monitor) on your servers, and config it correctly. See [wiki](https://github.com/lollipopkit/server_box_monitor/wiki) for more details.
|
||||||
- **Common issues** can be found in [app wiki](https://github.com/lollipopkit/flutter_server_box/wiki).
|
- **Common issues** can be found in [app wiki](https://github.com/lollipopkit/flutter_server_box/wiki).
|
||||||
|
|
||||||
@@ -59,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
|
||||||
|
|||||||
76
README_zh.md
@@ -2,10 +2,11 @@
|
|||||||
|
|
||||||
<h2 align="center">Flutter Server Box</h2>
|
<h2 align="center">Flutter Server Box</h2>
|
||||||
|
|
||||||
<p align="center">
|
<div align="center">
|
||||||
<img alt="lang" src="https://img.shields.io/badge/lang-dart-pink">
|
<a href="https://cdn.lpkt.cn/donate"><img alt="donate" src="https://img.shields.io/badge/捐赠-我-pink"></a>
|
||||||
<img alt="license" src="https://img.shields.io/badge/license-GPLv3-pink">
|
<img alt="语言" src="https://img.shields.io/badge/语言-dart-cyan">
|
||||||
</p>
|
<img alt="license" src="https://img.shields.io/badge/证书-GPLv3-yellow">
|
||||||
|
</div>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
使用 Flutter 开发的 <a href="../../issues/43">Linux</a> 服务器工具箱,提供服务器状态图表和管理工具。
|
使用 Flutter 开发的 <a href="../../issues/43">Linux</a> 服务器工具箱,提供服务器状态图表和管理工具。
|
||||||
@@ -14,59 +15,64 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
||||||
## ⬇️ Download
|
## 🏙️ 截屏
|
||||||
🎉 **现在 `Android / Linux / Windows` 版本使用 GitHub Actions 构建**。
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td><img width="200px" src="https://cdn.lpkt.cn/serverbox/screenshot/1.jpg"></td>
|
||||||
|
<td><img width="200px" src="https://cdn.lpkt.cn/serverbox/screenshot/2.jpg"></td>
|
||||||
|
<td><img width="200px" src="https://cdn.lpkt.cn/serverbox/screenshot/3.jpg"></td>
|
||||||
|
<td><img width="200px" src="https://cdn.lpkt.cn/serverbox/screenshot/4.jpg"></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
[iOS & macOS](https://apps.apple.com/app/id1586449703) / [Android & Linux & Windows](https://github.com/lollipopkit/flutter_server_box/releases)
|
|
||||||
|
|
||||||
- 所有 `v930` 之前的版本可以在 [这里](https://cdn.lolli.tech/serverbox/?sort=time&order=desc&layout=grid) 找到。
|
## 📥 安装
|
||||||
- 为了防止注入攻击等,请不要从不受信任的来源下载。例如:Gitee 的发行包与该项目无关。
|
|
||||||
|
平台 | 下载
|
||||||
|
--- | ---
|
||||||
|
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)
|
||||||
|
|
||||||
|
请从 **信任** 的来源下载!
|
||||||
|
|
||||||
|
|
||||||
## 🔖 特点
|
## 🔖 特点
|
||||||
- `状态图表`(CPU、传感器、GPU 等), `SSH` 终端, `SFTP`, `Docker & 包 & 进程` 管理器...
|
- `状态图表`(CPU、传感器、GPU 等), `SSH` 终端, `SFTP`, `Docker & 进程 & Systemd` 管理...
|
||||||
- 特殊支持:`生物认证`、`推送`、`桌面小部件`、`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>
|
|
||||||
<tr>
|
|
||||||
<td><img width="277px" src="imgs/server.png"></td>
|
|
||||||
<td><img width="277px" src="imgs/detail.png"></td>
|
|
||||||
<td><img width="277px" src="imgs/sftp.png"></td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<td><img width="277px" src="imgs/editor.png"> </td>
|
|
||||||
<td><img width="277px" src="imgs/ssh.png"></td>
|
|
||||||
<td><img width="277px" src="imgs/docker.png"></td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
|
|
||||||
## 🆘 帮助
|
## 🆘 帮助
|
||||||
|
|
||||||
- 吹水、参与开发、了解如何使用,QQ群 **762870488**
|
<div align="center">
|
||||||
- 为了可以在不使用 ServerBox app 时获取服务器状态(例如:桌面小部件、推送服务),你需要在你的服务器上安装 [ServerBoxMonitor](https://github.com/lollipopkit/server_box_monitor),并且正确配置,详情可见 [wiki](https://github.com/lollipopkit/server_box_monitor/wiki/%E4%B8%BB%E9%A1%B5)。
|
<a href="https://t.me/lpktg"><img alt="donate" src="https://img.shields.io/badge/Telegram-lpktg-green"></a>
|
||||||
- **常见问题**可以在 [app wiki](https://github.com/lollipopkit/flutter_server_box/wiki/主页) 查看。
|
<a href="https://discord.gg/SsVNbRhK7w"><img alt="discord" src="https://img.shields.io/badge/Discord-lpkt-purple"></a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
- 为了可以在不使用 ServerBox app 时获取服务器状态(例如:桌面小部件、推送服务),你需要在你的服务器上安装 [ServerBoxMonitor](https://github.com/lollipopkit/server_box_monitor),详情见 [wiki](https://github.com/lollipopkit/server_box_monitor/wiki/%E4%B8%BB%E9%A1%B5)。
|
||||||
|
- **常见问题** 可以在 [app wiki](https://github.com/lollipopkit/flutter_server_box/wiki/主页) 查看。
|
||||||
|
|
||||||
反馈前须知:
|
反馈前须知:
|
||||||
1. 反馈问题请附带 log(点击首页右上角),并以 bug 模版提交。
|
1. 反馈问题请附带 log(点击首页右上角),并以 bug 模版提交。
|
||||||
2. 反馈问题前请检查是否是 serverbox 的问题。
|
2. 反馈问题前请检查是否是 serverbox 的问题。
|
||||||
3. 欢迎所有有效、正面的反馈,主观(比如你觉得其他UI更好看)的反馈不一定会接受
|
3. 欢迎所有有效、正面的反馈,主观(比如你觉得其他UI更好看)的反馈不一定会接受
|
||||||
|
|
||||||
确认了解上述内容后,请在 [问题](https://github.com/lollipopkit/flutter_server_box/issues/new) 中反馈。
|
|
||||||
|
|
||||||
|
|
||||||
## 🧱 贡献
|
## 🧱 贡献
|
||||||
- 任何正面的贡献都欢迎。
|
任何正面的贡献都欢迎。
|
||||||
- [本地化翻译指南](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 的 第三方全平台客户端。
|
||||||
|
|||||||
@@ -30,14 +30,19 @@ linter:
|
|||||||
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
|
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
|
||||||
# producing the lint.
|
# producing the lint.
|
||||||
rules:
|
rules:
|
||||||
library_private_types_in_public_api: false
|
library_private_types_in_public_api: true
|
||||||
use_build_context_synchronously: false
|
use_build_context_synchronously: false
|
||||||
depend_on_referenced_packages: false
|
depend_on_referenced_packages: false
|
||||||
prefer_final_locals: true
|
prefer_final_locals: true
|
||||||
unnecessary_parenthesis: true
|
unnecessary_parenthesis: true
|
||||||
implicit_call_tearoffs: true
|
implicit_call_tearoffs: true
|
||||||
|
always_declare_return_types: true
|
||||||
|
always_use_package_imports: true
|
||||||
|
annotate_overrides: true
|
||||||
|
avoid_empty_else: true
|
||||||
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
||||||
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
||||||
|
avoid_return_types_on_setters: true
|
||||||
|
|
||||||
# Additional information about this file can be found at
|
# Additional information about this file can be found at
|
||||||
# https://dart.dev/guides/language/analysis-options
|
# https://dart.dev/guides/language/analysis-options
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
BIN
imgs/detail.png
|
Before Width: | Height: | Size: 135 KiB |
BIN
imgs/docker.png
|
Before Width: | Height: | Size: 115 KiB |
BIN
imgs/editor.png
|
Before Width: | Height: | Size: 173 KiB |
|
Before Width: | Height: | Size: 112 KiB |
BIN
imgs/server.png
|
Before Width: | Height: | Size: 135 KiB |
BIN
imgs/sftp.png
|
Before Width: | Height: | Size: 135 KiB |
BIN
imgs/ssh.png
|
Before Width: | Height: | Size: 144 KiB |
105
ios/Podfile.lock
@@ -1,6 +1,4 @@
|
|||||||
PODS:
|
PODS:
|
||||||
- device_info_plus (0.0.1):
|
|
||||||
- Flutter
|
|
||||||
- file_picker (0.0.1):
|
- file_picker (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- Flutter (1.0.0)
|
- Flutter (1.0.0)
|
||||||
@@ -8,19 +6,71 @@ PODS:
|
|||||||
- Flutter
|
- Flutter
|
||||||
- flutter_native_splash (0.0.1):
|
- flutter_native_splash (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
|
- GoogleDataTransport (9.4.1):
|
||||||
|
- GoogleUtilities/Environment (~> 7.7)
|
||||||
|
- nanopb (< 2.30911.0, >= 2.30908.0)
|
||||||
|
- PromisesObjC (< 3.0, >= 1.2)
|
||||||
|
- GoogleMLKit/BarcodeScanning (6.0.0):
|
||||||
|
- GoogleMLKit/MLKitCore
|
||||||
|
- MLKitBarcodeScanning (~> 5.0.0)
|
||||||
|
- GoogleMLKit/MLKitCore (6.0.0):
|
||||||
|
- MLKitCommon (~> 11.0.0)
|
||||||
|
- GoogleToolboxForMac/Defines (4.2.1)
|
||||||
|
- GoogleToolboxForMac/Logger (4.2.1):
|
||||||
|
- GoogleToolboxForMac/Defines (= 4.2.1)
|
||||||
|
- "GoogleToolboxForMac/NSData+zlib (4.2.1)":
|
||||||
|
- GoogleToolboxForMac/Defines (= 4.2.1)
|
||||||
|
- GoogleUtilities/Environment (7.13.3):
|
||||||
|
- GoogleUtilities/Privacy
|
||||||
|
- PromisesObjC (< 3.0, >= 1.2)
|
||||||
|
- GoogleUtilities/Logger (7.13.3):
|
||||||
|
- GoogleUtilities/Environment
|
||||||
|
- GoogleUtilities/Privacy
|
||||||
|
- GoogleUtilities/Privacy (7.13.3)
|
||||||
|
- GoogleUtilities/UserDefaults (7.13.3):
|
||||||
|
- GoogleUtilities/Logger
|
||||||
|
- GoogleUtilities/Privacy
|
||||||
|
- GoogleUtilitiesComponents (1.1.0):
|
||||||
|
- GoogleUtilities/Logger
|
||||||
|
- GTMSessionFetcher/Core (3.5.0)
|
||||||
- icloud_storage (0.0.1):
|
- icloud_storage (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- local_auth_darwin (0.0.1):
|
- local_auth_darwin (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
|
- FlutterMacOS
|
||||||
|
- MLImage (1.0.0-beta5)
|
||||||
|
- MLKitBarcodeScanning (5.0.0):
|
||||||
|
- MLKitCommon (~> 11.0)
|
||||||
|
- MLKitVision (~> 7.0)
|
||||||
|
- MLKitCommon (11.0.0):
|
||||||
|
- GoogleDataTransport (< 10.0, >= 9.4.1)
|
||||||
|
- GoogleToolboxForMac/Logger (< 5.0, >= 4.2.1)
|
||||||
|
- "GoogleToolboxForMac/NSData+zlib (< 5.0, >= 4.2.1)"
|
||||||
|
- GoogleUtilities/UserDefaults (< 8.0, >= 7.13.0)
|
||||||
|
- GoogleUtilitiesComponents (~> 1.0)
|
||||||
|
- GTMSessionFetcher/Core (< 4.0, >= 3.3.2)
|
||||||
|
- MLKitVision (7.0.0):
|
||||||
|
- GoogleToolboxForMac/Logger (< 5.0, >= 4.2.1)
|
||||||
|
- "GoogleToolboxForMac/NSData+zlib (< 5.0, >= 4.2.1)"
|
||||||
|
- GTMSessionFetcher/Core (< 4.0, >= 3.3.2)
|
||||||
|
- MLImage (= 1.0.0-beta5)
|
||||||
|
- MLKitCommon (~> 11.0)
|
||||||
|
- mobile_scanner (5.1.1):
|
||||||
|
- Flutter
|
||||||
|
- GoogleMLKit/BarcodeScanning (~> 6.0.0)
|
||||||
|
- nanopb (2.30910.0):
|
||||||
|
- nanopb/decode (= 2.30910.0)
|
||||||
|
- nanopb/encode (= 2.30910.0)
|
||||||
|
- nanopb/decode (2.30910.0)
|
||||||
|
- nanopb/encode (2.30910.0)
|
||||||
- package_info_plus (0.4.5):
|
- package_info_plus (0.4.5):
|
||||||
- Flutter
|
- Flutter
|
||||||
- path_provider_foundation (0.0.1):
|
- path_provider_foundation (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- permission_handler_apple (9.3.0):
|
|
||||||
- Flutter
|
|
||||||
- plain_notification_token (0.0.1):
|
- plain_notification_token (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
|
- PromisesObjC (2.4.0)
|
||||||
- share_plus (0.0.1):
|
- share_plus (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- shared_preferences_foundation (0.0.1):
|
- shared_preferences_foundation (0.0.1):
|
||||||
@@ -32,28 +82,43 @@ PODS:
|
|||||||
- Flutter
|
- Flutter
|
||||||
- watch_connectivity (0.0.1):
|
- watch_connectivity (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
|
- webview_flutter_wkwebview (0.0.1):
|
||||||
|
- Flutter
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
- 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`)
|
||||||
- flutter_background_service_ios (from `.symlinks/plugins/flutter_background_service_ios/ios`)
|
- flutter_background_service_ios (from `.symlinks/plugins/flutter_background_service_ios/ios`)
|
||||||
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
|
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
|
||||||
- icloud_storage (from `.symlinks/plugins/icloud_storage/ios`)
|
- icloud_storage (from `.symlinks/plugins/icloud_storage/ios`)
|
||||||
- local_auth_darwin (from `.symlinks/plugins/local_auth_darwin/darwin`)
|
- local_auth_darwin (from `.symlinks/plugins/local_auth_darwin/darwin`)
|
||||||
|
- mobile_scanner (from `.symlinks/plugins/mobile_scanner/ios`)
|
||||||
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
||||||
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
||||||
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
|
||||||
- plain_notification_token (from `.symlinks/plugins/plain_notification_token/ios`)
|
- plain_notification_token (from `.symlinks/plugins/plain_notification_token/ios`)
|
||||||
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
||||||
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||||
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
||||||
- wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`)
|
- wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`)
|
||||||
- watch_connectivity (from `.symlinks/plugins/watch_connectivity/ios`)
|
- watch_connectivity (from `.symlinks/plugins/watch_connectivity/ios`)
|
||||||
|
- webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/ios`)
|
||||||
|
|
||||||
|
SPEC REPOS:
|
||||||
|
trunk:
|
||||||
|
- GoogleDataTransport
|
||||||
|
- GoogleMLKit
|
||||||
|
- GoogleToolboxForMac
|
||||||
|
- GoogleUtilities
|
||||||
|
- GoogleUtilitiesComponents
|
||||||
|
- GTMSessionFetcher
|
||||||
|
- MLImage
|
||||||
|
- MLKitBarcodeScanning
|
||||||
|
- MLKitCommon
|
||||||
|
- MLKitVision
|
||||||
|
- nanopb
|
||||||
|
- PromisesObjC
|
||||||
|
|
||||||
EXTERNAL SOURCES:
|
EXTERNAL SOURCES:
|
||||||
device_info_plus:
|
|
||||||
:path: ".symlinks/plugins/device_info_plus/ios"
|
|
||||||
file_picker:
|
file_picker:
|
||||||
:path: ".symlinks/plugins/file_picker/ios"
|
:path: ".symlinks/plugins/file_picker/ios"
|
||||||
Flutter:
|
Flutter:
|
||||||
@@ -66,12 +131,12 @@ EXTERNAL SOURCES:
|
|||||||
:path: ".symlinks/plugins/icloud_storage/ios"
|
:path: ".symlinks/plugins/icloud_storage/ios"
|
||||||
local_auth_darwin:
|
local_auth_darwin:
|
||||||
:path: ".symlinks/plugins/local_auth_darwin/darwin"
|
:path: ".symlinks/plugins/local_auth_darwin/darwin"
|
||||||
|
mobile_scanner:
|
||||||
|
:path: ".symlinks/plugins/mobile_scanner/ios"
|
||||||
package_info_plus:
|
package_info_plus:
|
||||||
:path: ".symlinks/plugins/package_info_plus/ios"
|
:path: ".symlinks/plugins/package_info_plus/ios"
|
||||||
path_provider_foundation:
|
path_provider_foundation:
|
||||||
:path: ".symlinks/plugins/path_provider_foundation/darwin"
|
:path: ".symlinks/plugins/path_provider_foundation/darwin"
|
||||||
permission_handler_apple:
|
|
||||||
:path: ".symlinks/plugins/permission_handler_apple/ios"
|
|
||||||
plain_notification_token:
|
plain_notification_token:
|
||||||
:path: ".symlinks/plugins/plain_notification_token/ios"
|
:path: ".symlinks/plugins/plain_notification_token/ios"
|
||||||
share_plus:
|
share_plus:
|
||||||
@@ -84,24 +149,38 @@ EXTERNAL SOURCES:
|
|||||||
:path: ".symlinks/plugins/wakelock_plus/ios"
|
:path: ".symlinks/plugins/wakelock_plus/ios"
|
||||||
watch_connectivity:
|
watch_connectivity:
|
||||||
:path: ".symlinks/plugins/watch_connectivity/ios"
|
:path: ".symlinks/plugins/watch_connectivity/ios"
|
||||||
|
webview_flutter_wkwebview:
|
||||||
|
:path: ".symlinks/plugins/webview_flutter_wkwebview/ios"
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
device_info_plus: 97af1d7e84681a90d0693e63169a5d50e0839a0d
|
|
||||||
file_picker: c79185e70b9b45728cde2a8d8da454e0cb43f287
|
file_picker: c79185e70b9b45728cde2a8d8da454e0cb43f287
|
||||||
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
||||||
flutter_background_service_ios: e30e0d3ee69e4cee66272d0c78eacd48c2e94aac
|
flutter_background_service_ios: e30e0d3ee69e4cee66272d0c78eacd48c2e94aac
|
||||||
flutter_native_splash: edf599c81f74d093a4daf8e17bd7a018854bc778
|
flutter_native_splash: edf599c81f74d093a4daf8e17bd7a018854bc778
|
||||||
|
GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a
|
||||||
|
GoogleMLKit: 97ac7af399057e99182ee8edfa8249e3226a4065
|
||||||
|
GoogleToolboxForMac: d1a2cbf009c453f4d6ded37c105e2f67a32206d8
|
||||||
|
GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15
|
||||||
|
GoogleUtilitiesComponents: 679b2c881db3b615a2777504623df6122dd20afe
|
||||||
|
GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6
|
||||||
icloud_storage: d9ac7a33ced81df08ba7ea1bf3099cc0ee58f60a
|
icloud_storage: d9ac7a33ced81df08ba7ea1bf3099cc0ee58f60a
|
||||||
local_auth_darwin: 4d56c90c2683319835a61274b57620df9c4520ab
|
local_auth_darwin: 66e40372f1c29f383a314c738c7446e2f7fdadc3
|
||||||
|
MLImage: 1824212150da33ef225fbd3dc49f184cf611046c
|
||||||
|
MLKitBarcodeScanning: 10ca0845a6d15f2f6e911f682a1998b68b973e8b
|
||||||
|
MLKitCommon: afec63980417d29ffbb4790529a1b0a2291699e1
|
||||||
|
MLKitVision: e858c5f125ecc288e4a31127928301eaba9ae0c1
|
||||||
|
mobile_scanner: 8564358885a9253c43f822435b70f9345c87224f
|
||||||
|
nanopb: 438bc412db1928dac798aa6fd75726007be04262
|
||||||
package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c
|
package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c
|
||||||
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
|
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
|
||||||
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
|
|
||||||
plain_notification_token: b36467dc91939a7b6754267c701bbaca14996ee1
|
plain_notification_token: b36467dc91939a7b6754267c701bbaca14996ee1
|
||||||
|
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
|
||||||
share_plus: 8875f4f2500512ea181eef553c3e27dba5135aad
|
share_plus: 8875f4f2500512ea181eef553c3e27dba5135aad
|
||||||
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
|
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
|
||||||
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
|
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
|
||||||
wakelock_plus: 78ec7c5b202cab7761af8e2b2b3d0671be6c4ae1
|
wakelock_plus: 78ec7c5b202cab7761af8e2b2b3d0671be6c4ae1
|
||||||
watch_connectivity: 715eb484685e05846eab74795348a44bb2809b82
|
watch_connectivity: 715eb484685e05846eab74795348a44bb2809b82
|
||||||
|
webview_flutter_wkwebview: 2a23822e9039b7b1bc52e5add778e5d89ad488d1
|
||||||
|
|
||||||
PODFILE CHECKSUM: ec6ef69056f066e8b21a3391082f23b5ad2d37f8
|
PODFILE CHECKSUM: ec6ef69056f066e8b21a3391082f23b5ad2d37f8
|
||||||
|
|
||||||
|
|||||||
@@ -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 = 992;
|
CURRENT_PROJECT_VERSION = 1070;
|
||||||
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.992;
|
MARKETING_VERSION = 1.0.1070;
|
||||||
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 = 992;
|
CURRENT_PROJECT_VERSION = 1070;
|
||||||
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.992;
|
MARKETING_VERSION = 1.0.1070;
|
||||||
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 = 992;
|
CURRENT_PROJECT_VERSION = 1070;
|
||||||
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.992;
|
MARKETING_VERSION = 1.0.1070;
|
||||||
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 = 992;
|
CURRENT_PROJECT_VERSION = 1070;
|
||||||
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.992;
|
MARKETING_VERSION = 1.0.1070;
|
||||||
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 = 992;
|
CURRENT_PROJECT_VERSION = 1070;
|
||||||
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.992;
|
MARKETING_VERSION = 1.0.1070;
|
||||||
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 = 992;
|
CURRENT_PROJECT_VERSION = 1070;
|
||||||
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.992;
|
MARKETING_VERSION = 1.0.1070;
|
||||||
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 = 992;
|
CURRENT_PROJECT_VERSION = 1070;
|
||||||
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.992;
|
MARKETING_VERSION = 1.0.1070;
|
||||||
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 = 992;
|
CURRENT_PROJECT_VERSION = 1070;
|
||||||
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.992;
|
MARKETING_VERSION = 1.0.1070;
|
||||||
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 = 992;
|
CURRENT_PROJECT_VERSION = 1070;
|
||||||
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.992;
|
MARKETING_VERSION = 1.0.1070;
|
||||||
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;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import UIKit
|
|||||||
import WidgetKit
|
import WidgetKit
|
||||||
import Flutter
|
import Flutter
|
||||||
|
|
||||||
@UIApplicationMain
|
@main
|
||||||
@objc class AppDelegate: FlutterAppDelegate {
|
@objc class AppDelegate: FlutterAppDelegate {
|
||||||
override func application(
|
override func application(
|
||||||
_ application: UIApplication,
|
_ application: UIApplication,
|
||||||
|
|||||||
@@ -1,76 +1,81 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||||
<true/>
|
<true />
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>$(EXECUTABLE_NAME)</string>
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
<string>6.0</string>
|
<string>6.0</string>
|
||||||
<key>CFBundleLocalizations</key>
|
<key>CFBundleLocalizations</key>
|
||||||
<array>
|
<array>
|
||||||
<string>en</string>
|
<string>en</string>
|
||||||
<string>zh</string>
|
<string>zh</string>
|
||||||
</array>
|
</array>
|
||||||
<key>CFBundleName</key>
|
<key>CFBundleName</key>
|
||||||
<string>ServerBox</string>
|
<string>ServerBox</string>
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>$(MARKETING_VERSION)</string>
|
<string>$(MARKETING_VERSION)</string>
|
||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>????</string>
|
<string>????</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||||
<key>ITSAppUsesNonExemptEncryption</key>
|
<key>ITSAppUsesNonExemptEncryption</key>
|
||||||
<false/>
|
<false />
|
||||||
<key>LSRequiresIPhoneOS</key>
|
<key>LSRequiresIPhoneOS</key>
|
||||||
<true/>
|
<true />
|
||||||
<key>LSSupportsOpeningDocumentsInPlace</key>
|
<key>LSSupportsOpeningDocumentsInPlace</key>
|
||||||
<true/>
|
<true />
|
||||||
<key>NSBonjourServices</key>
|
<key>NSBonjourServices</key>
|
||||||
<array>
|
<array>
|
||||||
<string>_dartobservatory._tcp</string>
|
<string>_dartobservatory._tcp</string>
|
||||||
</array>
|
</array>
|
||||||
<key>NSFaceIDUsageDescription</key>
|
<key>NSUserActivityTypes</key>
|
||||||
<string>Required for auth</string>
|
<array>
|
||||||
<key>NSLocalNetworkUsageDescription</key>
|
<string>ConfigurationIntent</string>
|
||||||
<string>ServerBox needs to access your local network to discover and connect to your server.</string>
|
</array>
|
||||||
<key>NSUserActivityTypes</key>
|
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||||
<array>
|
<true />
|
||||||
<string>ConfigurationIntent</string>
|
<key>UIBackgroundModes</key>
|
||||||
</array>
|
<array>
|
||||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
<string>fetch</string>
|
||||||
<true/>
|
</array>
|
||||||
<key>UIBackgroundModes</key>
|
<key>UILaunchStoryboardName</key>
|
||||||
<array>
|
<string>LaunchScreen</string>
|
||||||
<string>fetch</string>
|
<key>UIMainStoryboardFile</key>
|
||||||
</array>
|
<string>Main</string>
|
||||||
<key>UILaunchStoryboardName</key>
|
<key>UIStatusBarHidden</key>
|
||||||
<string>LaunchScreen</string>
|
<false />
|
||||||
<key>UIMainStoryboardFile</key>
|
<key>UISupportedInterfaceOrientations</key>
|
||||||
<string>Main</string>
|
<array>
|
||||||
<key>UIStatusBarHidden</key>
|
<string>UIInterfaceOrientationPortrait</string>
|
||||||
<false/>
|
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||||
<key>UISupportedInterfaceOrientations</key>
|
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||||
<array>
|
</array>
|
||||||
<string>UIInterfaceOrientationPortrait</string>
|
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
<array>
|
||||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
<string>UIInterfaceOrientationPortrait</string>
|
||||||
</array>
|
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||||
<array>
|
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||||
<string>UIInterfaceOrientationPortrait</string>
|
</array>
|
||||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
<false />
|
||||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
|
||||||
</array>
|
<key>NSLocalNetworkUsageDescription</key>
|
||||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
<string>Access your local network to discover and connect to your server.</string>
|
||||||
<false/>
|
<key>NSFaceIDUsageDescription</key>
|
||||||
</dict>
|
<string>Required for auth</string>
|
||||||
</plist>
|
<key>NSCameraUsageDescription</key>
|
||||||
|
<string>Scan QR codes and etc.</string>
|
||||||
|
<key>NSPhotoLibraryUsageDescription</key>
|
||||||
|
<string>Get QR code and etc.</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -64,9 +64,14 @@
|
|||||||
<array>
|
<array>
|
||||||
<string>_dartobservatory._tcp</string>
|
<string>_dartobservatory._tcp</string>
|
||||||
</array>
|
</array>
|
||||||
|
|
||||||
<key>NSLocalNetworkUsageDescription</key>
|
<key>NSLocalNetworkUsageDescription</key>
|
||||||
<string>ServerBox needs to access your local network to discover and connect to your server.</string>
|
<string>Access your local network to discover and connect to your server.</string>
|
||||||
<key>NSFaceIDUsageDescription</key>
|
<key>NSFaceIDUsageDescription</key>
|
||||||
<string>Required for auth</string>
|
<string>Required for auth</string>
|
||||||
|
<key>NSCameraUsageDescription</key>
|
||||||
|
<string>Scan QR codes and etc.</string>
|
||||||
|
<key>NSPhotoLibraryUsageDescription</key>
|
||||||
|
<string>Get QR code and etc.</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||||
<true/>
|
<true />
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
@@ -28,13 +28,13 @@
|
|||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||||
<key>ITSAppUsesNonExemptEncryption</key>
|
<key>ITSAppUsesNonExemptEncryption</key>
|
||||||
<false/>
|
<false />
|
||||||
<key>LSRequiresIPhoneOS</key>
|
<key>LSRequiresIPhoneOS</key>
|
||||||
<true/>
|
<true />
|
||||||
<key>LSSupportsOpeningDocumentsInPlace</key>
|
<key>LSSupportsOpeningDocumentsInPlace</key>
|
||||||
<true/>
|
<true />
|
||||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||||
<true/>
|
<true />
|
||||||
<key>UIBackgroundModes</key>
|
<key>UIBackgroundModes</key>
|
||||||
<array>
|
<array>
|
||||||
<string>fetch</string>
|
<string>fetch</string>
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
<key>UIMainStoryboardFile</key>
|
<key>UIMainStoryboardFile</key>
|
||||||
<string>Main</string>
|
<string>Main</string>
|
||||||
<key>UIStatusBarHidden</key>
|
<key>UIStatusBarHidden</key>
|
||||||
<false/>
|
<false />
|
||||||
<key>UISupportedInterfaceOrientations</key>
|
<key>UISupportedInterfaceOrientations</key>
|
||||||
<array>
|
<array>
|
||||||
<string>UIInterfaceOrientationPortrait</string>
|
<string>UIInterfaceOrientationPortrait</string>
|
||||||
@@ -59,8 +59,13 @@
|
|||||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||||
</array>
|
</array>
|
||||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||||
<false/>
|
<false />
|
||||||
|
|
||||||
<key>NSFaceIDUsageDescription</key>
|
<key>NSFaceIDUsageDescription</key>
|
||||||
<string>Required for auth</string>
|
<string>Required for auth</string>
|
||||||
|
<key>NSCameraUsageDescription</key>
|
||||||
|
<string>Scan QR codes and etc.</string>
|
||||||
|
<key>NSPhotoLibraryUsageDescription</key>
|
||||||
|
<string>Get QR code and etc.</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
85
lib/app.dart
@@ -22,8 +22,22 @@ class MyApp extends StatelessWidget {
|
|||||||
listenable: RNodes.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) {
|
||||||
@@ -36,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);
|
||||||
},
|
},
|
||||||
@@ -48,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) {
|
||||||
@@ -58,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 [
|
||||||
@@ -76,41 +81,27 @@ class MyApp extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
supportedLocales: AppLocalizations.supportedLocales,
|
supportedLocales: AppLocalizations.supportedLocales,
|
||||||
localeListResolutionCallback: LocaleUtil.resolve,
|
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;
|
||||||
|
|
||||||
|
final intros = _IntroPage.builders;
|
||||||
|
if (intros.isNotEmpty) {
|
||||||
|
return _IntroPage(intros);
|
||||||
|
}
|
||||||
|
|
||||||
|
return const HomePage();
|
||||||
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildAppContent(BuildContext ctx) {
|
|
||||||
//if (Pros.app.isWearOS) return const WearHome();
|
|
||||||
return const _AppContent(
|
|
||||||
intro: _IntroPage(),
|
|
||||||
child: HomePage(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// It's used for init settings related to [BuildContext]
|
|
||||||
final class _AppContent extends StatelessWidget {
|
|
||||||
final Widget child;
|
|
||||||
final Widget intro;
|
|
||||||
|
|
||||||
const _AppContent({required this.child, required this.intro});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
context.setLibL10n();
|
|
||||||
final appL10n = AppLocalizations.of(context);
|
|
||||||
if (appL10n != null) l10n = appL10n;
|
|
||||||
|
|
||||||
final showIntro = Stores.setting.showIntro.fetch();
|
|
||||||
if (showIntro) return intro;
|
|
||||||
|
|
||||||
return child;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _setup(BuildContext context) async {
|
void _setup(BuildContext context) async {
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -5,72 +5,74 @@ import 'package:dartssh2/dartssh2.dart';
|
|||||||
import 'package:fl_lib/fl_lib.dart';
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
import '../../data/res/misc.dart';
|
import 'package:server_box/data/res/misc.dart';
|
||||||
|
|
||||||
typedef _OnStdout = void Function(String data, SSHSession session);
|
typedef OnStdout = void Function(String data, SSHSession session);
|
||||||
typedef _OnStdin = void Function(SSHSession session);
|
typedef OnStdin = void Function(SSHSession session);
|
||||||
|
|
||||||
typedef PwdRequestFunc = Future<String?> Function(String? user);
|
typedef PwdRequestFunc = Future<String?> Function(String? user);
|
||||||
|
|
||||||
extension SSHClientX on SSHClient {
|
extension SSHClientX on SSHClient {
|
||||||
Future<SSHSession> exec(
|
Future<(SSHSession, String)> exec(
|
||||||
String cmd, {
|
OnStdin onStdin, {
|
||||||
_OnStdout? onStderr,
|
String? entry,
|
||||||
_OnStdout? onStdout,
|
SSHPtyConfig? pty,
|
||||||
_OnStdin? stdin,
|
OnStdout? onStdout,
|
||||||
bool redirectToBash = false, // not working yet. do not use
|
OnStdout? onStderr,
|
||||||
|
bool stdout = true,
|
||||||
|
bool stderr = true,
|
||||||
|
Map<String, String>? env,
|
||||||
}) async {
|
}) async {
|
||||||
final session = await execute(redirectToBash ? "head -1 | bash" : cmd);
|
final session = await execute(
|
||||||
|
entry ?? 'cat | sh',
|
||||||
if (redirectToBash) {
|
pty: pty,
|
||||||
session.stdin.add("$cmd\n".uint8List);
|
environment: env,
|
||||||
}
|
);
|
||||||
|
|
||||||
|
final result = BytesBuilder(copy: false);
|
||||||
final stdoutDone = Completer<void>();
|
final stdoutDone = Completer<void>();
|
||||||
final stderrDone = Completer<void>();
|
final stderrDone = Completer<void>();
|
||||||
|
|
||||||
if (onStdout != null) {
|
session.stdout.listen(
|
||||||
session.stdout.listen(
|
(e) {
|
||||||
(e) => onStdout(e.string, session),
|
onStdout?.call(e.string, session);
|
||||||
onDone: stdoutDone.complete,
|
if (stdout) result.add(e);
|
||||||
);
|
},
|
||||||
} else {
|
onDone: stdoutDone.complete,
|
||||||
stdoutDone.complete();
|
onError: stderrDone.completeError,
|
||||||
}
|
);
|
||||||
|
|
||||||
if (onStderr != null) {
|
session.stderr.listen(
|
||||||
session.stderr.listen(
|
(e) {
|
||||||
(e) => onStderr(e.string, session),
|
onStderr?.call(e.string, session);
|
||||||
onDone: stderrDone.complete,
|
if (stderr) result.add(e);
|
||||||
);
|
},
|
||||||
} else {
|
onDone: stderrDone.complete,
|
||||||
stderrDone.complete();
|
onError: stderrDone.completeError,
|
||||||
}
|
);
|
||||||
|
|
||||||
if (stdin != null) {
|
onStdin(session);
|
||||||
stdin(session);
|
|
||||||
}
|
|
||||||
|
|
||||||
await stdoutDone.future;
|
await stdoutDone.future;
|
||||||
await stderrDone.future;
|
await stderrDone.future;
|
||||||
|
|
||||||
session.close();
|
return (session, result.takeBytes().string);
|
||||||
return session;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<int?> execWithPwd(
|
Future<int?> execWithPwd(
|
||||||
String cmd, {
|
String script, {
|
||||||
|
String? entry,
|
||||||
BuildContext? context,
|
BuildContext? context,
|
||||||
_OnStdout? onStdout,
|
OnStdout? onStdout,
|
||||||
_OnStdout? onStderr,
|
OnStdout? onStderr,
|
||||||
_OnStdin? stdin,
|
|
||||||
bool redirectToBash = false, // not working yet. do not use
|
|
||||||
required String id,
|
required String id,
|
||||||
}) async {
|
}) async {
|
||||||
var isRequestingPwd = false;
|
var isRequestingPwd = false;
|
||||||
final session = await exec(
|
final (session, _) = await exec(
|
||||||
cmd,
|
(sess) {
|
||||||
redirectToBash: redirectToBash,
|
sess.stdin.add('$script\n'.uint8List);
|
||||||
|
sess.stdin.close();
|
||||||
|
},
|
||||||
onStderr: (data, session) async {
|
onStderr: (data, session) async {
|
||||||
onStderr?.call(data, session);
|
onStderr?.call(data, session);
|
||||||
if (isRequestingPwd) return;
|
if (isRequestingPwd) return;
|
||||||
@@ -83,56 +85,38 @@ extension SSHClientX on SSHClient {
|
|||||||
? await context.showPwdDialog(title: user, id: id)
|
? await context.showPwdDialog(title: user, id: id)
|
||||||
: null;
|
: null;
|
||||||
if (pwd == null || pwd.isEmpty) {
|
if (pwd == null || pwd.isEmpty) {
|
||||||
session.kill(SSHSignal.TERM);
|
session.stdin.close();
|
||||||
} else {
|
} else {
|
||||||
session.stdin.add('$pwd\n'.uint8List);
|
session.stdin.add('$pwd\n'.uint8List);
|
||||||
}
|
}
|
||||||
isRequestingPwd = false;
|
isRequestingPwd = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onStdout: (data, sink) async {
|
onStdout: onStdout,
|
||||||
onStdout?.call(data, sink);
|
entry: entry,
|
||||||
},
|
|
||||||
stdin: stdin,
|
|
||||||
);
|
);
|
||||||
return session.exitCode;
|
return session.exitCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Uint8List> runForOutput(
|
Future<String> execForOutput(
|
||||||
String command, {
|
String script, {
|
||||||
bool runInPty = false,
|
SSHPtyConfig? pty,
|
||||||
bool stdout = true,
|
bool stdout = true,
|
||||||
bool stderr = true,
|
bool stderr = true,
|
||||||
Map<String, String>? environment,
|
String? entry,
|
||||||
Future<void> Function(SSHSession)? action,
|
Map<String, String>? env,
|
||||||
}) async {
|
}) async {
|
||||||
final session = await execute(
|
final ret = await exec(
|
||||||
command,
|
(session) {
|
||||||
pty: runInPty ? const SSHPtyConfig() : null,
|
session.stdin.add('$script\n'.uint8List);
|
||||||
environment: environment,
|
session.stdin.close();
|
||||||
|
},
|
||||||
|
pty: pty,
|
||||||
|
env: env,
|
||||||
|
stdout: stdout,
|
||||||
|
stderr: stderr,
|
||||||
|
entry: entry,
|
||||||
);
|
);
|
||||||
|
return ret.$2;
|
||||||
final result = BytesBuilder(copy: false);
|
|
||||||
final stdoutDone = Completer<void>();
|
|
||||||
final stderrDone = Completer<void>();
|
|
||||||
|
|
||||||
session.stdout.listen(
|
|
||||||
stdout ? result.add : (_) {},
|
|
||||||
onDone: stdoutDone.complete,
|
|
||||||
onError: stderrDone.completeError,
|
|
||||||
);
|
|
||||||
|
|
||||||
session.stderr.listen(
|
|
||||||
stderr ? result.add : (_) {},
|
|
||||||
onDone: stderrDone.complete,
|
|
||||||
onError: stderrDone.completeError,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (action != null) await action(session);
|
|
||||||
|
|
||||||
await stdoutDone.future;
|
|
||||||
await stderrDone.future;
|
|
||||||
|
|
||||||
return result.takeBytes();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
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:server_box/data/model/server/private_key_info.dart';
|
import 'package:server_box/data/model/server/private_key_info.dart';
|
||||||
import 'package:server_box/data/model/server/server_private_info.dart';
|
import 'package:server_box/data/model/server/server_private_info.dart';
|
||||||
import 'package:server_box/data/res/build_data.dart';
|
|
||||||
import 'package:server_box/data/res/provider.dart';
|
|
||||||
import 'package:server_box/data/res/store.dart';
|
import 'package:server_box/data/res/store.dart';
|
||||||
import 'package:server_box/view/page/backup.dart';
|
import 'package:server_box/view/page/backup.dart';
|
||||||
import 'package:server_box/view/page/container.dart';
|
import 'package:server_box/view/page/container.dart';
|
||||||
@@ -23,18 +20,18 @@ import 'package:server_box/view/page/ssh/page.dart';
|
|||||||
import 'package:server_box/view/page/setting/seq/virt_key.dart';
|
import 'package:server_box/view/page/setting/seq/virt_key.dart';
|
||||||
import 'package:server_box/view/page/storage/local.dart';
|
import 'package:server_box/view/page/storage/local.dart';
|
||||||
|
|
||||||
import '../data/model/server/snippet.dart';
|
import 'package:server_box/data/model/server/snippet.dart';
|
||||||
import '../view/page/editor.dart';
|
import 'package:server_box/view/page/editor.dart';
|
||||||
import '../view/page/process.dart';
|
import 'package:server_box/view/page/process.dart';
|
||||||
import '../view/page/server/edit.dart';
|
import 'package:server_box/view/page/server/edit.dart';
|
||||||
import '../view/page/server/tab.dart';
|
import 'package:server_box/view/page/server/tab.dart';
|
||||||
import '../view/page/setting/entry.dart';
|
import 'package:server_box/view/page/setting/entry.dart';
|
||||||
import '../view/page/setting/seq/srv_detail_seq.dart';
|
import 'package:server_box/view/page/setting/seq/srv_detail_seq.dart';
|
||||||
import '../view/page/setting/seq/srv_seq.dart';
|
import 'package:server_box/view/page/setting/seq/srv_seq.dart';
|
||||||
import '../view/page/snippet/edit.dart';
|
import 'package:server_box/view/page/snippet/edit.dart';
|
||||||
import '../view/page/snippet/list.dart';
|
import 'package:server_box/view/page/snippet/list.dart';
|
||||||
import '../view/page/storage/sftp.dart';
|
import 'package:server_box/view/page/storage/sftp.dart';
|
||||||
import '../view/page/storage/sftp_mission.dart';
|
import 'package:server_box/view/page/storage/sftp_mission.dart';
|
||||||
|
|
||||||
class AppRoutes {
|
class AppRoutes {
|
||||||
final Widget page;
|
final Widget page;
|
||||||
@@ -61,7 +58,7 @@ class AppRoutes {
|
|||||||
return Future.value(null);
|
return Future.value(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
static AppRoutes serverDetail({Key? key, required ServerPrivateInfo spi}) {
|
static AppRoutes serverDetail({Key? key, required Spi spi}) {
|
||||||
return AppRoutes(ServerDetailPage(key: key, spi: spi), 'server_detail');
|
return AppRoutes(ServerDetailPage(key: key, spi: spi), 'server_detail');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,7 +66,7 @@ class AppRoutes {
|
|||||||
return AppRoutes(ServerPage(key: key), 'server_tab');
|
return AppRoutes(ServerPage(key: key), 'server_tab');
|
||||||
}
|
}
|
||||||
|
|
||||||
static AppRoutes serverEdit({Key? key, ServerPrivateInfo? spi}) {
|
static AppRoutes serverEdit({Key? key, Spi? spi}) {
|
||||||
return AppRoutes(
|
return AppRoutes(
|
||||||
ServerEditPage(spi: spi),
|
ServerEditPage(spi: spi),
|
||||||
'server_${spi == null ? 'add' : 'edit'}',
|
'server_${spi == null ? 'add' : 'edit'}',
|
||||||
@@ -100,7 +97,7 @@ class AppRoutes {
|
|||||||
|
|
||||||
static AppRoutes ssh({
|
static AppRoutes ssh({
|
||||||
Key? key,
|
Key? key,
|
||||||
required ServerPrivateInfo spi,
|
required Spi spi,
|
||||||
String? initCmd,
|
String? initCmd,
|
||||||
Snippet? initSnippet,
|
Snippet? initSnippet,
|
||||||
}) {
|
}) {
|
||||||
@@ -135,10 +132,7 @@ class AppRoutes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static AppRoutes sftp(
|
static AppRoutes sftp(
|
||||||
{Key? key,
|
{Key? key, required Spi spi, String? initPath, bool isSelect = false}) {
|
||||||
required ServerPrivateInfo spi,
|
|
||||||
String? initPath,
|
|
||||||
bool isSelect = false}) {
|
|
||||||
return AppRoutes(
|
return AppRoutes(
|
||||||
SftpPage(
|
SftpPage(
|
||||||
key: key,
|
key: key,
|
||||||
@@ -153,21 +147,7 @@ class AppRoutes {
|
|||||||
return AppRoutes(BackupPage(key: key), 'backup');
|
return AppRoutes(BackupPage(key: key), 'backup');
|
||||||
}
|
}
|
||||||
|
|
||||||
static AppRoutes debug({Key? key}) {
|
static AppRoutes docker({Key? key, required Spi spi}) {
|
||||||
return AppRoutes(
|
|
||||||
DebugPage(
|
|
||||||
key: key,
|
|
||||||
args: DebugPageArgs(
|
|
||||||
notifier: Pros.debug.widgets,
|
|
||||||
onClear: Pros.debug.clear,
|
|
||||||
title: 'Logs(${BuildData.build})',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
'debug',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static AppRoutes docker({Key? key, required ServerPrivateInfo spi}) {
|
|
||||||
return AppRoutes(ContainerPage(key: key, spi: spi), 'docker');
|
return AppRoutes(ContainerPage(key: key, spi: spi), 'docker');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,7 +183,7 @@ class AppRoutes {
|
|||||||
return AppRoutes(PingPage(key: key), 'ping');
|
return AppRoutes(PingPage(key: key), 'ping');
|
||||||
}
|
}
|
||||||
|
|
||||||
static AppRoutes process({Key? key, required ServerPrivateInfo spi}) {
|
static AppRoutes process({Key? key, required Spi spi}) {
|
||||||
return AppRoutes(ProcessPage(key: key, spi: spi), 'process');
|
return AppRoutes(ProcessPage(key: key, spi: spi), 'process');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,7 +217,7 @@ class AppRoutes {
|
|||||||
'snippet_result');
|
'snippet_result');
|
||||||
}
|
}
|
||||||
|
|
||||||
static AppRoutes iperf({Key? key, required ServerPrivateInfo spi}) {
|
static AppRoutes iperf({Key? key, required Spi spi}) {
|
||||||
return AppRoutes(IPerfPage(key: key, spi: spi), 'iperf');
|
return AppRoutes(IPerfPage(key: key, spi: spi), 'iperf');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,11 +225,7 @@ class AppRoutes {
|
|||||||
return AppRoutes(ServerFuncBtnsOrderPage(key: key), 'server_func_btns_seq');
|
return AppRoutes(ServerFuncBtnsOrderPage(key: key), 'server_func_btns_seq');
|
||||||
}
|
}
|
||||||
|
|
||||||
static AppRoutes pve({Key? key, required ServerPrivateInfo spi}) {
|
static AppRoutes pve({Key? key, required Spi 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');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import 'package:flutter/foundation.dart';
|
|||||||
import 'package:server_box/data/model/app/error.dart';
|
import 'package:server_box/data/model/app/error.dart';
|
||||||
import 'package:server_box/data/res/store.dart';
|
import 'package:server_box/data/res/store.dart';
|
||||||
|
|
||||||
import '../../data/model/server/server_private_info.dart';
|
import 'package:server_box/data/model/server/server_private_info.dart';
|
||||||
|
|
||||||
/// Must put this func out of any Class.
|
/// Must put this func out of any Class.
|
||||||
///
|
///
|
||||||
@@ -42,7 +42,7 @@ String getPrivateKey(String id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<SSHClient> genClient(
|
Future<SSHClient> genClient(
|
||||||
ServerPrivateInfo spi, {
|
Spi spi, {
|
||||||
void Function(GenSSHClientStatus)? onStatus,
|
void Function(GenSSHClientStatus)? onStatus,
|
||||||
|
|
||||||
/// Only pass this param if using multi-threading and key login
|
/// Only pass this param if using multi-threading and key login
|
||||||
@@ -52,10 +52,10 @@ Future<SSHClient> genClient(
|
|||||||
String? jumpPrivateKey,
|
String? jumpPrivateKey,
|
||||||
Duration timeout = const Duration(seconds: 5),
|
Duration timeout = const Duration(seconds: 5),
|
||||||
|
|
||||||
/// [ServerPrivateInfo] of the jump server
|
/// [Spi] of the jump server
|
||||||
///
|
///
|
||||||
/// Must pass this param if using multi-threading and key login
|
/// Must pass this param if using multi-threading and key login
|
||||||
ServerPrivateInfo? jumpSpi,
|
Spi? jumpSpi,
|
||||||
|
|
||||||
/// Handle keyboard-interactive authentication
|
/// Handle keyboard-interactive authentication
|
||||||
FutureOr<List<String>?> Function(SSHUserInfoRequest)? onKeyboardInteractive,
|
FutureOr<List<String>?> Function(SSHUserInfoRequest)? onKeyboardInteractive,
|
||||||
@@ -95,8 +95,8 @@ Future<SSHClient> genClient(
|
|||||||
try {
|
try {
|
||||||
final ipPort = spi.fromStringUrl();
|
final ipPort = spi.fromStringUrl();
|
||||||
return await SSHSocket.connect(
|
return await SSHSocket.connect(
|
||||||
ipPort.ip,
|
ipPort.$1,
|
||||||
ipPort.port,
|
ipPort.$2,
|
||||||
timeout: timeout,
|
timeout: timeout,
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -4,16 +4,16 @@ import 'package:fl_lib/fl_lib.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:server_box/core/extension/context/locale.dart';
|
import 'package:server_box/core/extension/context/locale.dart';
|
||||||
import 'package:server_box/data/model/server/server_private_info.dart';
|
import 'package:server_box/data/model/server/server_private_info.dart';
|
||||||
import 'package:server_box/data/res/provider.dart';
|
import 'package:server_box/data/provider/app.dart';
|
||||||
|
|
||||||
abstract final class KeybordInteractive {
|
abstract final class KeybordInteractive {
|
||||||
static FutureOr<List<String>?> defaultHandle(
|
static FutureOr<List<String>?> defaultHandle(
|
||||||
ServerPrivateInfo spi, {
|
Spi spi, {
|
||||||
BuildContext? ctx,
|
BuildContext? ctx,
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
final res = await (ctx ?? Pros.app.ctx)?.showPwdDialog(
|
final res = await (ctx ?? AppProvider.ctx)?.showPwdDialog(
|
||||||
title: '2FA ${l10n.pwd}',
|
title: l10n.pwd,
|
||||||
id: spi.id,
|
id: spi.id,
|
||||||
label: spi.id,
|
label: spi.id,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import 'package:server_box/data/model/app/backup.dart';
|
|||||||
import 'package:server_box/data/model/app/sync.dart';
|
import 'package:server_box/data/model/app/sync.dart';
|
||||||
import 'package:server_box/data/res/misc.dart';
|
import 'package:server_box/data/res/misc.dart';
|
||||||
|
|
||||||
import '../../../data/model/app/error.dart';
|
import 'package:server_box/data/model/app/error.dart';
|
||||||
|
|
||||||
abstract final class ICloud {
|
abstract final class ICloud {
|
||||||
static const _containerId = 'iCloud.tech.lolli.serverbox';
|
static const _containerId = 'iCloud.tech.lolli.serverbox';
|
||||||
|
|||||||
@@ -2,24 +2,27 @@ import 'dart:convert';
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:fl_lib/fl_lib.dart';
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:server_box/data/model/server/private_key_info.dart';
|
import 'package:server_box/data/model/server/private_key_info.dart';
|
||||||
import 'package:server_box/data/model/server/server_private_info.dart';
|
import 'package:server_box/data/model/server/server_private_info.dart';
|
||||||
import 'package:server_box/data/model/server/snippet.dart';
|
import 'package:server_box/data/model/server/snippet.dart';
|
||||||
import 'package:server_box/data/res/misc.dart';
|
import 'package:server_box/data/res/misc.dart';
|
||||||
import 'package:server_box/data/res/provider.dart';
|
|
||||||
import 'package:server_box/data/res/rebuild.dart';
|
import 'package:server_box/data/res/rebuild.dart';
|
||||||
import 'package:server_box/data/res/store.dart';
|
import 'package:server_box/data/res/store.dart';
|
||||||
|
|
||||||
|
part 'backup.g.dart';
|
||||||
|
|
||||||
const backupFormatVersion = 1;
|
const backupFormatVersion = 1;
|
||||||
|
|
||||||
final _logger = Logger('Backup');
|
final _logger = Logger('Backup');
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
class Backup {
|
class Backup {
|
||||||
// backup format version
|
// backup format version
|
||||||
final int version;
|
final int version;
|
||||||
final String date;
|
final String date;
|
||||||
final List<ServerPrivateInfo> spis;
|
final List<Spi> spis;
|
||||||
final List<Snippet> snippets;
|
final List<Snippet> snippets;
|
||||||
final List<PrivateKeyInfo> keys;
|
final List<PrivateKeyInfo> keys;
|
||||||
final Map<String, dynamic> container;
|
final Map<String, dynamic> container;
|
||||||
@@ -37,31 +40,9 @@ class Backup {
|
|||||||
this.lastModTime,
|
this.lastModTime,
|
||||||
});
|
});
|
||||||
|
|
||||||
Backup.fromJson(Map<String, dynamic> json)
|
factory Backup.fromJson(Map<String, dynamic> json) => _$BackupFromJson(json);
|
||||||
: version = json['version'] as int,
|
|
||||||
date = json['date'],
|
|
||||||
spis = (json['spis'] as List)
|
|
||||||
.map((e) => ServerPrivateInfo.fromJson(e))
|
|
||||||
.toList(),
|
|
||||||
snippets =
|
|
||||||
(json['snippets'] as List).map((e) => Snippet.fromJson(e)).toList(),
|
|
||||||
keys = (json['keys'] as List)
|
|
||||||
.map((e) => PrivateKeyInfo.fromJson(e))
|
|
||||||
.toList(),
|
|
||||||
container = json['container'] ?? {},
|
|
||||||
lastModTime = json['lastModTime'],
|
|
||||||
history = json['history'] ?? {};
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => _$BackupToJson(this);
|
||||||
'version': version,
|
|
||||||
'date': date,
|
|
||||||
'spis': spis,
|
|
||||||
'snippets': snippets,
|
|
||||||
'keys': keys,
|
|
||||||
'container': container,
|
|
||||||
'lastModTime': lastModTime,
|
|
||||||
'history': history,
|
|
||||||
};
|
|
||||||
|
|
||||||
Backup.loadFromStore()
|
Backup.loadFromStore()
|
||||||
: version = backupFormatVersion,
|
: version = backupFormatVersion,
|
||||||
@@ -90,93 +71,119 @@ 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();
|
Provider.reload();
|
||||||
RNodes.app.build();
|
RNodes.app.notify();
|
||||||
|
|
||||||
_logger.info('Restore success');
|
_logger.info('Restore success');
|
||||||
}
|
}
|
||||||
|
|
||||||
Backup.fromJsonString(String raw)
|
factory Backup.fromJsonString(String raw) =>
|
||||||
: this.fromJson(json.decode(_diyDecrypt(raw)));
|
Backup.fromJson(json.decode(_diyDecrypt(raw)));
|
||||||
}
|
}
|
||||||
|
|
||||||
String _diyEncrypt(String raw) => json.encode(
|
String _diyEncrypt(String raw) => json.encode(
|
||||||
|
|||||||
35
lib/data/model/app/backup.g.dart
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'backup.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
Backup _$BackupFromJson(Map<String, dynamic> json) => Backup(
|
||||||
|
version: (json['version'] as num).toInt(),
|
||||||
|
date: json['date'] as String,
|
||||||
|
spis: (json['spis'] as List<dynamic>)
|
||||||
|
.map((e) => Spi.fromJson(e as Map<String, dynamic>))
|
||||||
|
.toList(),
|
||||||
|
snippets: (json['snippets'] as List<dynamic>)
|
||||||
|
.map((e) => Snippet.fromJson(e as Map<String, dynamic>))
|
||||||
|
.toList(),
|
||||||
|
keys: (json['keys'] as List<dynamic>)
|
||||||
|
.map((e) => PrivateKeyInfo.fromJson(e as Map<String, dynamic>))
|
||||||
|
.toList(),
|
||||||
|
container: json['container'] as Map<String, dynamic>,
|
||||||
|
history: json['history'] as Map<String, dynamic>,
|
||||||
|
lastModTime: (json['lastModTime'] as num?)?.toInt(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$BackupToJson(Backup instance) => <String, dynamic>{
|
||||||
|
'version': instance.version,
|
||||||
|
'date': instance.date,
|
||||||
|
'spis': instance.spis,
|
||||||
|
'snippets': instance.snippets,
|
||||||
|
'keys': instance.keys,
|
||||||
|
'container': instance.container,
|
||||||
|
'history': instance.history,
|
||||||
|
'lastModTime': instance.lastModTime,
|
||||||
|
};
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:server_box/core/extension/context/locale.dart';
|
import 'package:server_box/core/extension/context/locale.dart';
|
||||||
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,55 +2,76 @@ 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:server_box/core/extension/context/locale.dart';
|
import 'package:server_box/core/extension/context/locale.dart';
|
||||||
|
import 'package:server_box/data/res/store.dart';
|
||||||
|
|
||||||
part 'server_func.g.dart';
|
part 'server_func.g.dart';
|
||||||
|
|
||||||
@HiveType(typeId: 6)
|
@HiveType(typeId: 6)
|
||||||
enum ServerFuncBtn {
|
enum ServerFuncBtn {
|
||||||
@HiveField(0)
|
@HiveField(0)
|
||||||
terminal,
|
terminal._(),
|
||||||
@HiveField(1)
|
@HiveField(1)
|
||||||
sftp,
|
sftp._(),
|
||||||
@HiveField(2)
|
@HiveField(2)
|
||||||
container,
|
container._(),
|
||||||
@HiveField(3)
|
@HiveField(3)
|
||||||
process,
|
process._(),
|
||||||
@HiveField(4)
|
//@HiveField(4)
|
||||||
pkg,
|
//pkg,
|
||||||
@HiveField(5)
|
@HiveField(5)
|
||||||
snippet,
|
snippet._(),
|
||||||
@HiveField(6)
|
@HiveField(6)
|
||||||
iperf,
|
iperf._(),
|
||||||
// @HiveField(7)
|
// @HiveField(7)
|
||||||
// pve,
|
// pve,
|
||||||
|
@HiveField(8)
|
||||||
|
systemd._(1058),
|
||||||
;
|
;
|
||||||
|
|
||||||
|
final int? addedVersion;
|
||||||
|
|
||||||
|
const ServerFuncBtn._([this.addedVersion]);
|
||||||
|
|
||||||
|
static void autoAddNewFuncs(int cur) {
|
||||||
|
if (cur >= systemd.addedVersion!) {
|
||||||
|
final prop = Stores.setting.serverFuncBtns;
|
||||||
|
final list = prop.fetch();
|
||||||
|
if (!list.contains(systemd.index)) {
|
||||||
|
list.add(systemd.index);
|
||||||
|
prop.put(list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static final defaultIdxs = [
|
static final defaultIdxs = [
|
||||||
terminal,
|
terminal,
|
||||||
sftp,
|
sftp,
|
||||||
container,
|
container,
|
||||||
process,
|
process,
|
||||||
pkg,
|
//pkg,
|
||||||
snippet,
|
snippet,
|
||||||
|
systemd,
|
||||||
].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,
|
||||||
iperf => Icons.speed,
|
iperf => Icons.speed,
|
||||||
|
systemd => MingCute.plugin_2_fill,
|
||||||
};
|
};
|
||||||
|
|
||||||
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,
|
||||||
iperf => 'iperf',
|
iperf => 'iperf',
|
||||||
|
systemd => 'Systemd',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,12 +21,12 @@ 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:
|
||||||
return ServerFuncBtn.iperf;
|
return ServerFuncBtn.iperf;
|
||||||
|
case 8:
|
||||||
|
return ServerFuncBtn.systemd;
|
||||||
default:
|
default:
|
||||||
return ServerFuncBtn.terminal;
|
return ServerFuncBtn.terminal;
|
||||||
}
|
}
|
||||||
@@ -47,15 +47,15 @@ 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;
|
||||||
case ServerFuncBtn.iperf:
|
case ServerFuncBtn.iperf:
|
||||||
writer.writeByte(6);
|
writer.writeByte(6);
|
||||||
break;
|
break;
|
||||||
|
case ServerFuncBtn.systemd:
|
||||||
|
writer.writeByte(8);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
import 'package:hive_flutter/hive_flutter.dart';
|
import 'package:hive_flutter/hive_flutter.dart';
|
||||||
import 'package:server_box/core/extension/context/locale.dart';
|
import 'package:server_box/core/extension/context/locale.dart';
|
||||||
import 'package:server_box/data/model/server/server.dart';
|
import 'package:server_box/data/model/server/server.dart';
|
||||||
import 'package:server_box/data/res/store.dart';
|
|
||||||
|
|
||||||
part 'net_view.g.dart';
|
part 'net_view.g.dart';
|
||||||
|
|
||||||
@@ -14,80 +14,67 @@ 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) {
|
/// If no device is specified, return the cached value (only real devices,
|
||||||
final ignoreLocal = Stores.setting.ignoreLocalNet.fetch();
|
/// such as ethX, wlanX...).
|
||||||
switch (this) {
|
(String, String) build(ServerStatus ss, {String? dev}) {
|
||||||
case NetViewType.conn:
|
final notSepcifyDev = dev == null || dev.isEmpty;
|
||||||
return (
|
try {
|
||||||
'${l10n.conn}:\n${ss.tcp.maxConn}',
|
switch (this) {
|
||||||
'${l10n.failed}:\n${ss.tcp.fail}',
|
case NetViewType.conn:
|
||||||
);
|
|
||||||
case NetViewType.speed:
|
|
||||||
if (ignoreLocal) {
|
|
||||||
return (
|
return (
|
||||||
'↓:\n${ss.netSpeed.cachedRealVals.speedIn}',
|
'${l10n.conn}:\n${ss.tcp.maxConn}',
|
||||||
'↑:\n${ss.netSpeed.cachedRealVals.speedOut}',
|
'${libL10n.fail}:\n${ss.tcp.fail}',
|
||||||
);
|
);
|
||||||
}
|
case NetViewType.speed:
|
||||||
return (
|
if (notSepcifyDev) {
|
||||||
'↓:\n${ss.netSpeed.speedIn()}',
|
return (
|
||||||
'↑:\n${ss.netSpeed.speedOut()}',
|
'↓:\n${ss.netSpeed.cachedVals.speedIn}',
|
||||||
);
|
'↑:\n${ss.netSpeed.cachedVals.speedOut}',
|
||||||
case NetViewType.traffic:
|
);
|
||||||
if (ignoreLocal) {
|
}
|
||||||
return (
|
return (
|
||||||
'↓:\n${ss.netSpeed.cachedRealVals.sizeIn}',
|
'↓:\n${ss.netSpeed.speedIn(device: dev)}',
|
||||||
'↑:\n${ss.netSpeed.cachedRealVals.sizeOut}',
|
'↑:\n${ss.netSpeed.speedOut(device: dev)}',
|
||||||
);
|
);
|
||||||
}
|
case NetViewType.traffic:
|
||||||
return (
|
if (notSepcifyDev) {
|
||||||
'↓:\n${ss.netSpeed.sizeIn()}',
|
return (
|
||||||
'↑:\n${ss.netSpeed.sizeOut()}',
|
'↓:\n${ss.netSpeed.cachedVals.sizeIn}',
|
||||||
);
|
'↑:\n${ss.netSpeed.cachedVals.sizeOut}',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
'↓:\n${ss.netSpeed.sizeIn(device: dev)}',
|
||||||
|
'↑:\n${ss.netSpeed.sizeOut(device: dev)}',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (e, s) {
|
||||||
|
Loggers.app.warning('NetViewType.build', e, s);
|
||||||
|
return ('N/A', 'N/A');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,7 +1,8 @@
|
|||||||
import 'package:server_box/core/extension/context/locale.dart';
|
import 'package:server_box/core/extension/context/locale.dart';
|
||||||
|
import 'package:server_box/data/provider/server.dart';
|
||||||
|
|
||||||
import '../../res/build_data.dart';
|
import 'package:server_box/data/res/build_data.dart';
|
||||||
import '../server/system.dart';
|
import 'package:server_box/data/model/server/system.dart';
|
||||||
|
|
||||||
enum ShellFunc {
|
enum ShellFunc {
|
||||||
status,
|
status,
|
||||||
@@ -12,52 +13,61 @@ 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) {
|
||||||
///
|
final customScriptDir =
|
||||||
/// **Can't** use it in SFTP, because SFTP can't recognize `$HOME`
|
ServerProvider.pick(id: id)?.value.spi.custom?.scriptDir;
|
||||||
static String getShellPath(String home) => '$home/$_srvBoxDir/$scriptFile';
|
if (customScriptDir != null) return customScriptDir;
|
||||||
|
return _scriptDirMap.putIfAbsent(id, () {
|
||||||
static const srvBoxDir = '$_homeVar/$_srvBoxDir';
|
return scriptDirTmp;
|
||||||
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 +223,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 +242,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;
|
||||||
|
|||||||
@@ -45,21 +45,21 @@ final class PodmanImg implements ContainerImg {
|
|||||||
String toRawJson() => json.encode(toJson());
|
String toRawJson() => json.encode(toJson());
|
||||||
|
|
||||||
factory PodmanImg.fromJson(Map<String, dynamic> json) => PodmanImg(
|
factory PodmanImg.fromJson(Map<String, dynamic> json) => PodmanImg(
|
||||||
repository: json["repository"],
|
repository: json['repository'],
|
||||||
tag: json["tag"],
|
tag: json['tag'],
|
||||||
id: json["Id"],
|
id: json['Id'],
|
||||||
created: json["Created"],
|
created: json['Created'],
|
||||||
size: json["Size"],
|
size: json['Size'],
|
||||||
containers: json["Containers"],
|
containers: json['Containers'],
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => {
|
||||||
"repository": repository,
|
'repository': repository,
|
||||||
"tag": tag,
|
'tag': tag,
|
||||||
"Id": id,
|
'Id': id,
|
||||||
"Created": created,
|
'Created': created,
|
||||||
"Size": size,
|
'Size': size,
|
||||||
"Containers": containers,
|
'Containers': containers,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,36 +96,36 @@ final class DockerImg implements ContainerImg {
|
|||||||
String toRawJson() => json.encode(toJson());
|
String toRawJson() => json.encode(toJson());
|
||||||
|
|
||||||
factory DockerImg.fromJson(Map<String, dynamic> json) {
|
factory DockerImg.fromJson(Map<String, dynamic> json) {
|
||||||
final containers = switch (json["Containers"]) {
|
final containers = switch (json['Containers']) {
|
||||||
final String a => a,
|
final String a => a,
|
||||||
final Object? a => a.toString(),
|
final Object? a => a.toString(),
|
||||||
};
|
};
|
||||||
final repo = switch (json["Repository"] ?? json["Names"]) {
|
final repo = switch (json['Repository'] ?? json['Names']) {
|
||||||
final String a => a,
|
final String a => a,
|
||||||
final List a => a.firstOrNull.toString(),
|
final List a => a.firstOrNull.toString(),
|
||||||
final Object? a => a.toString(),
|
final Object? a => a.toString(),
|
||||||
};
|
};
|
||||||
final size = switch (json["Size"]) {
|
final size = switch (json['Size']) {
|
||||||
final String a => a,
|
final String a => a,
|
||||||
final int a => a.bytes2Str,
|
final int a => a.bytes2Str,
|
||||||
final Object? a => a.toString(),
|
final Object? a => a.toString(),
|
||||||
};
|
};
|
||||||
return DockerImg(
|
return DockerImg(
|
||||||
containers: containers,
|
containers: containers,
|
||||||
createdAt: json["CreatedAt"],
|
createdAt: json['CreatedAt'],
|
||||||
id: json["ID"] ?? json["Id"] ?? '',
|
id: json['ID'] ?? json['Id'] ?? '',
|
||||||
repository: repo,
|
repository: repo,
|
||||||
size: size,
|
size: size,
|
||||||
tag: json["Tag"],
|
tag: json['Tag'],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => {
|
||||||
"Containers": containers,
|
'Containers': containers,
|
||||||
"CreatedAt": createdAt,
|
'CreatedAt': createdAt,
|
||||||
"ID": id,
|
'ID': id,
|
||||||
"Repository": repository,
|
'Repository': repository,
|
||||||
"Size": size,
|
'Size': size,
|
||||||
"Tag": tag,
|
'Tag': tag,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,29 +84,29 @@ final class PodmanPs implements ContainerPs {
|
|||||||
String toRawJson() => json.encode(toJson());
|
String toRawJson() => json.encode(toJson());
|
||||||
|
|
||||||
factory PodmanPs.fromJson(Map<String, dynamic> json) => PodmanPs(
|
factory PodmanPs.fromJson(Map<String, dynamic> json) => PodmanPs(
|
||||||
command: json["Command"] == null
|
command: json['Command'] == null
|
||||||
? []
|
? []
|
||||||
: List<String>.from(json["Command"]!.map((x) => x)),
|
: List<String>.from(json['Command']!.map((x) => x)),
|
||||||
created:
|
created:
|
||||||
json["Created"] == null ? null : DateTime.parse(json["Created"]),
|
json['Created'] == null ? null : DateTime.parse(json['Created']),
|
||||||
exited: json["Exited"],
|
exited: json['Exited'],
|
||||||
id: json["Id"],
|
id: json['Id'],
|
||||||
image: json["Image"],
|
image: json['Image'],
|
||||||
names: json["Names"] == null
|
names: json['Names'] == null
|
||||||
? []
|
? []
|
||||||
: List<String>.from(json["Names"]!.map((x) => x)),
|
: List<String>.from(json['Names']!.map((x) => x)),
|
||||||
startedAt: json["StartedAt"],
|
startedAt: json['StartedAt'],
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => {
|
||||||
"Command":
|
'Command':
|
||||||
command == null ? [] : List<dynamic>.from(command!.map((x) => x)),
|
command == null ? [] : List<dynamic>.from(command!.map((x) => x)),
|
||||||
"Created": created?.toIso8601String(),
|
'Created': created?.toIso8601String(),
|
||||||
"Exited": exited,
|
'Exited': exited,
|
||||||
"Id": id,
|
'Id': id,
|
||||||
"Image": image,
|
'Image': image,
|
||||||
"Names": names == null ? [] : List<dynamic>.from(names!.map((x) => x)),
|
'Names': names == null ? [] : List<dynamic>.from(names!.map((x) => x)),
|
||||||
"StartedAt": startedAt,
|
'StartedAt': startedAt,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,9 +34,9 @@ class UpgradePkgInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _parseApt(String raw) {
|
void _parseApt(String raw) {
|
||||||
final split1 = raw.split("/");
|
final split1 = raw.split('/');
|
||||||
package = split1[0];
|
package = split1[0];
|
||||||
final split2 = split1[1].split(" ");
|
final split2 = split1[1].split(' ');
|
||||||
newVersion = split2[1];
|
newVersion = split2[1];
|
||||||
arch = split2[2];
|
arch = split2[2];
|
||||||
nowVersion = split2[5].replaceFirst(']', '');
|
nowVersion = split2[5].replaceFirst(']', '');
|
||||||
@@ -53,7 +53,7 @@ class UpgradePkgInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _parseZypper(String raw) {
|
void _parseZypper(String raw) {
|
||||||
final cols = raw.split("|");
|
final cols = raw.split('|');
|
||||||
package = cols[2].trim();
|
package = cols[2].trim();
|
||||||
nowVersion = cols[3].trim();
|
nowVersion = cols[3].trim();
|
||||||
newVersion = cols[4].trim();
|
newVersion = cols[4].trim();
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import '../../res/misc.dart';
|
import 'package:server_box/data/res/misc.dart';
|
||||||
|
|
||||||
class Conn {
|
class Conn {
|
||||||
final int maxConn;
|
final int maxConn;
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
import 'dart:collection';
|
|
||||||
|
|
||||||
import 'package:fl_chart/fl_chart.dart';
|
import 'package:fl_chart/fl_chart.dart';
|
||||||
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
import 'package:server_box/data/model/server/time_seq.dart';
|
import 'package:server_box/data/model/server/time_seq.dart';
|
||||||
import 'package:server_box/data/res/status.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,7 +1,9 @@
|
|||||||
import 'package:hive_flutter/adapters.dart';
|
import 'package:hive_flutter/adapters.dart';
|
||||||
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
part 'custom.g.dart';
|
part 'custom.g.dart';
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
@HiveType(typeId: 7)
|
@HiveType(typeId: 7)
|
||||||
final class ServerCustom {
|
final class ServerCustom {
|
||||||
// @HiveField(0)
|
// @HiveField(0)
|
||||||
@@ -19,6 +21,14 @@ final class ServerCustom {
|
|||||||
@HiveField(5)
|
@HiveField(5)
|
||||||
final String? logoUrl;
|
final String? logoUrl;
|
||||||
|
|
||||||
|
/// The device name of the network interface displayed in the home server card.
|
||||||
|
@HiveField(6)
|
||||||
|
final String? netDev;
|
||||||
|
|
||||||
|
/// The directory where the script is stored.
|
||||||
|
@HiveField(7)
|
||||||
|
final String? scriptDir;
|
||||||
|
|
||||||
const ServerCustom({
|
const ServerCustom({
|
||||||
//this.temperature,
|
//this.temperature,
|
||||||
this.pveAddr,
|
this.pveAddr,
|
||||||
@@ -26,51 +36,14 @@ final class ServerCustom {
|
|||||||
this.cmds,
|
this.cmds,
|
||||||
this.preferTempDev,
|
this.preferTempDev,
|
||||||
this.logoUrl,
|
this.logoUrl,
|
||||||
|
this.netDev,
|
||||||
|
this.scriptDir,
|
||||||
});
|
});
|
||||||
|
|
||||||
static ServerCustom fromJson(Map<String, dynamic> json) {
|
factory ServerCustom.fromJson(Map<String, dynamic> json) =>
|
||||||
//final temperature = json["temperature"] as String?;
|
_$ServerCustomFromJson(json);
|
||||||
final pveAddr = json["pveAddr"] as String?;
|
|
||||||
final pveIgnoreCert = json["pveIgnoreCert"] as bool;
|
|
||||||
final cmds = json["cmds"] as Map<String, dynamic>?;
|
|
||||||
final preferTempDev = json["preferTempDev"] as String?;
|
|
||||||
final logoUrl = json["logoUrl"] as String?;
|
|
||||||
return ServerCustom(
|
|
||||||
//temperature: temperature,
|
|
||||||
pveAddr: pveAddr,
|
|
||||||
pveIgnoreCert: pveIgnoreCert,
|
|
||||||
cmds: cmds?.cast<String, String>(),
|
|
||||||
preferTempDev: preferTempDev,
|
|
||||||
logoUrl: logoUrl,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() => _$ServerCustomToJson(this);
|
||||||
final json = <String, dynamic>{};
|
|
||||||
// if (temperature != null) {
|
|
||||||
// json["temperature"] = temperature;
|
|
||||||
// }
|
|
||||||
if (pveAddr != null) {
|
|
||||||
json["pveAddr"] = pveAddr;
|
|
||||||
}
|
|
||||||
json["pveIgnoreCert"] = pveIgnoreCert;
|
|
||||||
|
|
||||||
if (cmds != null) {
|
|
||||||
json["cmds"] = cmds;
|
|
||||||
}
|
|
||||||
if (preferTempDev != null) {
|
|
||||||
json["preferTempDev"] = preferTempDev;
|
|
||||||
}
|
|
||||||
if (logoUrl != null) {
|
|
||||||
json["logoUrl"] = logoUrl;
|
|
||||||
}
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return toJson().toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
@@ -80,7 +53,9 @@ final class ServerCustom {
|
|||||||
other.pveIgnoreCert == pveIgnoreCert &&
|
other.pveIgnoreCert == pveIgnoreCert &&
|
||||||
other.cmds == cmds &&
|
other.cmds == cmds &&
|
||||||
other.preferTempDev == preferTempDev &&
|
other.preferTempDev == preferTempDev &&
|
||||||
other.logoUrl == logoUrl;
|
other.logoUrl == logoUrl &&
|
||||||
|
other.netDev == netDev &&
|
||||||
|
other.scriptDir == scriptDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -90,5 +65,7 @@ final class ServerCustom {
|
|||||||
pveIgnoreCert.hashCode ^
|
pveIgnoreCert.hashCode ^
|
||||||
cmds.hashCode ^
|
cmds.hashCode ^
|
||||||
preferTempDev.hashCode ^
|
preferTempDev.hashCode ^
|
||||||
logoUrl.hashCode;
|
logoUrl.hashCode ^
|
||||||
|
netDev.hashCode ^
|
||||||
|
scriptDir.hashCode;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,13 +22,15 @@ class ServerCustomAdapter extends TypeAdapter<ServerCustom> {
|
|||||||
cmds: (fields[3] as Map?)?.cast<String, String>(),
|
cmds: (fields[3] as Map?)?.cast<String, String>(),
|
||||||
preferTempDev: fields[4] as String?,
|
preferTempDev: fields[4] as String?,
|
||||||
logoUrl: fields[5] as String?,
|
logoUrl: fields[5] as String?,
|
||||||
|
netDev: fields[6] as String?,
|
||||||
|
scriptDir: fields[7] as String?,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void write(BinaryWriter writer, ServerCustom obj) {
|
void write(BinaryWriter writer, ServerCustom obj) {
|
||||||
writer
|
writer
|
||||||
..writeByte(5)
|
..writeByte(7)
|
||||||
..writeByte(1)
|
..writeByte(1)
|
||||||
..write(obj.pveAddr)
|
..write(obj.pveAddr)
|
||||||
..writeByte(2)
|
..writeByte(2)
|
||||||
@@ -38,7 +40,11 @@ class ServerCustomAdapter extends TypeAdapter<ServerCustom> {
|
|||||||
..writeByte(4)
|
..writeByte(4)
|
||||||
..write(obj.preferTempDev)
|
..write(obj.preferTempDev)
|
||||||
..writeByte(5)
|
..writeByte(5)
|
||||||
..write(obj.logoUrl);
|
..write(obj.logoUrl)
|
||||||
|
..writeByte(6)
|
||||||
|
..write(obj.netDev)
|
||||||
|
..writeByte(7)
|
||||||
|
..write(obj.scriptDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -51,3 +57,30 @@ class ServerCustomAdapter extends TypeAdapter<ServerCustom> {
|
|||||||
runtimeType == other.runtimeType &&
|
runtimeType == other.runtimeType &&
|
||||||
typeId == other.typeId;
|
typeId == other.typeId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
ServerCustom _$ServerCustomFromJson(Map<String, dynamic> json) => ServerCustom(
|
||||||
|
pveAddr: json['pveAddr'] as String?,
|
||||||
|
pveIgnoreCert: json['pveIgnoreCert'] as bool? ?? false,
|
||||||
|
cmds: (json['cmds'] as Map<String, dynamic>?)?.map(
|
||||||
|
(k, e) => MapEntry(k, e as String),
|
||||||
|
),
|
||||||
|
preferTempDev: json['preferTempDev'] as String?,
|
||||||
|
logoUrl: json['logoUrl'] as String?,
|
||||||
|
netDev: json['netDev'] as String?,
|
||||||
|
scriptDir: json['scriptDir'] as String?,
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$ServerCustomToJson(ServerCustom instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'pveAddr': instance.pveAddr,
|
||||||
|
'pveIgnoreCert': instance.pveIgnoreCert,
|
||||||
|
'cmds': instance.cmds,
|
||||||
|
'preferTempDev': instance.preferTempDev,
|
||||||
|
'logoUrl': instance.logoUrl,
|
||||||
|
'netDev': instance.netDev,
|
||||||
|
'scriptDir': instance.scriptDir,
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:fl_lib/fl_lib.dart';
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
import 'package:server_box/data/model/server/time_seq.dart';
|
import 'package:server_box/data/model/server/time_seq.dart';
|
||||||
|
|
||||||
import '../../res/misc.dart';
|
import 'package:server_box/data/res/misc.dart';
|
||||||
|
|
||||||
class Disk {
|
class Disk {
|
||||||
final String fs;
|
final String fs;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:fl_lib/fl_lib.dart';
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
|
|
||||||
import 'time_seq.dart';
|
import 'package:server_box/data/model/server/time_seq.dart';
|
||||||
|
|
||||||
class NetSpeedPart extends TimeSeqIface<NetSpeedPart> {
|
class NetSpeedPart extends TimeSeqIface<NetSpeedPart> {
|
||||||
final String device;
|
final String device;
|
||||||
@@ -14,6 +14,13 @@ class NetSpeedPart extends TimeSeqIface<NetSpeedPart> {
|
|||||||
bool same(NetSpeedPart other) => device == other.device;
|
bool same(NetSpeedPart other) => device == other.device;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef CachedNetVals = ({
|
||||||
|
String sizeIn,
|
||||||
|
String sizeOut,
|
||||||
|
String speedIn,
|
||||||
|
String speedOut,
|
||||||
|
});
|
||||||
|
|
||||||
class NetSpeed extends TimeSeq<List<NetSpeedPart>> {
|
class NetSpeed extends TimeSeq<List<NetSpeedPart>> {
|
||||||
NetSpeed(super.init1, super.init2);
|
NetSpeed(super.init1, super.init2);
|
||||||
|
|
||||||
@@ -24,14 +31,14 @@ class NetSpeed extends TimeSeq<List<NetSpeedPart>> {
|
|||||||
|
|
||||||
realIfaces.clear();
|
realIfaces.clear();
|
||||||
realIfaces.addAll(devices
|
realIfaces.addAll(devices
|
||||||
.where((e) => realIfacePrefixs.any((prefix) => e.startsWith(prefix)))
|
.where((e) => realIfacePrefixs.any((prefix) => e.startsWith(prefix))));
|
||||||
.toList());
|
|
||||||
|
|
||||||
final sizeIn = this.sizeIn();
|
final sizeIn = this.sizeIn();
|
||||||
final sizeOut = this.sizeOut();
|
final sizeOut = this.sizeOut();
|
||||||
final speedIn = this.speedIn();
|
final speedIn = this.speedIn();
|
||||||
final speedOut = this.speedOut();
|
final speedOut = this.speedOut();
|
||||||
cachedRealVals = (
|
|
||||||
|
cachedVals = (
|
||||||
sizeIn: sizeIn,
|
sizeIn: sizeIn,
|
||||||
sizeOut: sizeOut,
|
sizeOut: sizeOut,
|
||||||
speedIn: speedIn,
|
speedIn: speedIn,
|
||||||
@@ -49,12 +56,7 @@ class NetSpeed extends TimeSeq<List<NetSpeedPart>> {
|
|||||||
/// Cached non-virtual network device prefix
|
/// Cached non-virtual network device prefix
|
||||||
final realIfaces = <String>[];
|
final realIfaces = <String>[];
|
||||||
|
|
||||||
({
|
CachedNetVals cachedVals =
|
||||||
String sizeIn,
|
|
||||||
String sizeOut,
|
|
||||||
String speedIn,
|
|
||||||
String speedOut,
|
|
||||||
}) cachedRealVals =
|
|
||||||
(sizeIn: '0kb', sizeOut: '0kb', speedIn: '0kb/s', speedOut: '0kb/s');
|
(sizeIn: '0kb', sizeOut: '0kb', speedIn: '0kb/s', speedOut: '0kb/s');
|
||||||
|
|
||||||
/// Time diff between [pre] and [now]
|
/// Time diff between [pre] and [now]
|
||||||
@@ -67,7 +69,8 @@ class NetSpeed extends TimeSeq<List<NetSpeedPart>> {
|
|||||||
BigInt sizeOutBytes(int i) => now[i].bytesOut;
|
BigInt sizeOutBytes(int i) => now[i].bytesOut;
|
||||||
|
|
||||||
String speedIn({String? device}) {
|
String speedIn({String? device}) {
|
||||||
if (pre[0].device == '' || now[0].device == '') return '0kb/s';
|
if (pre.isEmpty || now.isEmpty) return 'N/A';
|
||||||
|
if (pre.length != now.length) return 'N/A';
|
||||||
if (device == null) {
|
if (device == null) {
|
||||||
var speed = 0.0;
|
var speed = 0.0;
|
||||||
for (final device in devices) {
|
for (final device in devices) {
|
||||||
@@ -84,7 +87,8 @@ class NetSpeed extends TimeSeq<List<NetSpeedPart>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String sizeIn({String? device}) {
|
String sizeIn({String? device}) {
|
||||||
if (pre[0].device == '' || now[0].device == '') return '0kb';
|
if (pre.isEmpty || now.isEmpty) return 'N/A';
|
||||||
|
if (pre.length != now.length) return 'N/A';
|
||||||
if (device == null) {
|
if (device == null) {
|
||||||
var size = BigInt.from(0);
|
var size = BigInt.from(0);
|
||||||
for (final device in devices) {
|
for (final device in devices) {
|
||||||
@@ -101,7 +105,8 @@ class NetSpeed extends TimeSeq<List<NetSpeedPart>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String speedOut({String? device}) {
|
String speedOut({String? device}) {
|
||||||
if (pre[0].device == '' || now[0].device == '') return '0kb/s';
|
if (pre.isEmpty || now.isEmpty) return 'N/A';
|
||||||
|
if (pre.length != now.length) return 'N/A';
|
||||||
if (device == null) {
|
if (device == null) {
|
||||||
var speed = 0.0;
|
var speed = 0.0;
|
||||||
for (final device in devices) {
|
for (final device in devices) {
|
||||||
@@ -118,7 +123,8 @@ class NetSpeed extends TimeSeq<List<NetSpeedPart>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String sizeOut({String? device}) {
|
String sizeOut({String? device}) {
|
||||||
if (pre[0].device == '' || now[0].device == '') return '0kb';
|
if (pre.isEmpty || now.isEmpty) return 'N/A';
|
||||||
|
if (pre.length != now.length) return 'N/A';
|
||||||
if (device == null) {
|
if (device == null) {
|
||||||
var size = BigInt.from(0);
|
var size = BigInt.from(0);
|
||||||
for (final device in devices) {
|
for (final device in devices) {
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
import 'package:hive_flutter/hive_flutter.dart';
|
import 'package:hive_flutter/hive_flutter.dart';
|
||||||
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
part 'private_key_info.g.dart';
|
part 'private_key_info.g.dart';
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
@HiveType(typeId: 1)
|
@HiveType(typeId: 1)
|
||||||
class PrivateKeyInfo {
|
class PrivateKeyInfo {
|
||||||
@HiveField(0)
|
@HiveField(0)
|
||||||
final String id;
|
final String id;
|
||||||
|
@JsonKey(name: 'private_key')
|
||||||
@HiveField(1)
|
@HiveField(1)
|
||||||
final String key;
|
final String key;
|
||||||
|
|
||||||
@@ -14,6 +17,11 @@ class PrivateKeyInfo {
|
|||||||
required this.key,
|
required this.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
factory PrivateKeyInfo.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$PrivateKeyInfoFromJson(json);
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => _$PrivateKeyInfoToJson(this);
|
||||||
|
|
||||||
String? get type {
|
String? get type {
|
||||||
final lines = key.split('\n');
|
final lines = key.split('\n');
|
||||||
if (lines.length < 2) {
|
if (lines.length < 2) {
|
||||||
@@ -26,15 +34,4 @@ class PrivateKeyInfo {
|
|||||||
}
|
}
|
||||||
return splited[1];
|
return splited[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
PrivateKeyInfo.fromJson(Map<String, dynamic> json)
|
|
||||||
: id = json["id"].toString(),
|
|
||||||
key = json["private_key"].toString();
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
final data = <String, String>{};
|
|
||||||
data["id"] = id;
|
|
||||||
data["private_key"] = key;
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,3 +42,19 @@ class PrivateKeyInfoAdapter extends TypeAdapter<PrivateKeyInfo> {
|
|||||||
runtimeType == other.runtimeType &&
|
runtimeType == other.runtimeType &&
|
||||||
typeId == other.typeId;
|
typeId == other.typeId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
PrivateKeyInfo _$PrivateKeyInfoFromJson(Map<String, dynamic> json) =>
|
||||||
|
PrivateKeyInfo(
|
||||||
|
id: json['id'] as String,
|
||||||
|
key: json['private_key'] as String,
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$PrivateKeyInfoToJson(PrivateKeyInfo instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'id': instance.id,
|
||||||
|
'private_key': instance.key,
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:fl_lib/fl_lib.dart';
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
|
|
||||||
import '../../../data/res/misc.dart';
|
import 'package:server_box/data/res/misc.dart';
|
||||||
|
|
||||||
class _ProcValIdxMap {
|
class _ProcValIdxMap {
|
||||||
final int pid;
|
final int pid;
|
||||||
@@ -58,7 +58,7 @@ class Proc {
|
|||||||
required this.command,
|
required this.command,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory Proc.parse(String raw, _ProcValIdxMap map) {
|
factory Proc._parse(String raw, _ProcValIdxMap map) {
|
||||||
final parts = raw.split(RegExp(r'\s+'));
|
final parts = raw.split(RegExp(r'\s+'));
|
||||||
return Proc(
|
return Proc(
|
||||||
user: map.user == null ? null : parts[map.user!],
|
user: map.user == null ? null : parts[map.user!],
|
||||||
@@ -139,7 +139,7 @@ class PsResult {
|
|||||||
final line = lines[i];
|
final line = lines[i];
|
||||||
if (line.isEmpty) continue;
|
if (line.isEmpty) continue;
|
||||||
try {
|
try {
|
||||||
procs.add(Proc.parse(line, map));
|
procs.add(Proc._parse(line, map));
|
||||||
} catch (e, trace) {
|
} catch (e, trace) {
|
||||||
errs.add('$line: $e');
|
errs.add('$line: $e');
|
||||||
Loggers.app.warning('Process failed', e, trace);
|
Loggers.app.warning('Process failed', e, trace);
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import 'package:fl_lib/fl_lib.dart';
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
import 'package:server_box/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,8 +1,7 @@
|
|||||||
import 'package:dartssh2/dartssh2.dart';
|
import 'package:dartssh2/dartssh2.dart';
|
||||||
import 'package:fl_lib/fl_lib.dart';
|
|
||||||
import 'package:server_box/core/extension/context/locale.dart';
|
|
||||||
import 'package:server_box/data/model/app/error.dart';
|
import 'package:server_box/data/model/app/error.dart';
|
||||||
import 'package:server_box/data/model/app/shell_func.dart';
|
import 'package:server_box/data/model/app/shell_func.dart';
|
||||||
|
import 'package:server_box/data/model/app/tag_pickable.dart';
|
||||||
import 'package:server_box/data/model/server/battery.dart';
|
import 'package:server_box/data/model/server/battery.dart';
|
||||||
import 'package:server_box/data/model/server/conn.dart';
|
import 'package:server_box/data/model/server/conn.dart';
|
||||||
import 'package:server_box/data/model/server/cpu.dart';
|
import 'package:server_box/data/model/server/cpu.dart';
|
||||||
@@ -15,12 +14,8 @@ import 'package:server_box/data/model/server/server_private_info.dart';
|
|||||||
import 'package:server_box/data/model/server/system.dart';
|
import 'package:server_box/data/model/server/system.dart';
|
||||||
import 'package:server_box/data/model/server/temp.dart';
|
import 'package:server_box/data/model/server/temp.dart';
|
||||||
|
|
||||||
import '../app/tag_pickable.dart';
|
|
||||||
|
|
||||||
part 'server.ext.dart';
|
|
||||||
|
|
||||||
class Server implements TagPickable {
|
class Server implements TagPickable {
|
||||||
ServerPrivateInfo spi;
|
Spi spi;
|
||||||
ServerStatus status;
|
ServerStatus status;
|
||||||
SSHClient? client;
|
SSHClient? client;
|
||||||
ServerConn conn;
|
ServerConn conn;
|
||||||
@@ -95,5 +90,5 @@ enum ServerConn {
|
|||||||
/// Status parsing finished
|
/// Status parsing finished
|
||||||
finished;
|
finished;
|
||||||
|
|
||||||
operator <(ServerConn other) => index < other.index;
|
bool operator <(ServerConn other) => index < other.index;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,16 +1,26 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
import 'package:hive_flutter/hive_flutter.dart';
|
import 'package:hive_flutter/hive_flutter.dart';
|
||||||
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
import 'package:server_box/data/model/server/custom.dart';
|
import 'package:server_box/data/model/server/custom.dart';
|
||||||
import 'package:server_box/data/model/server/server.dart';
|
import 'package:server_box/data/model/server/server.dart';
|
||||||
import 'package:server_box/data/model/server/wol_cfg.dart';
|
import 'package:server_box/data/model/server/wol_cfg.dart';
|
||||||
import 'package:server_box/data/res/provider.dart';
|
import 'package:server_box/data/provider/server.dart';
|
||||||
|
|
||||||
import '../app/error.dart';
|
import 'package:server_box/data/model/app/error.dart';
|
||||||
|
|
||||||
part 'server_private_info.g.dart';
|
part 'server_private_info.g.dart';
|
||||||
|
|
||||||
/// In former version, it's called `ServerPrivateInfo`.
|
/// In the first version, it's called `ServerPrivateInfo` which was designed to
|
||||||
|
/// store the private information of a server.
|
||||||
|
///
|
||||||
|
/// Some params named as `spi` in the codebase which is the abbreviation of `ServerPrivateInfo`.
|
||||||
|
///
|
||||||
|
/// Nowaday, more fields are added to this class, and it's renamed to `Spi`.
|
||||||
|
@JsonSerializable()
|
||||||
@HiveType(typeId: 3)
|
@HiveType(typeId: 3)
|
||||||
class ServerPrivateInfo {
|
class Spi {
|
||||||
@HiveField(0)
|
@HiveField(0)
|
||||||
final String name;
|
final String name;
|
||||||
@HiveField(1)
|
@HiveField(1)
|
||||||
@@ -23,14 +33,15 @@ class ServerPrivateInfo {
|
|||||||
final String? pwd;
|
final String? pwd;
|
||||||
|
|
||||||
/// [id] of private key
|
/// [id] of private key
|
||||||
|
@JsonKey(name: 'pubKeyId')
|
||||||
@HiveField(5)
|
@HiveField(5)
|
||||||
final String? keyId;
|
final String? keyId;
|
||||||
@HiveField(6)
|
@HiveField(6)
|
||||||
final List<String>? tags;
|
final List<String>? tags;
|
||||||
@HiveField(7)
|
@HiveField(7)
|
||||||
final String? alterUrl;
|
final String? alterUrl;
|
||||||
@HiveField(8)
|
@HiveField(8, defaultValue: true)
|
||||||
final bool? autoConnect;
|
final bool autoConnect;
|
||||||
|
|
||||||
/// [id] of the jump server
|
/// [id] of the jump server
|
||||||
@HiveField(9)
|
@HiveField(9)
|
||||||
@@ -42,9 +53,13 @@ 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 Spi({
|
||||||
required this.name,
|
required this.name,
|
||||||
required this.ip,
|
required this.ip,
|
||||||
required this.port,
|
required this.port,
|
||||||
@@ -53,83 +68,28 @@ class ServerPrivateInfo {
|
|||||||
this.keyId,
|
this.keyId,
|
||||||
this.tags,
|
this.tags,
|
||||||
this.alterUrl,
|
this.alterUrl,
|
||||||
this.autoConnect,
|
this.autoConnect = true,
|
||||||
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) {
|
factory Spi.fromJson(Map<String, dynamic> json) => _$SpiFromJson(json);
|
||||||
final ip = json["ip"] as String? ?? '';
|
|
||||||
final port = json["port"] as int? ?? 22;
|
|
||||||
final user = json["user"] as String? ?? 'root';
|
|
||||||
final name = json["name"] as String? ?? '';
|
|
||||||
final pwd = json["pwd"] as String? ?? json["authorization"] as String?;
|
|
||||||
final keyId = json["pubKeyId"] as String?;
|
|
||||||
final tags = (json["tags"] as List?)?.cast<String>();
|
|
||||||
final alterUrl = json["alterUrl"] as String?;
|
|
||||||
final autoConnect = json["autoConnect"] as bool?;
|
|
||||||
final jumpId = json["jumpId"] as String?;
|
|
||||||
final custom = json["customCmd"] == null
|
|
||||||
? null
|
|
||||||
: ServerCustom.fromJson(json["custom"].cast<String, dynamic>());
|
|
||||||
final wolCfg = json["wolCfg"] == null
|
|
||||||
? null
|
|
||||||
: WakeOnLanCfg.fromJson(json["wolCfg"].cast<String, dynamic>());
|
|
||||||
|
|
||||||
return ServerPrivateInfo(
|
Map<String, dynamic> toJson() => _$SpiToJson(this);
|
||||||
name: name,
|
|
||||||
ip: ip,
|
|
||||||
port: port,
|
|
||||||
user: user,
|
|
||||||
pwd: pwd,
|
|
||||||
keyId: keyId,
|
|
||||||
tags: tags,
|
|
||||||
alterUrl: alterUrl,
|
|
||||||
autoConnect: autoConnect,
|
|
||||||
jumpId: jumpId,
|
|
||||||
custom: custom,
|
|
||||||
wolCfg: wolCfg,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
@override
|
||||||
final Map<String, dynamic> data = <String, dynamic>{};
|
String toString() => id;
|
||||||
data["name"] = name;
|
}
|
||||||
data["ip"] = ip;
|
|
||||||
data["port"] = port;
|
|
||||||
data["user"] = user;
|
|
||||||
if (pwd != null) {
|
|
||||||
data["pwd"] = pwd;
|
|
||||||
}
|
|
||||||
if (keyId != null) {
|
|
||||||
data["pubKeyId"] = keyId;
|
|
||||||
}
|
|
||||||
if (tags != null) {
|
|
||||||
data["tags"] = tags;
|
|
||||||
}
|
|
||||||
if (alterUrl != null) {
|
|
||||||
data["alterUrl"] = alterUrl;
|
|
||||||
}
|
|
||||||
if (autoConnect != null) {
|
|
||||||
data["autoConnect"] = autoConnect;
|
|
||||||
}
|
|
||||||
if (jumpId != null) {
|
|
||||||
data["jumpId"] = jumpId;
|
|
||||||
}
|
|
||||||
if (custom != null) {
|
|
||||||
data["custom"] = custom?.toJson();
|
|
||||||
}
|
|
||||||
if (wolCfg != null) {
|
|
||||||
data["wolCfg"] = wolCfg?.toJson();
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
Server? get server => Pros.server.pick(spi: this);
|
extension Spix on Spi {
|
||||||
Server? get jumpServer => Pros.server.pick(id: jumpId);
|
String toJsonString() => json.encode(toJson());
|
||||||
|
|
||||||
bool shouldReconnect(ServerPrivateInfo old) {
|
VNode<Server>? get server => ServerProvider.pick(spi: this);
|
||||||
|
VNode<Server>? get jumpServer => ServerProvider.pick(id: jumpId);
|
||||||
|
|
||||||
|
bool shouldReconnect(Spi old) {
|
||||||
return id != old.id ||
|
return id != old.id ||
|
||||||
pwd != old.pwd ||
|
pwd != old.pwd ||
|
||||||
keyId != old.keyId ||
|
keyId != old.keyId ||
|
||||||
@@ -138,7 +98,7 @@ class ServerPrivateInfo {
|
|||||||
custom?.cmds != old.custom?.cmds;
|
custom?.cmds != old.custom?.cmds;
|
||||||
}
|
}
|
||||||
|
|
||||||
_IpPort fromStringUrl() {
|
(String, int) fromStringUrl() {
|
||||||
if (alterUrl == null) {
|
if (alterUrl == null) {
|
||||||
throw SSHErr(type: SSHErrType.connect, message: 'alterUrl is null');
|
throw SSHErr(type: SSHErrType.connect, message: 'alterUrl is null');
|
||||||
}
|
}
|
||||||
@@ -155,18 +115,30 @@ class ServerPrivateInfo {
|
|||||||
if (port <= 0 || port > 65535) {
|
if (port <= 0 || port > 65535) {
|
||||||
throw SSHErr(type: SSHErrType.connect, message: 'alterUrl port error');
|
throw SSHErr(type: SSHErrType.connect, message: 'alterUrl port error');
|
||||||
}
|
}
|
||||||
return _IpPort(ip_, port_);
|
return (ip_, port_);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
static const example = Spi(
|
||||||
String toString() {
|
name: 'name',
|
||||||
return id;
|
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 {
|
bool get isRoot => user == 'root';
|
||||||
final String ip;
|
|
||||||
final int port;
|
|
||||||
|
|
||||||
_IpPort(this.ip, this.port);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,17 +6,17 @@ part of 'server_private_info.dart';
|
|||||||
// TypeAdapterGenerator
|
// TypeAdapterGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
class ServerPrivateInfoAdapter extends TypeAdapter<ServerPrivateInfo> {
|
class SpiAdapter extends TypeAdapter<Spi> {
|
||||||
@override
|
@override
|
||||||
final int typeId = 3;
|
final int typeId = 3;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ServerPrivateInfo read(BinaryReader reader) {
|
Spi read(BinaryReader reader) {
|
||||||
final numOfFields = reader.readByte();
|
final numOfFields = reader.readByte();
|
||||||
final fields = <int, dynamic>{
|
final fields = <int, dynamic>{
|
||||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||||
};
|
};
|
||||||
return ServerPrivateInfo(
|
return Spi(
|
||||||
name: fields[0] as String,
|
name: fields[0] as String,
|
||||||
ip: fields[1] as String,
|
ip: fields[1] as String,
|
||||||
port: fields[2] as int,
|
port: fields[2] as int,
|
||||||
@@ -25,17 +25,18 @@ class ServerPrivateInfoAdapter extends TypeAdapter<ServerPrivateInfo> {
|
|||||||
keyId: fields[5] as String?,
|
keyId: fields[5] as String?,
|
||||||
tags: (fields[6] as List?)?.cast<String>(),
|
tags: (fields[6] as List?)?.cast<String>(),
|
||||||
alterUrl: fields[7] as String?,
|
alterUrl: fields[7] as String?,
|
||||||
autoConnect: fields[8] as bool?,
|
autoConnect: fields[8] == null ? true : fields[8] as bool,
|
||||||
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, Spi 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
|
||||||
@@ -68,7 +71,49 @@ class ServerPrivateInfoAdapter extends TypeAdapter<ServerPrivateInfo> {
|
|||||||
@override
|
@override
|
||||||
bool operator ==(Object other) =>
|
bool operator ==(Object other) =>
|
||||||
identical(this, other) ||
|
identical(this, other) ||
|
||||||
other is ServerPrivateInfoAdapter &&
|
other is SpiAdapter &&
|
||||||
runtimeType == other.runtimeType &&
|
runtimeType == other.runtimeType &&
|
||||||
typeId == other.typeId;
|
typeId == other.typeId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
Spi _$SpiFromJson(Map<String, dynamic> json) => Spi(
|
||||||
|
name: json['name'] as String,
|
||||||
|
ip: json['ip'] as String,
|
||||||
|
port: (json['port'] as num).toInt(),
|
||||||
|
user: json['user'] as String,
|
||||||
|
pwd: json['pwd'] as String?,
|
||||||
|
keyId: json['pubKeyId'] as String?,
|
||||||
|
tags: (json['tags'] as List<dynamic>?)?.map((e) => e as String).toList(),
|
||||||
|
alterUrl: json['alterUrl'] as String?,
|
||||||
|
autoConnect: json['autoConnect'] as bool? ?? true,
|
||||||
|
jumpId: json['jumpId'] as String?,
|
||||||
|
custom: json['custom'] == null
|
||||||
|
? null
|
||||||
|
: ServerCustom.fromJson(json['custom'] as Map<String, dynamic>),
|
||||||
|
wolCfg: json['wolCfg'] == null
|
||||||
|
? null
|
||||||
|
: WakeOnLanCfg.fromJson(json['wolCfg'] as Map<String, dynamic>),
|
||||||
|
envs: (json['envs'] as Map<String, dynamic>?)?.map(
|
||||||
|
(k, e) => MapEntry(k, e as String),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$SpiToJson(Spi instance) => <String, dynamic>{
|
||||||
|
'name': instance.name,
|
||||||
|
'ip': instance.ip,
|
||||||
|
'port': instance.port,
|
||||||
|
'user': instance.user,
|
||||||
|
'pwd': instance.pwd,
|
||||||
|
'pubKeyId': instance.keyId,
|
||||||
|
'tags': instance.tags,
|
||||||
|
'alterUrl': instance.alterUrl,
|
||||||
|
'autoConnect': instance.autoConnect,
|
||||||
|
'jumpId': instance.jumpId,
|
||||||
|
'custom': instance.custom,
|
||||||
|
'wolCfg': instance.wolCfg,
|
||||||
|
'envs': instance.envs,
|
||||||
|
};
|
||||||
|
|||||||
@@ -5,12 +5,12 @@ import 'package:server_box/data/model/server/sensors.dart';
|
|||||||
import 'package:server_box/data/model/server/server.dart';
|
import 'package:server_box/data/model/server/server.dart';
|
||||||
import 'package:server_box/data/model/server/system.dart';
|
import 'package:server_box/data/model/server/system.dart';
|
||||||
|
|
||||||
import '../app/shell_func.dart';
|
import 'package:server_box/data/model/app/shell_func.dart';
|
||||||
import 'cpu.dart';
|
import 'package:server_box/data/model/server/cpu.dart';
|
||||||
import 'disk.dart';
|
import 'package:server_box/data/model/server/disk.dart';
|
||||||
import 'memory.dart';
|
import 'package:server_box/data/model/server/memory.dart';
|
||||||
import 'net_speed.dart';
|
import 'package:server_box/data/model/server/net_speed.dart';
|
||||||
import 'conn.dart';
|
import 'package:server_box/data/model/server/conn.dart';
|
||||||
|
|
||||||
class ServerStatusUpdateReq {
|
class ServerStatusUpdateReq {
|
||||||
final ServerStatus ss;
|
final ServerStatus ss;
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,13 +2,15 @@ import 'dart:async';
|
|||||||
|
|
||||||
import 'package:fl_lib/fl_lib.dart';
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
import 'package:hive_flutter/hive_flutter.dart';
|
import 'package:hive_flutter/hive_flutter.dart';
|
||||||
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
import 'package:server_box/data/model/server/server_private_info.dart';
|
import 'package:server_box/data/model/server/server_private_info.dart';
|
||||||
import 'package:xterm/core.dart';
|
import 'package:xterm/core.dart';
|
||||||
|
|
||||||
import '../app/tag_pickable.dart';
|
import 'package:server_box/data/model/app/tag_pickable.dart';
|
||||||
|
|
||||||
part 'snippet.g.dart';
|
part 'snippet.g.dart';
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
@HiveType(typeId: 2)
|
@HiveType(typeId: 2)
|
||||||
class Snippet implements TagPickable {
|
class Snippet implements TagPickable {
|
||||||
@HiveField(0)
|
@HiveField(0)
|
||||||
@@ -32,22 +34,10 @@ class Snippet implements TagPickable {
|
|||||||
this.autoRunOn,
|
this.autoRunOn,
|
||||||
});
|
});
|
||||||
|
|
||||||
Snippet.fromJson(Map<String, dynamic> json)
|
factory Snippet.fromJson(Map<String, dynamic> json) =>
|
||||||
: name = json['name'].toString(),
|
_$SnippetFromJson(json);
|
||||||
script = json['script'].toString(),
|
|
||||||
tags = json['tags']?.cast<String>(),
|
|
||||||
note = json['note']?.toString(),
|
|
||||||
autoRunOn = json['autoRunOn']?.cast<String>();
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() => _$SnippetToJson(this);
|
||||||
final data = <String, dynamic>{};
|
|
||||||
data['name'] = name;
|
|
||||||
data['script'] = script;
|
|
||||||
data['tags'] = tags;
|
|
||||||
data['note'] = note;
|
|
||||||
data['autoRunOn'] = autoRunOn;
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool containsTag(String tag) {
|
bool containsTag(String tag) {
|
||||||
@@ -59,7 +49,7 @@ class Snippet implements TagPickable {
|
|||||||
|
|
||||||
static final fmtFinder = RegExp(r'\$\{[^{}]+\}');
|
static final fmtFinder = RegExp(r'\$\{[^{}]+\}');
|
||||||
|
|
||||||
String fmtWithSpi(ServerPrivateInfo spi) {
|
String fmtWithSpi(Spi spi) {
|
||||||
return script.replaceAllMapped(
|
return script.replaceAllMapped(
|
||||||
fmtFinder,
|
fmtFinder,
|
||||||
(match) {
|
(match) {
|
||||||
@@ -74,7 +64,7 @@ class Snippet implements TagPickable {
|
|||||||
|
|
||||||
Future<void> runInTerm(
|
Future<void> runInTerm(
|
||||||
Terminal terminal,
|
Terminal terminal,
|
||||||
ServerPrivateInfo spi, {
|
Spi spi, {
|
||||||
bool autoEnter = false,
|
bool autoEnter = false,
|
||||||
}) async {
|
}) async {
|
||||||
final argsFmted = fmtWithSpi(spi);
|
final argsFmted = fmtWithSpi(spi);
|
||||||
@@ -170,12 +160,12 @@ class Snippet implements TagPickable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static final fmtArgs = {
|
static final fmtArgs = {
|
||||||
r'${host}': (ServerPrivateInfo spi) => spi.ip,
|
r'${host}': (Spi spi) => spi.ip,
|
||||||
r'${port}': (ServerPrivateInfo spi) => spi.port.toString(),
|
r'${port}': (Spi spi) => spi.port.toString(),
|
||||||
r'${user}': (ServerPrivateInfo spi) => spi.user,
|
r'${user}': (Spi spi) => spi.user,
|
||||||
r'${pwd}': (ServerPrivateInfo spi) => spi.pwd ?? '',
|
r'${pwd}': (Spi spi) => spi.pwd ?? '',
|
||||||
r'${id}': (ServerPrivateInfo spi) => spi.id,
|
r'${id}': (Spi spi) => spi.id,
|
||||||
r'${name}': (ServerPrivateInfo spi) => spi.name,
|
r'${name}': (Spi spi) => spi.name,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// r'${ctrl+ad}' -> TerminalKey.control, a, d
|
/// r'${ctrl+ad}' -> TerminalKey.control, a, d
|
||||||
@@ -183,6 +173,14 @@ class Snippet implements TagPickable {
|
|||||||
r'${ctrl': TerminalKey.control,
|
r'${ctrl': TerminalKey.control,
|
||||||
r'${alt': TerminalKey.alt,
|
r'${alt': TerminalKey.alt,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const example = Snippet(
|
||||||
|
name: 'example',
|
||||||
|
script: 'echo hello',
|
||||||
|
tags: ['tag'],
|
||||||
|
note: 'note',
|
||||||
|
autoRunOn: ['server_id'],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class SnippetResult {
|
class SnippetResult {
|
||||||
|
|||||||
@@ -51,3 +51,25 @@ class SnippetAdapter extends TypeAdapter<Snippet> {
|
|||||||
runtimeType == other.runtimeType &&
|
runtimeType == other.runtimeType &&
|
||||||
typeId == other.typeId;
|
typeId == other.typeId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
Snippet _$SnippetFromJson(Map<String, dynamic> json) => Snippet(
|
||||||
|
name: json['name'] as String,
|
||||||
|
script: json['script'] as String,
|
||||||
|
tags: (json['tags'] as List<dynamic>?)?.map((e) => e as String).toList(),
|
||||||
|
note: json['note'] as String?,
|
||||||
|
autoRunOn: (json['autoRunOn'] as List<dynamic>?)
|
||||||
|
?.map((e) => e as String)
|
||||||
|
.toList(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$SnippetToJson(Snippet instance) => <String, dynamic>{
|
||||||
|
'name': instance.name,
|
||||||
|
'script': instance.script,
|
||||||
|
'tags': instance.tags,
|
||||||
|
'note': instance.note,
|
||||||
|
'autoRunOn': instance.autoRunOn,
|
||||||
|
};
|
||||||
|
|||||||
118
lib/data/model/server/systemd.dart
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
enum SystemdUnitFunc {
|
||||||
|
start,
|
||||||
|
stop,
|
||||||
|
restart,
|
||||||
|
reload,
|
||||||
|
enable,
|
||||||
|
disable,
|
||||||
|
status,
|
||||||
|
;
|
||||||
|
|
||||||
|
IconData get icon => switch (this) {
|
||||||
|
start => Icons.play_arrow,
|
||||||
|
stop => Icons.stop,
|
||||||
|
restart => Icons.refresh,
|
||||||
|
reload => Icons.refresh,
|
||||||
|
enable => Icons.check,
|
||||||
|
disable => Icons.close,
|
||||||
|
status => Icons.info,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
enum SystemdUnitType {
|
||||||
|
service,
|
||||||
|
socket,
|
||||||
|
mount,
|
||||||
|
timer,
|
||||||
|
;
|
||||||
|
|
||||||
|
static SystemdUnitType? fromString(String? value) {
|
||||||
|
return values.firstWhereOrNull((e) => e.name == value?.toLowerCase());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum SystemdUnitScope {
|
||||||
|
system,
|
||||||
|
user,
|
||||||
|
;
|
||||||
|
|
||||||
|
Color? get color => switch (this) {
|
||||||
|
system => Colors.red,
|
||||||
|
_ => null,
|
||||||
|
};
|
||||||
|
|
||||||
|
String getCmdPrefix(bool isRoot) {
|
||||||
|
if (this == system) {
|
||||||
|
return isRoot ? 'systemctl' : 'sudo systemctl';
|
||||||
|
}
|
||||||
|
return 'systemctl --user';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum SystemdUnitState {
|
||||||
|
active,
|
||||||
|
inactive,
|
||||||
|
failed,
|
||||||
|
activating,
|
||||||
|
deactivating,
|
||||||
|
;
|
||||||
|
|
||||||
|
static SystemdUnitState? fromString(String? value) {
|
||||||
|
return values.firstWhereOrNull((e) => e.name == value?.toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
Color? get color => switch (this) {
|
||||||
|
failed => Colors.red,
|
||||||
|
_ => null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
final class SystemdUnit {
|
||||||
|
final String name;
|
||||||
|
final String? description;
|
||||||
|
final SystemdUnitType type;
|
||||||
|
final SystemdUnitScope scope;
|
||||||
|
final SystemdUnitState state;
|
||||||
|
|
||||||
|
SystemdUnit({
|
||||||
|
required this.name,
|
||||||
|
this.description,
|
||||||
|
required this.type,
|
||||||
|
required this.scope,
|
||||||
|
required this.state,
|
||||||
|
});
|
||||||
|
|
||||||
|
String getCmd({
|
||||||
|
required SystemdUnitFunc func,
|
||||||
|
required bool isRoot,
|
||||||
|
}) {
|
||||||
|
final prefix = scope.getCmdPrefix(isRoot);
|
||||||
|
return '$prefix ${func.name} $name';
|
||||||
|
}
|
||||||
|
|
||||||
|
List<SystemdUnitFunc> get availableFuncs {
|
||||||
|
final funcs = <SystemdUnitFunc>{};
|
||||||
|
switch (state) {
|
||||||
|
case SystemdUnitState.active:
|
||||||
|
funcs.addAll([SystemdUnitFunc.stop, SystemdUnitFunc.restart]);
|
||||||
|
break;
|
||||||
|
case SystemdUnitState.inactive:
|
||||||
|
funcs.addAll([SystemdUnitFunc.start]);
|
||||||
|
break;
|
||||||
|
case SystemdUnitState.failed:
|
||||||
|
funcs.addAll([SystemdUnitFunc.restart]);
|
||||||
|
break;
|
||||||
|
case SystemdUnitState.activating:
|
||||||
|
funcs.addAll([SystemdUnitFunc.stop]);
|
||||||
|
break;
|
||||||
|
case SystemdUnitState.deactivating:
|
||||||
|
funcs.addAll([SystemdUnitFunc.start]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
funcs.addAll([SystemdUnitFunc.status]);
|
||||||
|
return funcs.toList();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:hive_flutter/hive_flutter.dart';
|
import 'package:hive_flutter/hive_flutter.dart';
|
||||||
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
import 'package:wake_on_lan/wake_on_lan.dart';
|
import 'package:wake_on_lan/wake_on_lan.dart';
|
||||||
|
|
||||||
part 'wol_cfg.g.dart';
|
part 'wol_cfg.g.dart';
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
@HiveType(typeId: 8)
|
@HiveType(typeId: 8)
|
||||||
final class WakeOnLanCfg {
|
final class WakeOnLanCfg {
|
||||||
@HiveField(0)
|
@HiveField(0)
|
||||||
@@ -54,22 +56,8 @@ final class WakeOnLanCfg {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static WakeOnLanCfg fromJson(Map<String, dynamic> json) {
|
factory WakeOnLanCfg.fromJson(Map<String, dynamic> json) =>
|
||||||
return WakeOnLanCfg(
|
_$WakeOnLanCfgFromJson(json);
|
||||||
mac: json['mac'] as String,
|
|
||||||
ip: json['ip'] as String,
|
|
||||||
pwd: json['pwd'] as String?,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() => _$WakeOnLanCfgToJson(this);
|
||||||
final map = <String, dynamic>{
|
|
||||||
'mac': mac,
|
|
||||||
'ip': ip,
|
|
||||||
};
|
|
||||||
if (pwd != null) {
|
|
||||||
map['pwd'] = pwd;
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,3 +45,20 @@ class WakeOnLanCfgAdapter extends TypeAdapter<WakeOnLanCfg> {
|
|||||||
runtimeType == other.runtimeType &&
|
runtimeType == other.runtimeType &&
|
||||||
typeId == other.typeId;
|
typeId == other.typeId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
WakeOnLanCfg _$WakeOnLanCfgFromJson(Map<String, dynamic> json) => WakeOnLanCfg(
|
||||||
|
mac: json['mac'] as String,
|
||||||
|
ip: json['ip'] as String,
|
||||||
|
pwd: json['pwd'] as String?,
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$WakeOnLanCfgToJson(WakeOnLanCfg instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'mac': instance.mac,
|
||||||
|
'ip': instance.ip,
|
||||||
|
'pwd': instance.pwd,
|
||||||
|
};
|
||||||
|
|||||||
@@ -2,12 +2,14 @@ import 'package:fl_lib/fl_lib.dart';
|
|||||||
|
|
||||||
class AbsolutePath {
|
class AbsolutePath {
|
||||||
String _path;
|
String _path;
|
||||||
|
final _prePath = <String>[];
|
||||||
|
|
||||||
|
AbsolutePath(this._path);
|
||||||
|
|
||||||
String get path => _path;
|
String get path => _path;
|
||||||
final List<String> _prePath;
|
|
||||||
|
|
||||||
AbsolutePath(this._path) : _prePath = ['/'];
|
/// Update path, not set path
|
||||||
|
set path(String newPath) {
|
||||||
void update(String newPath) {
|
|
||||||
_prePath.add(_path);
|
_prePath.add(_path);
|
||||||
if (newPath == '..') {
|
if (newPath == '..') {
|
||||||
_path = _path.substring(0, _path.lastIndexOf('/'));
|
_path = _path.substring(0, _path.lastIndexOf('/'));
|
||||||
@@ -16,10 +18,6 @@ class AbsolutePath {
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (newPath == '/') {
|
|
||||||
_path = '/';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (newPath.startsWith('/')) {
|
if (newPath.startsWith('/')) {
|
||||||
_path = newPath;
|
_path = newPath;
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -2,9 +2,11 @@ import 'package:dartssh2/dartssh2.dart';
|
|||||||
import 'package:server_box/data/model/sftp/absolute_path.dart';
|
import 'package:server_box/data/model/sftp/absolute_path.dart';
|
||||||
|
|
||||||
class SftpBrowserStatus {
|
class SftpBrowserStatus {
|
||||||
List<SftpName>? files;
|
final List<SftpName> files = [];
|
||||||
AbsolutePath? path;
|
final AbsolutePath path = AbsolutePath('/');
|
||||||
SftpClient? client;
|
SftpClient? client;
|
||||||
|
|
||||||
SftpBrowserStatus();
|
SftpBrowserStatus(SSHClient client) {
|
||||||
|
client.sftp().then((value) => this.client = value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,12 @@
|
|||||||
import 'dart:async';
|
part of 'worker.dart';
|
||||||
|
|
||||||
import 'package:fl_lib/fl_lib.dart';
|
|
||||||
import 'package:server_box/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 Spi spi;
|
||||||
final String remotePath;
|
final String remotePath;
|
||||||
final String localPath;
|
final String localPath;
|
||||||
final SftpReqType type;
|
final SftpReqType type;
|
||||||
String? privateKey;
|
String? privateKey;
|
||||||
ServerPrivateInfo? jumpSpi;
|
Spi? jumpSpi;
|
||||||
String? jumpPrivateKey;
|
String? jumpPrivateKey;
|
||||||
|
|
||||||
SftpReq(
|
SftpReq(
|
||||||
@@ -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,37 +1,7 @@
|
|||||||
import 'package:device_info_plus/device_info_plus.dart';
|
|
||||||
import 'package:fl_lib/fl_lib.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class AppProvider extends ChangeNotifier {
|
final class AppProvider {
|
||||||
int? _newestBuild;
|
const AppProvider._();
|
||||||
int? get newestBuild => _newestBuild;
|
|
||||||
set newestBuild(int? build) {
|
|
||||||
_newestBuild = build;
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
BuildContext? ctx;
|
static BuildContext? ctx;
|
||||||
|
|
||||||
bool isWearOS = false;
|
|
||||||
|
|
||||||
Future<void> init() async {
|
|
||||||
await _initIsWearOS();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _initIsWearOS() async {
|
|
||||||
if (!isAndroid) {
|
|
||||||
isWearOS = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final deviceInfo = DeviceInfoPlugin();
|
|
||||||
final androidInfo = await deviceInfo.androidInfo;
|
|
||||||
|
|
||||||
const feat = 'android.hardware.type.watch';
|
|
||||||
final hasFeat = androidInfo.systemFeatures.contains(feat);
|
|
||||||
if (hasFeat) {
|
|
||||||
isWearOS = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ class ContainerProvider extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final res = await client?.run(_wrap(ContainerCmdType.images.exec(type)));
|
final res = await client?.run(_wrap(ContainerCmdType.images.exec(type)));
|
||||||
if (res?.string.toLowerCase().contains("permission denied") ?? false) {
|
if (res?.string.toLowerCase().contains('permission denied') ?? false) {
|
||||||
return sudoCompleter.complete(true);
|
return sudoCompleter.complete(true);
|
||||||
}
|
}
|
||||||
return sudoCompleter.complete(false);
|
return sudoCompleter.complete(false);
|
||||||
|
|||||||
@@ -1,35 +1,41 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
import 'package:server_box/data/model/server/private_key_info.dart';
|
import 'package:server_box/data/model/server/private_key_info.dart';
|
||||||
import 'package:server_box/data/res/store.dart';
|
import 'package:server_box/data/res/store.dart';
|
||||||
|
|
||||||
class PrivateKeyProvider extends ChangeNotifier {
|
class PrivateKeyProvider extends Provider {
|
||||||
List<PrivateKeyInfo> get pkis => _pkis;
|
const PrivateKeyProvider._();
|
||||||
late List<PrivateKeyInfo> _pkis;
|
static const instance = PrivateKeyProvider._();
|
||||||
|
|
||||||
|
static final pkis = <PrivateKeyInfo>[].vn;
|
||||||
|
|
||||||
|
@override
|
||||||
void load() {
|
void load() {
|
||||||
_pkis = Stores.key.fetch();
|
super.load();
|
||||||
|
pkis.value = Stores.key.fetch();
|
||||||
}
|
}
|
||||||
|
|
||||||
void add(PrivateKeyInfo info) {
|
static void add(PrivateKeyInfo info) {
|
||||||
_pkis.add(info);
|
pkis.value.add(info);
|
||||||
|
pkis.notify();
|
||||||
Stores.key.put(info);
|
Stores.key.put(info);
|
||||||
notifyListeners();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void delete(PrivateKeyInfo info) {
|
static void delete(PrivateKeyInfo info) {
|
||||||
_pkis.removeWhere((e) => e.id == info.id);
|
pkis.value.removeWhere((e) => e.id == info.id);
|
||||||
|
pkis.notify();
|
||||||
Stores.key.delete(info);
|
Stores.key.delete(info);
|
||||||
notifyListeners();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void update(PrivateKeyInfo old, PrivateKeyInfo newInfo) {
|
static void update(PrivateKeyInfo old, PrivateKeyInfo newInfo) {
|
||||||
final idx = _pkis.indexWhere((e) => e.id == old.id);
|
final idx = pkis.value.indexWhere((e) => e.id == old.id);
|
||||||
if (idx == -1) {
|
if (idx == -1) {
|
||||||
_pkis.add(newInfo);
|
pkis.value.add(newInfo);
|
||||||
|
Stores.key.put(newInfo);
|
||||||
|
Stores.key.delete(old);
|
||||||
} else {
|
} else {
|
||||||
_pkis[idx] = newInfo;
|
pkis.value[idx] = newInfo;
|
||||||
|
Stores.key.put(newInfo);
|
||||||
}
|
}
|
||||||
Stores.key.put(newInfo);
|
pkis.notify();
|
||||||
notifyListeners();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import 'package:dartssh2/dartssh2.dart';
|
|||||||
typedef PveCtrlFunc = Future<bool> Function(String node, String id);
|
typedef PveCtrlFunc = Future<bool> Function(String node, String id);
|
||||||
|
|
||||||
final class PveProvider extends ChangeNotifier {
|
final class PveProvider extends ChangeNotifier {
|
||||||
final ServerPrivateInfo spi;
|
final Spi spi;
|
||||||
late String addr;
|
late String addr;
|
||||||
late final SSHClient _client;
|
late final SSHClient _client;
|
||||||
late final ServerSocket _serverSocket;
|
late final ServerSocket _serverSocket;
|
||||||
@@ -23,7 +23,7 @@ final class PveProvider extends ChangeNotifier {
|
|||||||
int _localPort = 0;
|
int _localPort = 0;
|
||||||
|
|
||||||
PveProvider({required this.spi}) {
|
PveProvider({required this.spi}) {
|
||||||
final client = spi.server?.client;
|
final client = spi.server?.value.client;
|
||||||
if (client == null) {
|
if (client == null) {
|
||||||
throw Exception('Server client is null');
|
throw Exception('Server client is null');
|
||||||
}
|
}
|
||||||
@@ -104,7 +104,7 @@ final class PveProvider extends ChangeNotifier {
|
|||||||
socket.cast<List<int>>().pipe(forward.sink);
|
socket.cast<List<int>>().pipe(forward.sink);
|
||||||
});*/
|
});*/
|
||||||
|
|
||||||
if (url.isScheme("https")) {
|
if (url.isScheme('https')) {
|
||||||
return SecureSocket.startConnect('localhost', _localPort,
|
return SecureSocket.startConnect('localhost', _localPort,
|
||||||
onBadCertificate: (_) => true);
|
onBadCertificate: (_) => true);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,44 +1,44 @@
|
|||||||
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:server_box/core/extension/ssh_client.dart';
|
import 'package:server_box/core/extension/ssh_client.dart';
|
||||||
import 'package:server_box/core/utils/ssh_auth.dart';
|
import 'package:server_box/core/utils/ssh_auth.dart';
|
||||||
import 'package:server_box/data/model/app/error.dart';
|
import 'package:server_box/data/model/app/error.dart';
|
||||||
import 'package:server_box/data/model/app/shell_func.dart';
|
import 'package:server_box/data/model/app/shell_func.dart';
|
||||||
import 'package:server_box/data/model/server/system.dart';
|
import 'package:server_box/data/model/server/system.dart';
|
||||||
import 'package:server_box/data/model/sftp/req.dart';
|
|
||||||
import 'package:server_box/data/res/provider.dart';
|
|
||||||
import 'package:server_box/data/res/store.dart';
|
import 'package:server_box/data/res/store.dart';
|
||||||
|
|
||||||
import '../../core/utils/server.dart';
|
import 'package:server_box/core/utils/server.dart';
|
||||||
import '../model/server/server.dart';
|
import 'package:server_box/data/model/server/server.dart';
|
||||||
import '../model/server/server_private_info.dart';
|
import 'package:server_box/data/model/server/server_private_info.dart';
|
||||||
import '../model/server/server_status_update_req.dart';
|
import 'package:server_box/data/model/server/server_status_update_req.dart';
|
||||||
import '../model/server/try_limiter.dart';
|
import 'package:server_box/data/model/server/try_limiter.dart';
|
||||||
import '../res/status.dart';
|
import 'package:server_box/data/res/status.dart';
|
||||||
|
|
||||||
class ServerProvider extends ChangeNotifier {
|
class ServerProvider extends Provider {
|
||||||
final Map<String, Server> _servers = {};
|
const ServerProvider._();
|
||||||
Iterable<Server> get servers => _servers.values;
|
static const instance = ServerProvider._();
|
||||||
final List<String> _serverOrder = [];
|
|
||||||
List<String> get serverOrder => _serverOrder;
|
|
||||||
final _tags = ValueNotifier(<String>[]);
|
|
||||||
ValueNotifier<List<String>> get tags => _tags;
|
|
||||||
|
|
||||||
Timer? _timer;
|
static final Map<String, VNode<Server>> servers = {};
|
||||||
|
static final serverOrder = <String>[].vn;
|
||||||
|
static final _tags = <String>{}.vn;
|
||||||
|
static VNode<Set<String>> get tags => _tags;
|
||||||
|
|
||||||
final _manualDisconnectedIds = <String>{};
|
static Timer? _timer;
|
||||||
|
|
||||||
|
static final _manualDisconnectedIds = <String>{};
|
||||||
|
|
||||||
|
@override
|
||||||
Future<void> load() async {
|
Future<void> load() async {
|
||||||
// Issue #147
|
super.load();
|
||||||
|
// #147
|
||||||
// Clear all servers because of restarting app will cause duplicate servers
|
// Clear all servers because of restarting app will cause duplicate servers
|
||||||
final oldServers = Map<String, Server>.from(_servers);
|
final oldServers = Map<String, VNode<Server>>.from(servers);
|
||||||
_servers.clear();
|
servers.clear();
|
||||||
_serverOrder.clear();
|
serverOrder.value.clear();
|
||||||
|
|
||||||
final spis = Stores.server.fetch();
|
final spis = Stores.server.fetch();
|
||||||
for (int idx = 0; idx < spis.length; idx++) {
|
for (int idx = 0; idx < spis.length; idx++) {
|
||||||
@@ -46,12 +46,13 @@ class ServerProvider extends ChangeNotifier {
|
|||||||
final originServer = oldServers[spi.id];
|
final originServer = oldServers[spi.id];
|
||||||
final newServer = genServer(spi);
|
final newServer = genServer(spi);
|
||||||
|
|
||||||
/// Issues #258
|
/// #258
|
||||||
/// If not [shouldReconnect], then keep the old state.
|
/// If not [shouldReconnect], then keep the old state.
|
||||||
if (originServer != null && !originServer.spi.shouldReconnect(spi)) {
|
if (originServer != null &&
|
||||||
newServer.conn = originServer.conn;
|
!originServer.value.spi.shouldReconnect(spi)) {
|
||||||
|
newServer.conn = originServer.value.conn;
|
||||||
}
|
}
|
||||||
_servers[spi.id] = newServer;
|
servers[spi.id] = newServer.vn;
|
||||||
}
|
}
|
||||||
final serverOrder_ = Stores.setting.serverOrder.fetch();
|
final serverOrder_ = Stores.setting.serverOrder.fetch();
|
||||||
if (serverOrder_.isNotEmpty) {
|
if (serverOrder_.isNotEmpty) {
|
||||||
@@ -59,66 +60,53 @@ class ServerProvider extends ChangeNotifier {
|
|||||||
order: serverOrder_,
|
order: serverOrder_,
|
||||||
finder: (n, id) => n.id == id,
|
finder: (n, id) => n.id == id,
|
||||||
);
|
);
|
||||||
_serverOrder.addAll(spis.map((e) => e.id));
|
serverOrder.value.addAll(spis.map((e) => e.id));
|
||||||
} else {
|
} else {
|
||||||
_serverOrder.addAll(_servers.keys);
|
serverOrder.value.addAll(servers.keys);
|
||||||
}
|
}
|
||||||
// Must use [equals] to compare [Order] here.
|
// Must use [equals] to compare [Order] here.
|
||||||
if (!_serverOrder.equals(serverOrder_)) {
|
if (!serverOrder.value.equals(serverOrder_)) {
|
||||||
Stores.setting.serverOrder.put(_serverOrder);
|
Stores.setting.serverOrder.put(serverOrder.value);
|
||||||
}
|
}
|
||||||
_updateTags();
|
_updateTags();
|
||||||
notifyListeners();
|
// Must notify here, or the UI will not be updated.
|
||||||
|
serverOrder.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a [Server] by [spi] or [id].
|
/// Get a [Server] by [spi] or [id].
|
||||||
///
|
///
|
||||||
/// Priority: [spi] > [id]
|
/// Priority: [spi] > [id]
|
||||||
Server? pick({ServerPrivateInfo? spi, String? id}) {
|
static VNode<Server>? pick({Spi? spi, String? id}) {
|
||||||
if (spi != null) {
|
if (spi != null) {
|
||||||
return _servers[spi.id];
|
return servers[spi.id];
|
||||||
}
|
}
|
||||||
if (id != null) {
|
if (id != null) {
|
||||||
return _servers[id];
|
return servers[id];
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _updateTags() {
|
static void _updateTags() {
|
||||||
_tags.value.clear();
|
for (final s in servers.values) {
|
||||||
for (final s in _servers.values) {
|
final tags = s.value.spi.tags;
|
||||||
if (s.spi.tags == null) continue;
|
if (tags == null) continue;
|
||||||
for (final t in s.spi.tags!) {
|
for (final t in tags) {
|
||||||
if (!_tags.value.contains(t)) {
|
if (!_tags.value.contains(t)) {
|
||||||
_tags.value.add(t);
|
_tags.value.add(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_tags.value.sort();
|
_tags.value = (_tags.value.toList()..sort()).toSet();
|
||||||
_tags.notifyListeners();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void renameTag(String old, String new_) {
|
static Server genServer(Spi spi) {
|
||||||
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) {
|
|
||||||
return Server(spi, InitStatus.status, ServerConn.disconnected);
|
return Server(spi, InitStatus.status, ServerConn.disconnected);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// if [spi] is specificed then only refresh this server
|
/// if [spi] is specificed then only refresh this server
|
||||||
/// [onlyFailed] only refresh failed servers
|
/// [onlyFailed] only refresh failed servers
|
||||||
Future<void> refresh({
|
static Future<void> refresh({
|
||||||
ServerPrivateInfo? spi,
|
Spi? spi,
|
||||||
bool onlyFailed = false,
|
bool onlyFailed = false,
|
||||||
}) async {
|
}) async {
|
||||||
if (spi != null) {
|
if (spi != null) {
|
||||||
@@ -127,23 +115,24 @@ class ServerProvider extends ChangeNotifier {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await Future.wait(_servers.values.map((s) => _connectFn(s, onlyFailed)));
|
await Future.wait(servers.values.map((val) async {
|
||||||
|
final s = val.value;
|
||||||
|
if (onlyFailed) {
|
||||||
|
if (s.conn != ServerConn.failed) return;
|
||||||
|
TryLimiter.reset(s.spi.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_manualDisconnectedIds.contains(s.spi.id)) return;
|
||||||
|
|
||||||
|
if (s.conn == ServerConn.disconnected && !s.spi.autoConnect) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await _getData(s.spi);
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _connectFn(Server s, bool onlyFailed) async {
|
static Future<void> startAutoRefresh() async {
|
||||||
if (onlyFailed) {
|
|
||||||
if (s.conn != ServerConn.failed) return;
|
|
||||||
TryLimiter.reset(s.spi.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(s.spi.autoConnect ?? true) && s.conn == ServerConn.disconnected ||
|
|
||||||
_manualDisconnectedIds.contains(s.spi.id)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return await _getData(s.spi);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> startAutoRefresh() async {
|
|
||||||
var duration = Stores.setting.serverStatusUpdateInterval.fetch();
|
var duration = Stores.setting.serverStatusUpdateInterval.fetch();
|
||||||
stopAutoRefresh();
|
stopAutoRefresh();
|
||||||
if (duration == 0) return;
|
if (duration == 0) return;
|
||||||
@@ -156,84 +145,85 @@ class ServerProvider extends ChangeNotifier {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void stopAutoRefresh() {
|
static void stopAutoRefresh() {
|
||||||
if (_timer != null) {
|
if (_timer != null) {
|
||||||
_timer!.cancel();
|
_timer!.cancel();
|
||||||
_timer = null;
|
_timer = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get isAutoRefreshOn => _timer != null;
|
static bool get isAutoRefreshOn => _timer != null;
|
||||||
|
|
||||||
void setDisconnected() {
|
static void setDisconnected() {
|
||||||
for (final s in _servers.values) {
|
for (final s in servers.values) {
|
||||||
s.conn = ServerConn.disconnected;
|
s.value.conn = ServerConn.disconnected;
|
||||||
|
s.notify();
|
||||||
}
|
}
|
||||||
//TryLimiter.clear();
|
//TryLimiter.clear();
|
||||||
notifyListeners();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void closeServer({String? id}) {
|
static void closeServer({String? id}) {
|
||||||
if (id == null) {
|
if (id == null) {
|
||||||
for (final s in _servers.values) {
|
for (final s in servers.values) {
|
||||||
_closeOneServer(s.spi.id);
|
_closeOneServer(s.value.spi.id);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_closeOneServer(id);
|
_closeOneServer(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _closeOneServer(String id) {
|
static void _closeOneServer(String id) {
|
||||||
final item = _servers[id];
|
final s = servers[id];
|
||||||
|
final item = s?.value;
|
||||||
item?.client?.close();
|
item?.client?.close();
|
||||||
item?.client = null;
|
item?.client = null;
|
||||||
item?.conn = ServerConn.disconnected;
|
item?.conn = ServerConn.disconnected;
|
||||||
_manualDisconnectedIds.add(id);
|
_manualDisconnectedIds.add(id);
|
||||||
notifyListeners();
|
s?.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
void addServer(ServerPrivateInfo spi) {
|
static void addServer(Spi spi) {
|
||||||
_servers[spi.id] = genServer(spi);
|
servers[spi.id] = genServer(spi).vn;
|
||||||
notifyListeners();
|
|
||||||
Stores.server.put(spi);
|
Stores.server.put(spi);
|
||||||
_serverOrder.add(spi.id);
|
serverOrder.value.add(spi.id);
|
||||||
Stores.setting.serverOrder.put(_serverOrder);
|
serverOrder.notify();
|
||||||
|
Stores.setting.serverOrder.put(serverOrder.value);
|
||||||
_updateTags();
|
_updateTags();
|
||||||
refresh(spi: spi);
|
refresh(spi: spi);
|
||||||
}
|
}
|
||||||
|
|
||||||
void delServer(String id) {
|
static void delServer(String id) {
|
||||||
_servers.remove(id);
|
servers.remove(id);
|
||||||
_serverOrder.remove(id);
|
serverOrder.value.remove(id);
|
||||||
Stores.setting.serverOrder.put(_serverOrder);
|
serverOrder.notify();
|
||||||
_updateTags();
|
Stores.setting.serverOrder.put(serverOrder.value);
|
||||||
notifyListeners();
|
|
||||||
Stores.server.delete(id);
|
Stores.server.delete(id);
|
||||||
}
|
|
||||||
|
|
||||||
void deleteAll() {
|
|
||||||
_servers.clear();
|
|
||||||
_serverOrder.clear();
|
|
||||||
Stores.setting.serverOrder.put(_serverOrder);
|
|
||||||
_updateTags();
|
_updateTags();
|
||||||
notifyListeners();
|
|
||||||
Stores.server.deleteAll();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> updateServer(
|
static void deleteAll() {
|
||||||
ServerPrivateInfo old,
|
servers.clear();
|
||||||
ServerPrivateInfo newSpi,
|
serverOrder.value.clear();
|
||||||
|
serverOrder.notify();
|
||||||
|
Stores.setting.serverOrder.put(serverOrder.value);
|
||||||
|
Stores.server.deleteAll();
|
||||||
|
_updateTags();
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<void> updateServer(
|
||||||
|
Spi old,
|
||||||
|
Spi newSpi,
|
||||||
) async {
|
) async {
|
||||||
if (old != newSpi) {
|
if (old != newSpi) {
|
||||||
Stores.server.update(old, newSpi);
|
Stores.server.update(old, newSpi);
|
||||||
_servers[old.id]?.spi = newSpi;
|
servers[old.id]?.value.spi = newSpi;
|
||||||
|
|
||||||
if (newSpi.id != old.id) {
|
if (newSpi.id != old.id) {
|
||||||
_servers[newSpi.id] = _servers[old.id]!;
|
servers[newSpi.id] = servers[old.id]!;
|
||||||
_servers[newSpi.id]?.spi = newSpi;
|
servers.remove(old.id);
|
||||||
_servers.remove(old.id);
|
serverOrder.value.update(old.id, newSpi.id);
|
||||||
_serverOrder.update(old.id, newSpi.id);
|
Stores.setting.serverOrder.put(serverOrder.value);
|
||||||
Stores.setting.serverOrder.put(_serverOrder);
|
serverOrder.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only reconnect if neccessary
|
// Only reconnect if neccessary
|
||||||
@@ -242,33 +232,32 @@ class ServerProvider extends ChangeNotifier {
|
|||||||
TryLimiter.reset(newSpi.id);
|
TryLimiter.reset(newSpi.id);
|
||||||
refresh(spi: newSpi);
|
refresh(spi: newSpi);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only update if [spi.tags] changed
|
|
||||||
_updateTags();
|
|
||||||
}
|
}
|
||||||
|
_updateTags();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _setServerState(Server s, ServerConn ss) {
|
static void _setServerState(VNode<Server> s, ServerConn ss) {
|
||||||
s.conn = ss;
|
s.value.conn = ss;
|
||||||
notifyListeners();
|
s.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _getData(ServerPrivateInfo spi) async {
|
static Future<void> _getData(Spi spi) async {
|
||||||
final sid = spi.id;
|
final sid = spi.id;
|
||||||
final s = _servers[sid];
|
final s = servers[sid];
|
||||||
|
|
||||||
if (s == null) return;
|
if (s == null) return;
|
||||||
|
|
||||||
|
final sv = s.value;
|
||||||
if (!TryLimiter.canTry(sid)) {
|
if (!TryLimiter.canTry(sid)) {
|
||||||
if (s.conn != ServerConn.failed) {
|
if (sv.conn != ServerConn.failed) {
|
||||||
_setServerState(s, ServerConn.failed);
|
_setServerState(s, ServerConn.failed);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
s.status.err = null;
|
sv.status.err = null;
|
||||||
|
|
||||||
if (s.needGenClient || (s.client?.isClosed ?? true)) {
|
if (sv.needGenClient || (sv.client?.isClosed ?? true)) {
|
||||||
_setServerState(s, ServerConn.connecting);
|
_setServerState(s, ServerConn.connecting);
|
||||||
|
|
||||||
final wol = spi.wolCfg;
|
final wol = spi.wolCfg;
|
||||||
@@ -289,7 +278,7 @@ class ServerProvider extends ChangeNotifier {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
final time1 = DateTime.now();
|
final time1 = DateTime.now();
|
||||||
s.client = await genClient(
|
sv.client = await genClient(
|
||||||
spi,
|
spi,
|
||||||
timeout: Duration(seconds: Stores.setting.timeout.fetch()),
|
timeout: Duration(seconds: Stores.setting.timeout.fetch()),
|
||||||
onKeyboardInteractive: (_) => KeybordInteractive.defaultHandle(spi),
|
onKeyboardInteractive: (_) => KeybordInteractive.defaultHandle(spi),
|
||||||
@@ -303,7 +292,7 @@ class ServerProvider extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
TryLimiter.inc(sid);
|
TryLimiter.inc(sid);
|
||||||
s.status.err = SSHErr(type: SSHErrType.connect, message: e.toString());
|
sv.status.err = SSHErr(type: SSHErrType.connect, message: e.toString());
|
||||||
_setServerState(s, ServerConn.failed);
|
_setServerState(s, ServerConn.failed);
|
||||||
|
|
||||||
/// In order to keep privacy, print [spi.name] instead of [spi.id]
|
/// In order to keep privacy, print [spi.name] instead of [spi.id]
|
||||||
@@ -313,71 +302,50 @@ 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 sv.client!.exec(
|
||||||
ShellFunc.installShellCmd,
|
(session) async {
|
||||||
action: (session) async {
|
|
||||||
session.stdin.add(scriptRaw);
|
session.stdin.add(scriptRaw);
|
||||||
session.stdin.close();
|
session.stdin.close();
|
||||||
},
|
},
|
||||||
|
entry: ShellFunc.getInstallShellCmd(spi.id),
|
||||||
);
|
);
|
||||||
|
if (writeScriptResult.isNotEmpty) {
|
||||||
|
ShellFunc.switchScriptDir(spi.id);
|
||||||
|
throw 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());
|
||||||
|
sv.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());
|
||||||
|
sv.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');
|
sv.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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s.conn == ServerConn.connecting) return;
|
if (sv.conn == ServerConn.connecting) return;
|
||||||
|
|
||||||
/// Keep [finished] state, or the UI will be refreshed to [loading] state
|
/// Keep [finished] state, or the UI will be refreshed to [loading] state
|
||||||
/// instead of the '$Temp | $Uptime'.
|
/// instead of the '$Temp | $Uptime'.
|
||||||
/// eg: '32C | 7 days'
|
/// eg: '32C | 7 days'
|
||||||
if (s.conn != ServerConn.finished) {
|
if (sv.conn != ServerConn.finished) {
|
||||||
_setServerState(s, ServerConn.loading);
|
_setServerState(s, ServerConn.loading);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -385,17 +353,17 @@ class ServerProvider extends ChangeNotifier {
|
|||||||
String? raw;
|
String? raw;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
raw = await s.client?.run(ShellFunc.status.exec).string;
|
raw = await sv.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()) {
|
||||||
// Keep previous server status when err occurs
|
// Keep previous server status when err occurs
|
||||||
if (s.conn != ServerConn.failed && s.status.more.isNotEmpty) {
|
if (sv.conn != ServerConn.failed && sv.status.more.isNotEmpty) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TryLimiter.inc(sid);
|
TryLimiter.inc(sid);
|
||||||
s.status.err = SSHErr(
|
sv.status.err = SSHErr(
|
||||||
type: SSHErrType.segements,
|
type: SSHErrType.segements,
|
||||||
message: 'Seperate segments failed, raw:\n$raw',
|
message: 'Seperate segments failed, raw:\n$raw',
|
||||||
);
|
);
|
||||||
@@ -404,7 +372,7 @@ class ServerProvider extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
TryLimiter.inc(sid);
|
TryLimiter.inc(sid);
|
||||||
s.status.err = SSHErr(type: SSHErrType.getStatus, message: e.toString());
|
sv.status.err = SSHErr(type: SSHErrType.getStatus, message: e.toString());
|
||||||
_setServerState(s, ServerConn.failed);
|
_setServerState(s, ServerConn.failed);
|
||||||
Loggers.app.warning('Get status from ${spi.name} failed', e);
|
Loggers.app.warning('Get status from ${spi.name} failed', e);
|
||||||
return;
|
return;
|
||||||
@@ -415,36 +383,36 @@ class ServerProvider extends ChangeNotifier {
|
|||||||
if (!systemType.isSegmentsLenMatch(segments.length - customCmdLen)) {
|
if (!systemType.isSegmentsLenMatch(segments.length - customCmdLen)) {
|
||||||
TryLimiter.inc(sid);
|
TryLimiter.inc(sid);
|
||||||
if (raw.contains('Could not chdir to home directory /var/services/')) {
|
if (raw.contains('Could not chdir to home directory /var/services/')) {
|
||||||
s.status.err = SSHErr(type: SSHErrType.chdir, message: raw);
|
sv.status.err = SSHErr(type: SSHErrType.chdir, message: raw);
|
||||||
_setServerState(s, ServerConn.failed);
|
_setServerState(s, ServerConn.failed);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final expected = systemType.segmentsLen;
|
final expected = systemType.segmentsLen;
|
||||||
final actual = segments.length;
|
final actual = segments.length;
|
||||||
s.status.err = SSHErr(
|
sv.status.err = SSHErr(
|
||||||
type: SSHErrType.segements,
|
type: SSHErrType.segements,
|
||||||
message: 'Segments: expect $expected, got $actual, raw:\n\n$raw',
|
message: 'Segments: expect $expected, got $actual, raw:\n\n$raw',
|
||||||
);
|
);
|
||||||
_setServerState(s, ServerConn.failed);
|
_setServerState(s, ServerConn.failed);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
s.status.system = systemType;
|
sv.status.system = systemType;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final req = ServerStatusUpdateReq(
|
final req = ServerStatusUpdateReq(
|
||||||
ss: s.status,
|
ss: sv.status,
|
||||||
segments: segments,
|
segments: segments,
|
||||||
system: systemType,
|
system: systemType,
|
||||||
customCmds: spi.custom?.cmds ?? {},
|
customCmds: spi.custom?.cmds ?? {},
|
||||||
);
|
);
|
||||||
s.status = await Computer.shared.start(
|
sv.status = await Computer.shared.start(
|
||||||
getStatus,
|
getStatus,
|
||||||
req,
|
req,
|
||||||
taskName: 'StatusUpdateReq<${s.id}>',
|
taskName: 'StatusUpdateReq<${sv.id}>',
|
||||||
);
|
);
|
||||||
} catch (e, trace) {
|
} catch (e, trace) {
|
||||||
TryLimiter.inc(sid);
|
TryLimiter.inc(sid);
|
||||||
s.status.err = SSHErr(
|
sv.status.err = SSHErr(
|
||||||
type: SSHErrType.getStatus,
|
type: SSHErrType.getStatus,
|
||||||
message: 'Parse failed: $e\n\n$raw',
|
message: 'Parse failed: $e\n\n$raw',
|
||||||
);
|
);
|
||||||
@@ -458,26 +426,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.fmtWithArgs(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,39 +1,41 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
|
import 'package:server_box/data/model/sftp/worker.dart';
|
||||||
|
|
||||||
import '../model/sftp/req.dart';
|
class SftpProvider extends Provider {
|
||||||
|
const SftpProvider._();
|
||||||
|
static const instance = SftpProvider._();
|
||||||
|
|
||||||
class SftpProvider extends ChangeNotifier {
|
static final status = <SftpReqStatus>[].vn;
|
||||||
final List<SftpReqStatus> _status = [];
|
|
||||||
List<SftpReqStatus> get status => _status;
|
|
||||||
|
|
||||||
SftpReqStatus? get(int id) {
|
static SftpReqStatus? get(int id) {
|
||||||
return _status.singleWhere((element) => element.id == id);
|
return status.value.singleWhere((element) => element.id == id);
|
||||||
}
|
}
|
||||||
|
|
||||||
int add(SftpReq req, {Completer? completer}) {
|
static int add(SftpReq req, {Completer? completer}) {
|
||||||
final status = SftpReqStatus(
|
final reqStat = SftpReqStatus(
|
||||||
notifyListeners: notifyListeners,
|
notifyListeners: status.notify,
|
||||||
completer: completer,
|
completer: completer,
|
||||||
req: req,
|
req: req,
|
||||||
);
|
);
|
||||||
_status.add(status);
|
status.value.add(reqStat);
|
||||||
return status.id;
|
status.notify();
|
||||||
|
return reqStat.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
static void dispose() {
|
||||||
void dispose() {
|
for (final item in status.value) {
|
||||||
for (final item in _status) {
|
|
||||||
item.dispose();
|
item.dispose();
|
||||||
}
|
}
|
||||||
super.dispose();
|
status.value.clear();
|
||||||
|
status.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
void cancel(int id) {
|
static void cancel(int id) {
|
||||||
final idx = _status.indexWhere((element) => element.id == id);
|
final idx = status.value.indexWhere((e) => e.id == id);
|
||||||
_status[idx].dispose();
|
status.value[idx].dispose();
|
||||||
_status.removeAt(idx);
|
status.value.removeAt(idx);
|
||||||
notifyListeners();
|
status.notify();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,21 @@
|
|||||||
import 'dart:convert';
|
|
||||||
|
|
||||||
import 'package:fl_lib/fl_lib.dart';
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:server_box/data/model/server/snippet.dart';
|
import 'package:server_box/data/model/server/snippet.dart';
|
||||||
import 'package:server_box/data/res/store.dart';
|
import 'package:server_box/data/res/store.dart';
|
||||||
|
|
||||||
class SnippetProvider extends ChangeNotifier {
|
class SnippetProvider extends Provider {
|
||||||
late List<Snippet> _snippets;
|
const SnippetProvider._();
|
||||||
List<Snippet> get snippets => _snippets;
|
static const instance = SnippetProvider._();
|
||||||
|
|
||||||
final _tags = ValueNotifier(<String>[]);
|
static final snippets = <Snippet>[].vn;
|
||||||
ValueNotifier<List<String>> get tags => _tags;
|
static final tags = <String>{}.vn;
|
||||||
|
|
||||||
|
@override
|
||||||
void load() {
|
void load() {
|
||||||
_snippets = Stores.snippet.fetch();
|
super.load();
|
||||||
|
final snippets_ = Stores.snippet.fetch();
|
||||||
final order = Stores.setting.snippetOrder.fetch();
|
final order = Stores.setting.snippetOrder.fetch();
|
||||||
if (order.isNotEmpty) {
|
if (order.isNotEmpty) {
|
||||||
final surplus = _snippets.reorder(
|
final surplus = snippets_.reorder(
|
||||||
order: order,
|
order: order,
|
||||||
finder: (n, name) => n.name == name,
|
finder: (n, name) => n.name == name,
|
||||||
);
|
);
|
||||||
@@ -25,47 +24,46 @@ class SnippetProvider extends ChangeNotifier {
|
|||||||
Stores.setting.snippetOrder.put(order);
|
Stores.setting.snippetOrder.put(order);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
snippets.value = snippets_;
|
||||||
_updateTags();
|
_updateTags();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _updateTags() {
|
static void _updateTags() {
|
||||||
_tags.value.clear();
|
final tags_ = <String>{};
|
||||||
final tags = <String>{};
|
for (final s in snippets.value) {
|
||||||
for (final s in _snippets) {
|
final t = s.tags;
|
||||||
if (s.tags?.isEmpty ?? true) {
|
if (t != null) {
|
||||||
continue;
|
tags_.addAll(t);
|
||||||
}
|
}
|
||||||
tags.addAll(s.tags!);
|
|
||||||
}
|
}
|
||||||
_tags.value.addAll(tags);
|
tags.value = tags_;
|
||||||
_tags.notifyListeners();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void add(Snippet snippet) {
|
static void add(Snippet snippet) {
|
||||||
_snippets.add(snippet);
|
snippets.value.add(snippet);
|
||||||
|
snippets.notify();
|
||||||
Stores.snippet.put(snippet);
|
Stores.snippet.put(snippet);
|
||||||
_updateTags();
|
_updateTags();
|
||||||
notifyListeners();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void del(Snippet snippet) {
|
static void del(Snippet snippet) {
|
||||||
_snippets.remove(snippet);
|
snippets.value.remove(snippet);
|
||||||
|
snippets.notify();
|
||||||
Stores.snippet.delete(snippet);
|
Stores.snippet.delete(snippet);
|
||||||
_updateTags();
|
_updateTags();
|
||||||
notifyListeners();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void update(Snippet old, Snippet newOne) {
|
static void update(Snippet old, Snippet newOne) {
|
||||||
|
snippets.value.remove(old);
|
||||||
|
snippets.value.add(newOne);
|
||||||
|
snippets.notify();
|
||||||
Stores.snippet.delete(old);
|
Stores.snippet.delete(old);
|
||||||
Stores.snippet.put(newOne);
|
Stores.snippet.put(newOne);
|
||||||
_snippets.remove(old);
|
|
||||||
_snippets.add(newOne);
|
|
||||||
_updateTags();
|
_updateTags();
|
||||||
notifyListeners();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void renameTag(String old, String newOne) {
|
static void renameTag(String old, String newOne) {
|
||||||
for (final s in _snippets) {
|
for (final s in snippets.value) {
|
||||||
if (s.tags?.contains(old) ?? false) {
|
if (s.tags?.contains(old) ?? false) {
|
||||||
s.tags?.remove(old);
|
s.tags?.remove(old);
|
||||||
s.tags?.add(newOne);
|
s.tags?.add(newOne);
|
||||||
@@ -73,8 +71,5 @@ class SnippetProvider extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
_updateTags();
|
_updateTags();
|
||||||
notifyListeners();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String get export => json.encode(snippets);
|
|
||||||
}
|
}
|
||||||
|
|||||||
162
lib/data/provider/systemd.dart
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
|
import 'package:server_box/core/extension/ssh_client.dart';
|
||||||
|
import 'package:server_box/data/model/app/shell_func.dart';
|
||||||
|
import 'package:server_box/data/model/server/server.dart';
|
||||||
|
import 'package:server_box/data/model/server/server_private_info.dart';
|
||||||
|
import 'package:server_box/data/model/server/systemd.dart';
|
||||||
|
import 'package:server_box/data/provider/server.dart';
|
||||||
|
|
||||||
|
final class SystemdProvider {
|
||||||
|
late final VNode<Server> _si;
|
||||||
|
late final bool _isRoot;
|
||||||
|
|
||||||
|
SystemdProvider.init(Spi spi) {
|
||||||
|
_isRoot = spi.isRoot;
|
||||||
|
_si = ServerProvider.pick(spi: spi)!;
|
||||||
|
getUnits();
|
||||||
|
}
|
||||||
|
|
||||||
|
final isBusy = false.vn;
|
||||||
|
final units = <SystemdUnit>[].vn;
|
||||||
|
|
||||||
|
Future<void> getUnits() async {
|
||||||
|
isBusy.value = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
final client = _si.value.client;
|
||||||
|
final result = await client!.execForOutput(_getUnitsCmd);
|
||||||
|
final units = result.split('\n');
|
||||||
|
|
||||||
|
final userUnits = <String>[];
|
||||||
|
final systemUnits = <String>[];
|
||||||
|
for (final unit in units) {
|
||||||
|
if (unit.startsWith('/etc/systemd/system')) {
|
||||||
|
systemUnits.add(unit);
|
||||||
|
} else if (unit.startsWith('~/.config/systemd/user')) {
|
||||||
|
userUnits.add(unit);
|
||||||
|
} else if (unit.trim().isNotEmpty) {
|
||||||
|
Loggers.app.warning('Unknown unit: $unit');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final parsedUserUnits =
|
||||||
|
await _parseUnitObj(userUnits, SystemdUnitScope.user);
|
||||||
|
final parsedSystemUnits =
|
||||||
|
await _parseUnitObj(systemUnits, SystemdUnitScope.system);
|
||||||
|
this.units.value = [...parsedUserUnits, ...parsedSystemUnits];
|
||||||
|
} catch (e, s) {
|
||||||
|
Loggers.app.warning('Parse systemd', e, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
isBusy.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<SystemdUnit>> _parseUnitObj(
|
||||||
|
List<String> unitNames,
|
||||||
|
SystemdUnitScope scope,
|
||||||
|
) async {
|
||||||
|
final unitNames_ = unitNames
|
||||||
|
.map((e) => e.trim().split('/').last.split('.').first)
|
||||||
|
.toList();
|
||||||
|
final script = '''
|
||||||
|
for unit in ${unitNames_.join(' ')}; do
|
||||||
|
state=\$(systemctl show --no-pager \$unit)
|
||||||
|
echo -n "${ShellFunc.seperator}\n\$state"
|
||||||
|
done
|
||||||
|
''';
|
||||||
|
final client = _si.value.client!;
|
||||||
|
final result = await client.execForOutput(script);
|
||||||
|
final units = result.split(ShellFunc.seperator);
|
||||||
|
|
||||||
|
final parsedUnits = <SystemdUnit>[];
|
||||||
|
for (final unit in units) {
|
||||||
|
final parts = unit.split('\n');
|
||||||
|
var name = '';
|
||||||
|
var type = '';
|
||||||
|
var state = '';
|
||||||
|
String? description;
|
||||||
|
for (final part in parts) {
|
||||||
|
if (part.startsWith('Id=')) {
|
||||||
|
final val = _getIniVal(part).split('.');
|
||||||
|
name = val.first;
|
||||||
|
type = val.last;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (part.startsWith('ActiveState=')) {
|
||||||
|
state = _getIniVal(part);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (part.startsWith('Description=')) {
|
||||||
|
description = _getIniVal(part);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final unitType = SystemdUnitType.fromString(type);
|
||||||
|
if (unitType == null) {
|
||||||
|
Loggers.app.warning('Unit type: $type');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final unitState = SystemdUnitState.fromString(state);
|
||||||
|
if (unitState == null) {
|
||||||
|
Loggers.app.warning('Unit state: $state');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedUnits.add(SystemdUnit(
|
||||||
|
name: name,
|
||||||
|
type: unitType,
|
||||||
|
scope: scope,
|
||||||
|
state: unitState,
|
||||||
|
description: description,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedUnits.sort((a, b) {
|
||||||
|
// user units first
|
||||||
|
if (a.scope != b.scope) {
|
||||||
|
return a.scope == SystemdUnitScope.user ? -1 : 1;
|
||||||
|
}
|
||||||
|
// active units first
|
||||||
|
if (a.state != b.state) {
|
||||||
|
return a.state == SystemdUnitState.active ? -1 : 1;
|
||||||
|
}
|
||||||
|
return a.name.compareTo(b.name);
|
||||||
|
});
|
||||||
|
return parsedUnits;
|
||||||
|
}
|
||||||
|
|
||||||
|
late final _getUnitsCmd = '''
|
||||||
|
get_files() {
|
||||||
|
unit_type=\$1
|
||||||
|
base_dir=\$2
|
||||||
|
|
||||||
|
# If base_dir is not a directory, return
|
||||||
|
if [ ! -d "\$base_dir" ]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
find "\$base_dir" -type f -name "*.\$unit_type" -print | sort
|
||||||
|
}
|
||||||
|
|
||||||
|
get_type_files() {
|
||||||
|
unit_type=\$1
|
||||||
|
base_dir=""
|
||||||
|
|
||||||
|
${_isRoot ? """
|
||||||
|
get_files \$unit_type /etc/systemd/system
|
||||||
|
get_files \$unit_type ~/.config/systemd/user""" : """
|
||||||
|
get_files \$unit_type ~/.config/systemd/user"""}
|
||||||
|
}
|
||||||
|
|
||||||
|
types="service socket mount timer"
|
||||||
|
|
||||||
|
for type in \$types; do
|
||||||
|
get_type_files \$type
|
||||||
|
done
|
||||||
|
''';
|
||||||
|
}
|
||||||
|
|
||||||
|
String _getIniVal(String line) {
|
||||||
|
return line.split('=').last;
|
||||||
|
}
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
// This file is generated by fl_build. Do not edit.
|
// This file is generated by fl_build. Do not edit.
|
||||||
|
// ignore_for_file: prefer_single_quotes
|
||||||
|
|
||||||
class BuildData {
|
class BuildData {
|
||||||
static const String name = "ServerBox";
|
static const String name = "ServerBox";
|
||||||
static const int build = 992;
|
static const int build = 1070;
|
||||||
static const int script = 49;
|
static const int script = 58;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,17 +9,20 @@ abstract final class GithubIds {
|
|||||||
'azkadev',
|
'azkadev',
|
||||||
'kalashnikov',
|
'kalashnikov',
|
||||||
'calvinweb',
|
'calvinweb',
|
||||||
|
'No06',
|
||||||
'QazCetelic',
|
'QazCetelic',
|
||||||
'RainSunMe',
|
'RainSunMe',
|
||||||
'FrancXPT',
|
'FrancXPT',
|
||||||
'Liloupar',
|
'Liloupar',
|
||||||
'dccif',
|
'dccif',
|
||||||
|
'mikropsoft',
|
||||||
};
|
};
|
||||||
|
|
||||||
static const participants = <GhId>{
|
static const participants = <GhId>{
|
||||||
'jaychoubaby',
|
'jaychoubaby',
|
||||||
'fecture',
|
'fecture',
|
||||||
'Tao173',
|
'Tao173',
|
||||||
|
'Jasonzhu1207',
|
||||||
'QingAnLe',
|
'QingAnLe',
|
||||||
'wxdjs',
|
'wxdjs',
|
||||||
'Aeorq',
|
'Aeorq',
|
||||||
@@ -72,7 +75,6 @@ abstract final class GithubIds {
|
|||||||
'pgs666',
|
'pgs666',
|
||||||
'FHU-yezi',
|
'FHU-yezi',
|
||||||
'ZRY233',
|
'ZRY233',
|
||||||
'Jasonzhu1207',
|
|
||||||
'sakuraanzu',
|
'sakuraanzu',
|
||||||
'licaon-kter',
|
'licaon-kter',
|
||||||
'77160860',
|
'77160860',
|
||||||
@@ -84,6 +86,18 @@ abstract final class GithubIds {
|
|||||||
'Mooling0602',
|
'Mooling0602',
|
||||||
'IllTamer',
|
'IllTamer',
|
||||||
'marlkiller',
|
'marlkiller',
|
||||||
|
'hlarc',
|
||||||
|
'itsandrewpao',
|
||||||
|
'StudyingLover',
|
||||||
|
'QJAG1024',
|
||||||
|
'Wuming-HUST',
|
||||||
|
'WolfCanglong',
|
||||||
|
'liwenjie119',
|
||||||
|
'logce',
|
||||||
|
'h-lyf',
|
||||||
|
'88484396',
|
||||||
|
'honggeigei',
|
||||||
|
'likecreep',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
import 'package:fl_lib/fl_lib.dart';
|
|
||||||
import 'package:server_box/data/provider/app.dart';
|
|
||||||
import 'package:server_box/data/provider/private_key.dart';
|
|
||||||
import 'package:server_box/data/provider/server.dart';
|
|
||||||
import 'package:server_box/data/provider/sftp.dart';
|
|
||||||
import 'package:server_box/data/provider/snippet.dart';
|
|
||||||
|
|
||||||
abstract final class Pros {
|
|
||||||
static final app = AppProvider();
|
|
||||||
static final debug = DebugProvider(maxLines: 177);
|
|
||||||
static final key = PrivateKeyProvider();
|
|
||||||
static final server = ServerProvider();
|
|
||||||
static final sftp = SftpProvider();
|
|
||||||
static final snippet = SnippetProvider();
|
|
||||||
|
|
||||||
static void reload() {
|
|
||||||
key.load();
|
|
||||||
server.load();
|
|
||||||
snippet.load();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
import 'package:server_box/data/model/server/server.dart';
|
import 'package:server_box/data/model/server/server.dart';
|
||||||
import 'package:server_box/data/model/server/temp.dart';
|
import 'package:server_box/data/model/server/temp.dart';
|
||||||
|
|
||||||
import '../model/server/cpu.dart';
|
import 'package:server_box/data/model/server/cpu.dart';
|
||||||
import '../model/server/disk.dart';
|
import 'package:server_box/data/model/server/disk.dart';
|
||||||
import '../model/server/memory.dart';
|
import 'package:server_box/data/model/server/memory.dart';
|
||||||
import '../model/server/net_speed.dart';
|
import 'package:server_box/data/model/server/net_speed.dart';
|
||||||
import '../model/server/conn.dart';
|
import 'package:server_box/data/model/server/conn.dart';
|
||||||
import '../model/server/system.dart';
|
import 'package:server_box/data/model/server/system.dart';
|
||||||
|
|
||||||
abstract final class InitStatus {
|
abstract final class InitStatus {
|
||||||
static SingleCpuCore get _initOneTimeCpuStatus => SingleCpuCore(
|
static SingleCpuCore get _initOneTimeCpuStatus => SingleCpuCore(
|
||||||
|
|||||||
@@ -23,6 +23,10 @@ abstract final class Stores {
|
|||||||
snippet,
|
snippet,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
static Future<void> init() async {
|
||||||
|
await Future.wait(all.map((store) => store.init()));
|
||||||
|
}
|
||||||
|
|
||||||
static int? get lastModTime {
|
static int? get lastModTime {
|
||||||
int? lastModTime = 0;
|
int? lastModTime = 0;
|
||||||
for (final store in all) {
|
for (final store in all) {
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
abstract final class Urls {
|
abstract final class Urls {
|
||||||
static const cdnBase = 'https://cdn.lolli.tech/serverbox';
|
static const cdnBase = 'https://cdn.lpkt.cn/serverbox';
|
||||||
static const updateCfg = '$cdnBase/update2.json';
|
static const updateCfg = '$cdnBase/update2.json';
|
||||||
static const myGithub = 'https://github.com/lollipopkit';
|
static const myGithub = 'https://github.com/lollipopkit';
|
||||||
static const thisRepo = '$myGithub/flutter_server_box';
|
static const thisRepo = '$myGithub/flutter_server_box';
|
||||||
static const appHelp = '$thisRepo#-help';
|
static const appHelp = '$thisRepo#-help';
|
||||||
static const appWiki = '$thisRepo/wiki';
|
static const appWiki = '$thisRepo/wiki';
|
||||||
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 '../model/server/private_key_info.dart';
|
import 'package:server_box/data/model/server/private_key_info.dart';
|
||||||
|
|
||||||
class PrivateKeyStore extends PersistentStore {
|
class PrivateKeyStore extends PersistentStore {
|
||||||
PrivateKeyStore() : super('key');
|
PrivateKeyStore() : super('key');
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
import 'package:fl_lib/fl_lib.dart';
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
|
|
||||||
import '../model/server/server_private_info.dart';
|
import 'package:server_box/data/model/server/server_private_info.dart';
|
||||||
|
|
||||||
class ServerStore extends PersistentStore {
|
class ServerStore extends PersistentStore {
|
||||||
ServerStore() : super('server');
|
ServerStore() : super('server');
|
||||||
|
|
||||||
void put(ServerPrivateInfo info) {
|
void put(Spi info) {
|
||||||
box.put(info.id, info);
|
box.put(info.id, info);
|
||||||
box.updateLastModified();
|
box.updateLastModified();
|
||||||
}
|
}
|
||||||
|
|
||||||
List<ServerPrivateInfo> fetch() {
|
List<Spi> fetch() {
|
||||||
final ids = box.keys;
|
final ids = box.keys;
|
||||||
final List<ServerPrivateInfo> ss = [];
|
final List<Spi> ss = [];
|
||||||
for (final id in ids) {
|
for (final id in ids) {
|
||||||
final s = box.get(id);
|
final s = box.get(id);
|
||||||
if (s != null && s is ServerPrivateInfo) {
|
if (s != null && s is Spi) {
|
||||||
ss.add(s);
|
ss.add(s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -32,7 +32,7 @@ class ServerStore extends PersistentStore {
|
|||||||
box.updateLastModified();
|
box.updateLastModified();
|
||||||
}
|
}
|
||||||
|
|
||||||
void update(ServerPrivateInfo old, ServerPrivateInfo newInfo) {
|
void update(Spi old, Spi newInfo) {
|
||||||
if (!have(old)) {
|
if (!have(old)) {
|
||||||
throw Exception('Old spi: $old not found');
|
throw Exception('Old spi: $old not found');
|
||||||
}
|
}
|
||||||
@@ -40,5 +40,5 @@ class ServerStore extends PersistentStore {
|
|||||||
put(newInfo);
|
put(newInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool have(ServerPrivateInfo s) => box.get(s.id) != null;
|
bool have(Spi s) => box.get(s.id) != null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import 'package:server_box/data/model/app/menu/server_func.dart';
|
|||||||
import 'package:server_box/data/model/app/server_detail_card.dart';
|
import 'package:server_box/data/model/app/server_detail_card.dart';
|
||||||
import 'package:server_box/data/model/ssh/virtual_key.dart';
|
import 'package:server_box/data/model/ssh/virtual_key.dart';
|
||||||
|
|
||||||
import '../model/app/net_view.dart';
|
import 'package:server_box/data/model/app/net_view.dart';
|
||||||
import '../res/default.dart';
|
import 'package:server_box/data/res/default.dart';
|
||||||
|
|
||||||
class SettingStore extends PersistentStore {
|
class SettingStore extends PersistentStore {
|
||||||
SettingStore() : super('setting');
|
SettingStore() : super('setting');
|
||||||
@@ -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);
|
||||||
@@ -232,7 +180,7 @@ class SettingStore extends PersistentStore {
|
|||||||
|
|
||||||
/// Ignore local network device (eg: br-xxx, ovs-system...)
|
/// Ignore local network device (eg: br-xxx, ovs-system...)
|
||||||
/// when building traffic view on server tab
|
/// when building traffic view on server tab
|
||||||
late final ignoreLocalNet = property('ignoreLocalNet', true);
|
//late final ignoreLocalNet = property('ignoreLocalNet', true);
|
||||||
|
|
||||||
/// Remerber pwd in memory
|
/// Remerber pwd in memory
|
||||||
/// Used for [DialogX.showPwdDialog]
|
/// Used for [DialogX.showPwdDialog]
|
||||||
@@ -280,7 +228,13 @@ class SettingStore extends PersistentStore {
|
|||||||
/// Format: {width}x{height}
|
/// Format: {width}x{height}
|
||||||
late final windowSize = property('windowSize', '');
|
late final windowSize = property('windowSize', '');
|
||||||
|
|
||||||
late final showIntro = property('showIntro', true);
|
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
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:fl_lib/fl_lib.dart';
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
|
|
||||||
import '../model/server/snippet.dart';
|
import 'package:server_box/data/model/server/snippet.dart';
|
||||||
|
|
||||||
class SnippetStore extends PersistentStore {
|
class SnippetStore extends PersistentStore {
|
||||||
SnippetStore() : super('snippet');
|
SnippetStore() : super('snippet');
|
||||||
|
|||||||
117
lib/intro.dart
@@ -1,107 +1,102 @@
|
|||||||
part of 'app.dart';
|
part of 'app.dart';
|
||||||
|
|
||||||
final class _IntroPage extends StatelessWidget {
|
final class _IntroPage extends StatelessWidget {
|
||||||
const _IntroPage();
|
final List<IntroPageBuilder> pages;
|
||||||
|
|
||||||
static final _setting = Stores.setting;
|
const _IntroPage(this.pages);
|
||||||
static const _kIconSize = 23.0;
|
|
||||||
static const _introListPad = EdgeInsets.symmetric(horizontal: 17);
|
static const _builders = {
|
||||||
|
1: _buildAppSettings,
|
||||||
|
};
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return LayoutBuilder(
|
return LayoutBuilder(
|
||||||
builder: (context, cons) {
|
builder: (context, cons) {
|
||||||
final padTop = cons.maxHeight * .2;
|
final padTop = cons.maxHeight * .16;
|
||||||
|
final pages_ = pages.map((e) => e(context, padTop)).toList();
|
||||||
return IntroPage(
|
return IntroPage(
|
||||||
pages: [
|
args: IntroPageArgs(
|
||||||
_buildAppSettings(context, padTop),
|
pages: pages_,
|
||||||
_buildRecommended(context, padTop),
|
onDone: (ctx) {
|
||||||
],
|
Stores.setting.introVer.put(BuildData.build);
|
||||||
onDone: (ctx) {
|
Navigator.of(ctx).pushReplacement(
|
||||||
Stores.setting.showIntro.put(false);
|
MaterialPageRoute(builder: (_) => const HomePage()),
|
||||||
Navigator.of(ctx).pushReplacement(
|
);
|
||||||
MaterialPageRoute(builder: (_) => const HomePage()),
|
},
|
||||||
);
|
),
|
||||||
},
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildRecommended(BuildContext context, double padTop) {
|
static Widget _buildAppSettings(BuildContext ctx, double padTop) {
|
||||||
return ListView(
|
return ListView(
|
||||||
padding: _introListPad,
|
padding: _introListPad,
|
||||||
children: [
|
children: [
|
||||||
SizedBox(height: padTop),
|
SizedBox(height: padTop),
|
||||||
const Icon(Bootstrap.stars, size: 35),
|
IntroPage.title(text: l10n.init, big: true),
|
||||||
SizedBox(height: padTop),
|
|
||||||
ListTile(
|
|
||||||
leading: const Icon(MingCute.delete_2_fill),
|
|
||||||
title: const Text('rm -r'),
|
|
||||||
subtitle: Text(l10n.sftpRmrDirSummary, style: UIs.textGrey),
|
|
||||||
trailing: StoreSwitch(prop: _setting.sftpRmrDir),
|
|
||||||
).cardx,
|
|
||||||
ListTile(
|
|
||||||
leading: const Icon(IonIcons.stats_chart, size: _kIconSize),
|
|
||||||
title: Text(l10n.parseContainerStats),
|
|
||||||
subtitle: Text(l10n.parseContainerStatsTip, style: UIs.textGrey),
|
|
||||||
trailing: StoreSwitch(prop: _setting.containerParseStat),
|
|
||||||
).cardx,
|
|
||||||
ListTile(
|
|
||||||
leading: const Icon(OctIcons.cpu),
|
|
||||||
title: Text('CPU ${l10n.noLineChart}'),
|
|
||||||
subtitle: Text(l10n.cpuViewAsProgressTip, style: UIs.textGrey),
|
|
||||||
trailing: StoreSwitch(prop: _setting.cpuViewAsProgress),
|
|
||||||
).cardx,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildAppSettings(BuildContext ctx, double padTop) {
|
|
||||||
return ListView(
|
|
||||||
padding: _introListPad,
|
|
||||||
children: [
|
|
||||||
SizedBox(height: padTop),
|
|
||||||
_buildTitle(l10n.init, big: true),
|
|
||||||
SizedBox(height: padTop),
|
SizedBox(height: padTop),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(IonIcons.language),
|
leading: const Icon(IonIcons.language),
|
||||||
title: Text(l10n.language),
|
title: Text(libL10n.language),
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
final selected = await ctx.showPickSingleDialog(
|
final selected = await ctx.showPickSingleDialog(
|
||||||
title: l10n.language,
|
title: libL10n.language,
|
||||||
items: AppLocalizations.supportedLocales,
|
items: AppLocalizations.supportedLocales,
|
||||||
name: (p0) => p0.code,
|
display: (p0) => p0.nativeName,
|
||||||
initial: _setting.locale.fetch().toLocale,
|
initial: _setting.locale.fetch().toLocale,
|
||||||
);
|
);
|
||||||
if (selected != null) {
|
if (selected != null) {
|
||||||
_setting.locale.put(selected.code);
|
_setting.locale.put(selected.code);
|
||||||
RNodes.app.build();
|
RNodes.app.notify();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
trailing: Text(
|
trailing: Text(
|
||||||
l10n.languageName,
|
ctx.localeNativeName,
|
||||||
style: const TextStyle(fontSize: 15, color: Colors.grey),
|
style: const TextStyle(fontSize: 15, color: Colors.grey),
|
||||||
),
|
),
|
||||||
).cardx,
|
).cardx,
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.update),
|
leading: const Icon(Icons.update),
|
||||||
title: Text(l10n.autoCheckUpdate),
|
title: Text(libL10n.autoCheckUpdate),
|
||||||
subtitle: Text(l10n.fdroidReleaseTip, style: UIs.textGrey),
|
subtitle: Text(l10n.fdroidReleaseTip, style: UIs.textGrey),
|
||||||
trailing: StoreSwitch(prop: _setting.autoCheckAppUpdate),
|
trailing: StoreSwitch(prop: _setting.autoCheckAppUpdate),
|
||||||
).cardx,
|
).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,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildTitle(String text, {bool big = false}) {
|
static List<IntroPageBuilder> get builders {
|
||||||
return Center(
|
final storedVer = _setting.introVer.fetch();
|
||||||
child: Text(
|
return _builders.entries
|
||||||
text,
|
.where((e) => e.key > storedVer)
|
||||||
style: big
|
.map((e) => e.value)
|
||||||
? const TextStyle(fontSize: 41, fontWeight: FontWeight.w500)
|
.toList();
|
||||||
: UIs.textGrey,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static final _setting = Stores.setting;
|
||||||
|
static const _kIconSize = 23.0;
|
||||||
|
static const _introListPad = EdgeInsets.symmetric(horizontal: 17);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,87 +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",
|
||||||
"acceptBeta": "Akzeptieren Sie Testversion-Updates",
|
"acceptBeta": "Akzeptieren Sie Testversion-Updates",
|
||||||
"add": "Neu",
|
|
||||||
"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",
|
||||||
"beforeConnect": "ServerBox wird nach der Verbindung ein Skript in `~/.config/server_box` schreiben und ausführen. Für weitere technische Details besuchen Sie bitte [Github]({url}).",
|
|
||||||
"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",
|
||||||
"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",
|
||||||
@@ -89,77 +44,48 @@
|
|||||||
"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",
|
||||||
"fdroidReleaseTip": "Wenn Sie diese App von Fdroid heruntergeladen haben, wird empfohlen, diese Option zu deaktivieren.",
|
"fdroidReleaseTip": "Wenn Sie diese App von F-Droid heruntergeladen haben, wird empfohlen, diese Option zu deaktivieren.",
|
||||||
"feedback": "Feedback",
|
|
||||||
"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",
|
||||||
"forExample": "Zum Beispiel",
|
|
||||||
"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",
|
||||||
"hideTitleBarTip": "Nach dem Einschalten halten Sie bitte die drei Tasten in der oberen rechten Ecke gedrückt, um sie zu ziehen.",
|
|
||||||
"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",
|
|
||||||
"inAppUpdate": "Im App aktualisieren? Andernfalls mit einem Browser herunterladen.",
|
|
||||||
"init": "Initialisieren",
|
"init": "Initialisieren",
|
||||||
"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",
|
||||||
@@ -167,60 +93,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",
|
||||||
@@ -230,16 +132,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",
|
"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",
|
||||||
@@ -254,14 +151,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",
|
||||||
@@ -270,17 +161,19 @@
|
|||||||
"size": "Größe",
|
"size": "Größe",
|
||||||
"snippet": "Snippet",
|
"snippet": "Snippet",
|
||||||
"softWrap": "Weicher Umbruch",
|
"softWrap": "Weicher Umbruch",
|
||||||
|
"specifyDev": "Gerät angeben",
|
||||||
|
"specifyDevTip": "Zum Beispiel bezieht sich die Standard-Netzwerkverkehrsstatistik auf alle Geräte. Hier können Sie ein bestimmtes Gerät angeben.",
|
||||||
"speed": "Tempo",
|
"speed": "Tempo",
|
||||||
"spentTime": "Benötigte Zeit: {time}",
|
"spentTime": "Benötigte Zeit: {time}",
|
||||||
"sshTermHelp": "Wenn das Terminal scrollbar ist, kann durch horizontales Ziehen Text ausgewählt werden. Durch Klicken auf die Tastentaste wird die Tastatur ein- oder ausgeschaltet. Das Dateisymbol öffnet den aktuellen Pfad SFTP. Die Zwischenablage-Schaltfläche kopiert den Inhalt, wenn Text ausgewählt ist, und fügt Inhalte aus der Zwischenablage in das Terminal ein, wenn kein Text ausgewählt ist und Inhalte in der Zwischenablage vorhanden sind. Das Codesymbol fügt Code-Schnipsel ins Terminal ein und führt sie aus.",
|
"sshTermHelp": "Wenn das Terminal scrollbar ist, kann durch horizontales Ziehen Text ausgewählt werden. Durch Klicken auf die Tastentaste wird die Tastatur ein- oder ausgeschaltet. Das Dateisymbol öffnet den aktuellen Pfad SFTP. Die Zwischenablage-Schaltfläche kopiert den Inhalt, wenn Text ausgewählt ist, und fügt Inhalte aus der Zwischenablage in das Terminal ein, wenn kein Text ausgewählt ist und Inhalte in der Zwischenablage vorhanden sind. Das Codesymbol fügt Code-Schnipsel ins Terminal ein und führt sie aus.",
|
||||||
"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.",
|
||||||
@@ -296,35 +189,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.",
|
||||||
@@ -335,8 +218,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,87 +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.",
|
||||||
"acceptBeta": "Accept test version updates",
|
"acceptBeta": "Accept beta version updates",
|
||||||
"add": "Add",
|
"addSystemPrivateKeyTip": "Currently private keys don't exist, do you want to add the one that comes with the system (~/.ssh/id_rsa)?",
|
||||||
"addAServer": "add a server",
|
|
||||||
"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",
|
||||||
"beforeConnect": "ServerBox will write and execute a script in `~/.config/server_box` after connection. For more technical details, please visit [Github]({url}).",
|
"bgRun": "Run in background",
|
||||||
"bgRun": "Run in backgroud",
|
"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\".",
|
||||||
"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\".",
|
|
||||||
"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",
|
||||||
"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",
|
||||||
@@ -89,138 +44,85 @@
|
|||||||
"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.",
|
||||||
"fdroidReleaseTip": "If you downloaded this app from Fdroid, 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",
|
||||||
"forExample": "For example",
|
|
||||||
"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.",
|
"highlight": "Code highlighting",
|
||||||
"highlight": "Code highlight",
|
|
||||||
"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",
|
|
||||||
"inAppUpdate": "Update within the app? Otherwise, download using a browser.",
|
|
||||||
"init": "Initialize",
|
"init": "Initialize",
|
||||||
"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",
|
||||||
@@ -230,16 +132,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",
|
"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",
|
||||||
@@ -252,16 +149,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",
|
||||||
@@ -270,20 +161,22 @@
|
|||||||
"size": "Size",
|
"size": "Size",
|
||||||
"snippet": "Snippet",
|
"snippet": "Snippet",
|
||||||
"softWrap": "Soft wrap",
|
"softWrap": "Soft wrap",
|
||||||
|
"specifyDev": "Specify device",
|
||||||
|
"specifyDevTip": "For example, network traffic statistics are by default for all devices. You can specify a particular device here.",
|
||||||
"speed": "Speed",
|
"speed": "Speed",
|
||||||
"spentTime": "Spent time: {time}",
|
"spentTime": "Spent time: {time}",
|
||||||
"sshTermHelp": "When the terminal is scrollable, dragging horizontally can select text. Clicking the keyboard button turns the keyboard on/off. The file icon opens the current path SFTP. The clipboard button copies the content when text is selected, and pastes content from the clipboard into the terminal when no text is selected and there is content on the clipboard. The code icon pastes code snippets into the terminal and executes them.",
|
"sshTermHelp": "When the terminal is scrollable, dragging horizontally can select text. Clicking the keyboard button turns the keyboard on/off. The file icon opens the current path SFTP. The clipboard button copies the content when text is selected, and pastes content from the clipboard into the terminal when no text is selected and there is content on the clipboard. The code icon pastes code snippets into the terminal and executes them.",
|
||||||
"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.",
|
||||||
@@ -296,47 +189,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,87 +1,42 @@
|
|||||||
{
|
{
|
||||||
"@@locale": "es",
|
"@@locale": "es",
|
||||||
"about": "Acerca de",
|
|
||||||
"aboutThanks": "Gracias a los siguientes participantes.",
|
"aboutThanks": "Gracias a los siguientes participantes.",
|
||||||
"acceptBeta": "Aceptar actualizaciones de la versión de prueba",
|
"acceptBeta": "Aceptar actualizaciones de la versión de prueba",
|
||||||
"add": "Añadir",
|
|
||||||
"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",
|
||||||
"beforeConnect": "ServerBox escribirá y ejecutará un script en `~/.config/server_box` después de la conexión. Para más detalles técnicos, por favor visite [Github]({url}).",
|
|
||||||
"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?",
|
||||||
"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",
|
||||||
@@ -89,77 +44,48 @@
|
|||||||
"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",
|
||||||
"fdroidReleaseTip": "Si descargaste esta aplicación desde Fdroid, se recomienda desactivar esta opción.",
|
"fdroidReleaseTip": "Si descargaste esta aplicación desde F-Droid, se recomienda desactivar esta opción.",
|
||||||
"feedback": "Retroalimentació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",
|
||||||
"forExample": "Por ejemplo",
|
|
||||||
"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",
|
||||||
"hideTitleBarTip": "Después de encenderlo, mantenga presionados los tres botones en la esquina superior derecha para arrastrar.",
|
|
||||||
"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",
|
|
||||||
"inAppUpdate": "¿Actualizar dentro de la app? De lo contrario, descargar usando un navegador.",
|
|
||||||
"init": "Inicializar",
|
"init": "Inicializar",
|
||||||
"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",
|
||||||
@@ -167,60 +93,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",
|
||||||
@@ -230,16 +132,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",
|
"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",
|
||||||
@@ -254,14 +151,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",
|
||||||
@@ -270,17 +161,19 @@
|
|||||||
"size": "Tamaño",
|
"size": "Tamaño",
|
||||||
"snippet": "Fragmento de código",
|
"snippet": "Fragmento de código",
|
||||||
"softWrap": "Salto de línea suave",
|
"softWrap": "Salto de línea suave",
|
||||||
|
"specifyDev": "Especificar dispositivo",
|
||||||
|
"specifyDevTip": "Por ejemplo, las estadísticas de tráfico de red son por defecto para todos los dispositivos. Aquí puede especificar un dispositivo en particular.",
|
||||||
"speed": "Velocidad",
|
"speed": "Velocidad",
|
||||||
"spentTime": "Tiempo gastado: {time}",
|
"spentTime": "Tiempo gastado: {time}",
|
||||||
"sshTermHelp": "Cuando el terminal es desplazable, arrastrar horizontalmente puede seleccionar texto. Hacer clic en el botón del teclado enciende/apaga el teclado. El icono de archivo abre el SFTP de la ruta actual. El botón del portapapeles copia el contenido cuando se selecciona texto y pega el contenido del portapapeles en el terminal cuando no se selecciona texto y hay contenido en el portapapeles. El icono de código pega fragmentos de código en el terminal y los ejecuta.",
|
"sshTermHelp": "Cuando el terminal es desplazable, arrastrar horizontalmente puede seleccionar texto. Hacer clic en el botón del teclado enciende/apaga el teclado. El icono de archivo abre el SFTP de la ruta actual. El botón del portapapeles copia el contenido cuando se selecciona texto y pega el contenido del portapapeles en el terminal cuando no se selecciona texto y hay contenido en el portapapeles. El icono de código pega fragmentos de código en el terminal y los ejecuta.",
|
||||||
"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.",
|
||||||
@@ -296,35 +189,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.",
|
||||||
@@ -335,8 +218,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."
|
||||||
}
|
}
|
||||||
@@ -1,87 +1,42 @@
|
|||||||
{
|
{
|
||||||
"@@locale": "fr",
|
"@@locale": "fr",
|
||||||
"about": "À propos",
|
|
||||||
"aboutThanks": "Merci aux personnes suivantes qui ont participé.",
|
"aboutThanks": "Merci aux personnes suivantes qui ont participé.",
|
||||||
"acceptBeta": "Accepter les mises à jour de la version de test",
|
"acceptBeta": "Accepter les mises à jour de la version de test",
|
||||||
"add": "Ajouter",
|
|
||||||
"addAServer": "Ajouter un serveur",
|
|
||||||
"addPrivateKey": "Ajouter une clé privée",
|
|
||||||
"addSystemPrivateKeyTip": "Actuellement, vous n'avez aucune clé privée. Souhaitez-vous ajouter celle qui vient avec le système (~/.ssh/id_rsa) ?",
|
"addSystemPrivateKeyTip": "Actuellement, vous n'avez aucune clé privée. Souhaitez-vous ajouter celle qui vient avec le système (~/.ssh/id_rsa) ?",
|
||||||
"added2List": "Ajouté à la liste des tâches",
|
"added2List": "Ajouté à la liste des tâches",
|
||||||
"addr": "Adresse",
|
"addr": "Adresse",
|
||||||
"all": "Tous",
|
|
||||||
"alreadyLastDir": "Déjà dans le dernier répertoire.",
|
"alreadyLastDir": "Déjà dans le dernier répertoire.",
|
||||||
"alterUrl": "Modifier l'URL",
|
|
||||||
"askContinue": "{msg}. Continuer ?",
|
|
||||||
"attention": "Attention",
|
|
||||||
"authFailTip": "Échec de l'authentification. Veuillez vérifier si le mot de passe/clé/hôte/utilisateur, etc., est incorrect.",
|
"authFailTip": "Échec de l'authentification. Veuillez vérifier si le mot de passe/clé/hôte/utilisateur, etc., est incorrect.",
|
||||||
"authRequired": "Authentification requise",
|
|
||||||
"auto": "Automatique",
|
|
||||||
"autoBackupConflict": "Un seul sauvegarde automatique peut être activé en même temps.",
|
"autoBackupConflict": "Un seul sauvegarde automatique peut être activé en même temps.",
|
||||||
"autoCheckUpdate": "Vérification automatique des mises à jour",
|
|
||||||
"autoConnect": "Connexion automatique",
|
"autoConnect": "Connexion automatique",
|
||||||
"autoRun": "Exécution automatique",
|
"autoRun": "Exécution automatique",
|
||||||
"autoUpdateHomeWidget": "Mise à jour automatique du widget d'accueil",
|
"autoUpdateHomeWidget": "Mise à jour automatique du widget d'accueil",
|
||||||
"backup": "Sauvegarde",
|
|
||||||
"backupTip": "Les données exportées sont simplement chiffrées. \nVeuillez les garder en sécurité.",
|
"backupTip": "Les données exportées sont simplement chiffrées. \nVeuillez les garder en sécurité.",
|
||||||
"backupVersionNotMatch": "La version de sauvegarde ne correspond pas.",
|
"backupVersionNotMatch": "La version de sauvegarde ne correspond pas.",
|
||||||
"battery": "Batterie",
|
"battery": "Batterie",
|
||||||
"beforeConnect": "ServerBox écrira et exécutera un script dans `~/.config/server_box` après la connexion. Pour plus de détails techniques, veuillez visiter [Github]({url}).",
|
|
||||||
"bgRun": "Exécution en arrière-plan",
|
"bgRun": "Exécution en arrière-plan",
|
||||||
"bgRunTip": "Cette option signifie seulement que le programme essaiera de s'exécuter en arrière-plan, que cela soit possible dépend de l'autorisation activée ou non. Pour Android natif, veuillez désactiver l'« Optimisation de la batterie » dans cette application, et pour MIUI, veuillez changer la politique d'économie d'énergie en « Illimité ».",
|
"bgRunTip": "Cette option signifie seulement que le programme essaiera de s'exécuter en arrière-plan, que cela soit possible dépend de l'autorisation activée ou non. Pour Android natif, veuillez désactiver l'« Optimisation de la batterie » dans cette application, et pour MIUI, veuillez changer la politique d'économie d'énergie en « Illimité ».",
|
||||||
"bioAuth": "Authentification biométrique",
|
|
||||||
"browser": "Navigateur",
|
|
||||||
"bulkImportServers": "Importation groupée des serveurs",
|
|
||||||
"bulkImportServersTip": "Vous pouvez trouver le [format]({url}) ici.",
|
|
||||||
"canPullRefresh": "Vous pouvez tirer pour rafraîchir.",
|
|
||||||
"cancel": "Annuler",
|
|
||||||
"choose": "Choisir",
|
|
||||||
"chooseFontFile": "Choisir un fichier de police",
|
|
||||||
"choosePrivateKey": "Choisir une clé privée",
|
|
||||||
"clear": "Effacer",
|
|
||||||
"clipboard": "Presse-papiers",
|
|
||||||
"close": "Fermer",
|
|
||||||
"cmd": "Commande",
|
"cmd": "Commande",
|
||||||
"cnKeyboardComp": "Compatibilité avec Android chinois",
|
|
||||||
"cnKeyboardCompTip": "Si le terminal affiche un clavier sécurisé, vous pouvez l'activer.",
|
|
||||||
"collapseUI": "Réduire",
|
|
||||||
"collapseUITip": "Indique si les longues listes présentées dans l'interface utilisateur doivent être réduites par défaut.",
|
"collapseUITip": "Indique si les longues listes présentées dans l'interface utilisateur doivent être réduites par défaut.",
|
||||||
"conn": "Connexion",
|
"conn": "Connexion",
|
||||||
"connected": "Connecté",
|
|
||||||
"container": "Conteneur",
|
"container": "Conteneur",
|
||||||
"containerName": "Nom du conteneur",
|
|
||||||
"containerStatus": "État du conteneur",
|
|
||||||
"containerTrySudoTip": "Par exemple : Dans l'application, l'utilisateur est défini comme aaa, mais Docker est installé sous l'utilisateur root. Dans ce cas, vous devez activer cette option.",
|
"containerTrySudoTip": "Par exemple : Dans l'application, l'utilisateur est défini comme aaa, mais Docker est installé sous l'utilisateur root. Dans ce cas, vous devez activer cette option.",
|
||||||
"content": "Contenu",
|
|
||||||
"convert": "Convertir",
|
"convert": "Convertir",
|
||||||
"copy": "Copier",
|
|
||||||
"copyPath": "Copier le chemin",
|
"copyPath": "Copier le chemin",
|
||||||
"cpuViewAsProgressTip": "Afficher le taux d'utilisation de chaque CPU sous forme de barre de progression (ancien style)",
|
"cpuViewAsProgressTip": "Afficher le taux d'utilisation de chaque CPU sous forme de barre de progression (ancien style)",
|
||||||
"createFile": "Créer un fichier",
|
|
||||||
"createFolder": "Créer un dossier",
|
|
||||||
"cursorType": "Type de curseur",
|
"cursorType": "Type de curseur",
|
||||||
"customCmd": "Commandes personnalisées",
|
"customCmd": "Commandes personnalisées",
|
||||||
"customCmdDocUrl": "https://github.com/lollipopkit/flutter_server_box/wiki#custom-commands",
|
"customCmdDocUrl": "https://github.com/lollipopkit/flutter_server_box/wiki#custom-commands",
|
||||||
"customCmdHint": "\"Nom de la commande\": \"Commande\"",
|
"customCmdHint": "\"Nom de la commande\": \"Commande\"",
|
||||||
"dark": "Sombre",
|
|
||||||
"day": "Jour",
|
|
||||||
"debug": "Débogage",
|
|
||||||
"decode": "Décoder",
|
"decode": "Décoder",
|
||||||
"decompress": "Décompresser",
|
"decompress": "Décompresser",
|
||||||
"delete": "Supprimer",
|
|
||||||
"deleteScripts": "Supprimer les scripts du serveur en même temps",
|
|
||||||
"deleteServers": "Supprimer des serveurs en lot",
|
"deleteServers": "Supprimer des serveurs en lot",
|
||||||
"deviceName": "Nom de l'appareil",
|
|
||||||
"dirEmpty": "Assurez-vous que le répertoire est vide.",
|
"dirEmpty": "Assurez-vous que le répertoire est vide.",
|
||||||
"disabled": "Désactivé",
|
|
||||||
"disconnected": "Déconnecté",
|
"disconnected": "Déconnecté",
|
||||||
"disk": "Disque",
|
"disk": "Disque",
|
||||||
"diskIgnorePath": "Chemin à ignorer pour le disque",
|
"diskIgnorePath": "Chemin à ignorer pour le disque",
|
||||||
"displayCpuIndex": "Afficher l'index CPU",
|
"displayCpuIndex": "Afficher l'index CPU",
|
||||||
"displayName": "Nom d'affichage",
|
|
||||||
"dl2Local": "Télécharger {fileName} localement ?",
|
"dl2Local": "Télécharger {fileName} localement ?",
|
||||||
"doc": "Documentation",
|
|
||||||
"dockerEditHost": "Modifier DOCKER_HOST",
|
|
||||||
"dockerEmptyRunningItems": "Aucun conteneur en cours d'exécution.\nCela peut être dû à :\n- L'utilisateur d'installation de Docker n'est pas le même que celui configuré dans l'application.\n- La variable d'environnement DOCKER_HOST n'a pas été lue correctement. Vous pouvez l'obtenir en exécutant `echo $DOCKER_HOST` dans le terminal.",
|
"dockerEmptyRunningItems": "Aucun conteneur en cours d'exécution.\nCela peut être dû à :\n- L'utilisateur d'installation de Docker n'est pas le même que celui configuré dans l'application.\n- La variable d'environnement DOCKER_HOST n'a pas été lue correctement. Vous pouvez l'obtenir en exécutant `echo $DOCKER_HOST` dans le terminal.",
|
||||||
"dockerImagesFmt": "{count} images",
|
"dockerImagesFmt": "{count} images",
|
||||||
"dockerNotInstalled": "Docker non installé",
|
"dockerNotInstalled": "Docker non installé",
|
||||||
@@ -89,77 +44,48 @@
|
|||||||
"dockerStatusRunningFmt": "{count} conteneur en cours d'exécution.",
|
"dockerStatusRunningFmt": "{count} conteneur en cours d'exécution.",
|
||||||
"doubleColumnMode": "Mode double colonne",
|
"doubleColumnMode": "Mode double colonne",
|
||||||
"doubleColumnTip": "Cette option n'active que la fonctionnalité, qu'elle puisse être activée dépend de la largeur de l'appareil.",
|
"doubleColumnTip": "Cette option n'active que la fonctionnalité, qu'elle puisse être activée dépend de la largeur de l'appareil.",
|
||||||
"download": "Télécharger",
|
|
||||||
"edit": "Éditer",
|
|
||||||
"editVirtKeys": "Modifier les touches virtuelles",
|
"editVirtKeys": "Modifier les touches virtuelles",
|
||||||
"editor": "Éditeur",
|
"editor": "Éditeur",
|
||||||
"editorHighlightTip": "La performance actuelle de mise en surbrillance du code est pire et peut être désactivée en option pour s'améliorer.",
|
"editorHighlightTip": "La performance actuelle de mise en surbrillance du code est pire et peut être désactivée en option pour s'améliorer.",
|
||||||
"encode": "Encoder",
|
"encode": "Encoder",
|
||||||
"error": "Erreur",
|
"envVars": "Variable d’environnement",
|
||||||
"exampleName": "Nom de l'exemple",
|
|
||||||
"experimentalFeature": "Fonctionnalité expérimentale",
|
"experimentalFeature": "Fonctionnalité expérimentale",
|
||||||
"export": "Exporter",
|
|
||||||
"extraArgs": "Arguments supplémentaires",
|
"extraArgs": "Arguments supplémentaires",
|
||||||
"failed": "Échoué",
|
"fallbackSshDest": "Destino SSH alternativo",
|
||||||
"fdroidReleaseTip": "Si vous avez téléchargé cette application depuis Fdroid, il est recommandé de désactiver cette option.",
|
"fdroidReleaseTip": "Si vous avez téléchargé cette application depuis F-Droid, il est recommandé de désactiver cette option.",
|
||||||
"feedback": "Retour",
|
|
||||||
"feedbackOnGithub": "Si vous avez des questions, veuillez donner votre avis sur Github.",
|
|
||||||
"fieldMustNotEmpty": "Ces champs ne doivent pas être vides.",
|
|
||||||
"fileNotExist": "{file} n'existe pas",
|
|
||||||
"fileTooLarge": "Fichier '{file}' trop volumineux {size}, max {sizeMax}",
|
"fileTooLarge": "Fichier '{file}' trop volumineux {size}, max {sizeMax}",
|
||||||
"files": "Fichiers",
|
|
||||||
"finished": "Terminé",
|
|
||||||
"followSystem": "Suivre le système",
|
"followSystem": "Suivre le système",
|
||||||
"font": "Police",
|
"font": "Police",
|
||||||
"fontSize": "Taille de la police",
|
"fontSize": "Taille de la police",
|
||||||
"forExample": "Par exemple",
|
|
||||||
"force": "Forcer",
|
"force": "Forcer",
|
||||||
"foundNUpdate": "{count} mise à jour trouvée",
|
|
||||||
"fullScreen": "Mode plein écran",
|
"fullScreen": "Mode plein écran",
|
||||||
"fullScreenJitter": "Secousse en plein écran",
|
"fullScreenJitter": "Secousse en plein écran",
|
||||||
"fullScreenJitterHelp": "Pour éviter les brûlures d'écran",
|
"fullScreenJitterHelp": "Pour éviter les brûlures d'écran",
|
||||||
"fullScreenTip": "Le mode plein écran doit-il être activé lorsque l'appareil est orienté en mode paysage ? Cette option s'applique uniquement à l'onglet serveur.",
|
"fullScreenTip": "Le mode plein écran doit-il être activé lorsque l'appareil est orienté en mode paysage ? Cette option s'applique uniquement à l'onglet serveur.",
|
||||||
"getPushTokenFailed": "Impossible de récupérer le jeton de notification",
|
|
||||||
"gettingToken": "Récupération du jeton...",
|
|
||||||
"goBackQ": "Revenir en arrière ?",
|
"goBackQ": "Revenir en arrière ?",
|
||||||
"goto": "Aller à",
|
"goto": "Aller à",
|
||||||
"hideTitleBar": "Masquer la barre de titre",
|
"hideTitleBar": "Masquer la barre de titre",
|
||||||
"hideTitleBarTip": "Après l'avoir allumé, veuillez maintenir les trois boutons dans le coin supérieur droit pour les faire glisser.",
|
|
||||||
"highlight": "Mise en surbrillance du code",
|
"highlight": "Mise en surbrillance du code",
|
||||||
"homeWidgetUrlConfig": "Configurer l'URL du widget d'accueil",
|
"homeWidgetUrlConfig": "Configurer l'URL du widget d'accueil",
|
||||||
"host": "Hôte",
|
"host": "Hôte",
|
||||||
"hour": "Heure",
|
|
||||||
"httpFailedWithCode": "Échec de la requête, code d'état : {code}",
|
"httpFailedWithCode": "Échec de la requête, code d'état : {code}",
|
||||||
"icloudSynced": "iCloud est en cours de synchronisation et certains paramètres peuvent nécessiter un redémarrage de l'application pour prendre effet.",
|
|
||||||
"ignoreCert": "Ignorer le certificat",
|
"ignoreCert": "Ignorer le certificat",
|
||||||
"image": "Image",
|
"image": "Image",
|
||||||
"imagesList": "Liste des images",
|
"imagesList": "Liste des images",
|
||||||
"import": "Importer",
|
|
||||||
"inAppUpdate": "Mettre à jour dans l'application ? Sinon, téléchargez en utilisant un navigateur.",
|
|
||||||
"init": "Initialiser",
|
"init": "Initialiser",
|
||||||
"inner": "Interne",
|
"inner": "Interne",
|
||||||
"inputDomainHere": "Saisissez le domaine ici",
|
|
||||||
"install": "Installer",
|
"install": "Installer",
|
||||||
"installDockerWithUrl": "Veuillez d'abord installer docker depuis https://docs.docker.com/engine/install.",
|
"installDockerWithUrl": "Veuillez d'abord installer docker depuis https://docs.docker.com/engine/install.",
|
||||||
"invalid": "Invalide",
|
"invalid": "Invalide",
|
||||||
"invalidJson": "JSON invalide",
|
|
||||||
"invalidVersion": "Version invalide",
|
|
||||||
"invalidVersionHelp": "Assurez-vous que docker est correctement installé, ou que vous utilisez une version non compilée par vous-même. Si vous n'avez pas les problèmes ci-dessus, veuillez soumettre un problème sur {url}.",
|
|
||||||
"isBusy": "Est occupé maintenant",
|
|
||||||
"jumpServer": "Aller au serveur",
|
"jumpServer": "Aller au serveur",
|
||||||
"keepForeground": "Garder l'application en premier plan !",
|
"keepForeground": "Garder l'application en premier plan !",
|
||||||
"keepStatusWhenErr": "Conserver l'état du dernier serveur",
|
"keepStatusWhenErr": "Conserver l'état du dernier serveur",
|
||||||
"keepStatusWhenErrTip": "Uniquement en cas d'erreur lors de l'exécution du script",
|
"keepStatusWhenErrTip": "Uniquement en cas d'erreur lors de l'exécution du script",
|
||||||
"keyAuth": "Authentification par clé",
|
"keyAuth": "Authentification par clé",
|
||||||
"language": "Langue",
|
"letterCache": "Mise en cache des lettres",
|
||||||
"languageName": "Français",
|
"letterCacheTip": "Recommandé de désactiver, mais après désactivation, il sera impossible de saisir des caractères CJK.",
|
||||||
"lastTry": "Dernière tentative",
|
|
||||||
"launchPage": "Page de lancement",
|
|
||||||
"license": "Licence",
|
"license": "Licence",
|
||||||
"light": "Clair",
|
|
||||||
"loadingFiles": "Chargement des fichiers...",
|
|
||||||
"location": "Emplacement",
|
"location": "Emplacement",
|
||||||
"log": "Journal",
|
|
||||||
"loss": "Perte",
|
"loss": "Perte",
|
||||||
"madeWithLove": "Fabriqué avec ❤️ par {myGithub}",
|
"madeWithLove": "Fabriqué avec ❤️ par {myGithub}",
|
||||||
"manual": "Manuel",
|
"manual": "Manuel",
|
||||||
@@ -167,60 +93,36 @@
|
|||||||
"maxRetryCount": "Nombre de reconnexions au serveur",
|
"maxRetryCount": "Nombre de reconnexions au serveur",
|
||||||
"maxRetryCountEqual0": "Il va réessayer encore et encore.",
|
"maxRetryCountEqual0": "Il va réessayer encore et encore.",
|
||||||
"min": "min",
|
"min": "min",
|
||||||
"minute": "Minute",
|
|
||||||
"mission": "Mission",
|
"mission": "Mission",
|
||||||
"more": "Plus",
|
"more": "Plus",
|
||||||
"moveOutServerFuncBtnsHelp": "Activé : peut être affiché sous chaque carte sur la page de l'onglet Serveur. Désactivé : peut être affiché en haut de la page de détails du serveur.",
|
"moveOutServerFuncBtnsHelp": "Activé : peut être affiché sous chaque carte sur la page de l'onglet Serveur. Désactivé : peut être affiché en haut de la page de détails du serveur.",
|
||||||
"ms": "ms",
|
"ms": "ms",
|
||||||
"name": "Nom",
|
|
||||||
"needHomeDir": "Si vous êtes utilisateur Synology, [consultez ici](https://kb.synology.com/DSM/tutorial/user_enable_home_service). Les utilisateurs d'autres systèmes doivent rechercher comment créer un répertoire personnel.",
|
"needHomeDir": "Si vous êtes utilisateur Synology, [consultez ici](https://kb.synology.com/DSM/tutorial/user_enable_home_service). Les utilisateurs d'autres systèmes doivent rechercher comment créer un répertoire personnel.",
|
||||||
"needRestart": "Nécessite un redémarrage de l'application",
|
"needRestart": "Nécessite un redémarrage de l'application",
|
||||||
"net": "Réseau",
|
"net": "Réseau",
|
||||||
"netViewType": "Type de vue réseau",
|
"netViewType": "Type de vue réseau",
|
||||||
"newContainer": "Nouveau conteneur",
|
"newContainer": "Nouveau conteneur",
|
||||||
"noClient": "Pas de client",
|
|
||||||
"noInterface": "Pas d'interface",
|
|
||||||
"noLineChart": "Ne pas utiliser de graphiques linéaires",
|
"noLineChart": "Ne pas utiliser de graphiques linéaires",
|
||||||
"noNotiPerm": "Pas de permissions de notification, peut-être pas d'indication de progression lors de la mise à jour des applications.",
|
"noLineChartForCpu": "Ne pas utiliser de graphiques linéaires pour l'unité centrale",
|
||||||
"noOptions": "Pas d'options",
|
|
||||||
"noPrivateKeyTip": "La clé privée n'existe pas, elle a peut-être été supprimée ou il y a une erreur de configuration.",
|
"noPrivateKeyTip": "La clé privée n'existe pas, elle a peut-être été supprimée ou il y a une erreur de configuration.",
|
||||||
"noPromptAgain": "Ne pas demander à nouveau",
|
"noPromptAgain": "Ne pas demander à nouveau",
|
||||||
"noResult": "Pas de résultat",
|
|
||||||
"noSavedPrivateKey": "Aucune clé privée enregistrée.",
|
|
||||||
"noSavedSnippet": "Aucun extrait de code enregistré.",
|
|
||||||
"noServerAvailable": "Aucun serveur disponible.",
|
|
||||||
"noTask": "Pas de tâche",
|
|
||||||
"noUpdateAvailable": "Pas de mise à jour disponible",
|
|
||||||
"node": "Nœud",
|
"node": "Nœud",
|
||||||
"notAvailable": "Indisponible",
|
"notAvailable": "Indisponible",
|
||||||
"notSelected": "Non sélectionné",
|
|
||||||
"note": "Note",
|
|
||||||
"nullToken": "Jeton nul",
|
|
||||||
"ok": "OK",
|
|
||||||
"onServerDetailPage": "Sur la page de détails du serveur",
|
"onServerDetailPage": "Sur la page de détails du serveur",
|
||||||
"onlyOneLine": "Afficher uniquement en une seule ligne (défilement)",
|
"onlyOneLine": "Afficher uniquement en une seule ligne (défilement)",
|
||||||
"onlyWhenCoreBiggerThan8": "Fonctionne uniquement lorsque le nombre de cœurs est > 8",
|
"onlyWhenCoreBiggerThan8": "Fonctionne uniquement lorsque le nombre de cœurs est > 8",
|
||||||
"open": "Ouvrir",
|
|
||||||
"openLastPath": "Ouvrir le dernier chemin",
|
"openLastPath": "Ouvrir le dernier chemin",
|
||||||
"openLastPathTip": "Les différents serveurs auront des journaux différents, et le journal est le chemin vers la sortie",
|
"openLastPathTip": "Les différents serveurs auront des journaux différents, et le journal est le chemin vers la sortie",
|
||||||
"parseContainerStats": "Analyser l'état d'occupation du conteneur",
|
|
||||||
"parseContainerStatsTip": "L'analyse de l'occupation des conteneurs Docker est relativement lente.",
|
"parseContainerStatsTip": "L'analyse de l'occupation des conteneurs Docker est relativement lente.",
|
||||||
"paste": "Coller",
|
|
||||||
"path": "Chemin",
|
|
||||||
"percentOfSize": "{percent}% de {size}",
|
"percentOfSize": "{percent}% de {size}",
|
||||||
"pickFile": "Choisir un fichier",
|
"permission": "Permissions",
|
||||||
"pingAvg": "Moy.:",
|
"pingAvg": "Moy.:",
|
||||||
"pingInputIP": "Veuillez saisir une adresse IP / un domaine cible.",
|
"pingInputIP": "Veuillez saisir une adresse IP / un domaine cible.",
|
||||||
"pingNoServer": "Aucun serveur à pinger.\nVeuillez ajouter un serveur dans l'onglet serveur.",
|
"pingNoServer": "Aucun serveur à pinger.\nVeuillez ajouter un serveur dans l'onglet serveur.",
|
||||||
"pkg": "Pkg",
|
"pkg": "Pkg",
|
||||||
"pkgUpgradeTip": "Veuillez sauvegarder votre système avant de procéder à la mise à jour.",
|
|
||||||
"platformNotSupportUpdate": "La plateforme actuelle ne prend pas en charge la mise à jour dans l'application.\nVeuillez le compiler depuis les sources et l'installer.",
|
|
||||||
"plugInType": "Type d'insertion",
|
"plugInType": "Type d'insertion",
|
||||||
"plzEnterHost": "Veuillez saisir l'hôte.",
|
|
||||||
"plzSelectKey": "Veuillez sélectionner une clé.",
|
|
||||||
"port": "Port",
|
"port": "Port",
|
||||||
"preview": "Aperçu",
|
"preview": "Aperçu",
|
||||||
"primaryColorSeed": "Graine de couleur primaire",
|
|
||||||
"privateKey": "Clé privée",
|
"privateKey": "Clé privée",
|
||||||
"process": "Processus",
|
"process": "Processus",
|
||||||
"pushToken": "Jeton d'identification",
|
"pushToken": "Jeton d'identification",
|
||||||
@@ -230,16 +132,11 @@
|
|||||||
"pwd": "Mot de passe",
|
"pwd": "Mot de passe",
|
||||||
"read": "Lire",
|
"read": "Lire",
|
||||||
"reboot": "Redémarrer",
|
"reboot": "Redémarrer",
|
||||||
"rememberChoice": "Se souvenir du choix",
|
|
||||||
"rememberPwdInMem": "Mémoriser le mot de passe en mémoire",
|
"rememberPwdInMem": "Mémoriser le mot de passe en mémoire",
|
||||||
"rememberPwdInMemTip": "Utilisé pour les conteneurs, la suspension, etc.",
|
"rememberPwdInMemTip": "Utilisé pour les conteneurs, la suspension, etc.",
|
||||||
"rememberWindowSize": "Se souvenir de la taille de la fenêtre",
|
"rememberWindowSize": "Se souvenir de la taille de la fenêtre",
|
||||||
"remotePath": "Chemin distant",
|
"remotePath": "Chemin distant",
|
||||||
"rename": "Renommer",
|
|
||||||
"reportBugsOnGithubIssue": "Veuillez signaler les bugs sur {url}",
|
|
||||||
"restart": "Redémarrer",
|
"restart": "Redémarrer",
|
||||||
"restore": "Restaurer",
|
|
||||||
"restoreSuccess": "Restauration réussie. Redémarrez l'application pour appliquer.",
|
|
||||||
"result": "Résultat",
|
"result": "Résultat",
|
||||||
"rotateAngel": "Angle de rotation",
|
"rotateAngel": "Angle de rotation",
|
||||||
"route": "Routage",
|
"route": "Routage",
|
||||||
@@ -254,14 +151,8 @@
|
|||||||
"serverDetailOrder": "Ordre des widgets de la page de détails du serveur",
|
"serverDetailOrder": "Ordre des widgets de la page de détails du serveur",
|
||||||
"serverFuncBtns": "Boutons de fonction du serveur",
|
"serverFuncBtns": "Boutons de fonction du serveur",
|
||||||
"serverOrder": "Ordre du serveur",
|
"serverOrder": "Ordre du serveur",
|
||||||
"serverTabConnecting": "Connexion en cours...",
|
|
||||||
"serverTabEmpty": "Aucun serveur.\nCliquez sur le fab pour en ajouter un.",
|
|
||||||
"serverTabFailed": "Échec",
|
|
||||||
"serverTabLoading": "Chargement...",
|
|
||||||
"serverTabPlzSave": "Veuillez 'enregistrer' cette clé privée à nouveau.",
|
|
||||||
"serverTabUnkown": "État inconnu",
|
|
||||||
"setting": "Paramètres",
|
|
||||||
"sftpDlPrepare": "Préparation de la connexion...",
|
"sftpDlPrepare": "Préparation de la connexion...",
|
||||||
|
"sftpEditorTip": "Si vide, utilisez l’éditeur de fichiers intégré de l’application. Si une valeur est présente, utilisez l’éditeur du serveur distant, par exemple `vim` (il est recommandé de détecter automatiquement selon `EDITOR`).",
|
||||||
"sftpRmrDirSummary": "Utilisez `rm -r` pour supprimer un dossier en SFTP.",
|
"sftpRmrDirSummary": "Utilisez `rm -r` pour supprimer un dossier en SFTP.",
|
||||||
"sftpSSHConnected": "SFTP Connecté",
|
"sftpSSHConnected": "SFTP Connecté",
|
||||||
"sftpShowFoldersFirst": "Afficher d'abord les dossiers",
|
"sftpShowFoldersFirst": "Afficher d'abord les dossiers",
|
||||||
@@ -270,17 +161,19 @@
|
|||||||
"size": "Taille",
|
"size": "Taille",
|
||||||
"snippet": "Extrait",
|
"snippet": "Extrait",
|
||||||
"softWrap": "Retour à la ligne souple",
|
"softWrap": "Retour à la ligne souple",
|
||||||
|
"specifyDev": "Spécifier l'appareil",
|
||||||
|
"specifyDevTip": "Par exemple, les statistiques de trafic réseau concernent par défaut tous les appareils. Vous pouvez spécifier ici un appareil particulier.",
|
||||||
"speed": "Vitesse",
|
"speed": "Vitesse",
|
||||||
"spentTime": "Temps écoulé : {time}",
|
"spentTime": "Temps écoulé : {time}",
|
||||||
"sshTermHelp": "Lorsque le terminal est défilable, faire glisser horizontalement permet de sélectionner du texte. En cliquant sur le bouton du clavier, vous activez/désactivez le clavier. L'icône de fichier ouvre le chemin actuel SFTP. Le bouton du presse-papiers copie le contenu lorsque du texte est sélectionné, et colle le contenu du presse-papiers dans le terminal lorsqu'aucun texte n'est sélectionné et qu'il y a du contenu dans le presse-papiers. L'icône de code colle des extraits de code dans le terminal et les exécute.",
|
"sshTermHelp": "Lorsque le terminal est défilable, faire glisser horizontalement permet de sélectionner du texte. En cliquant sur le bouton du clavier, vous activez/désactivez le clavier. L'icône de fichier ouvre le chemin actuel SFTP. Le bouton du presse-papiers copie le contenu lorsque du texte est sélectionné, et colle le contenu du presse-papiers dans le terminal lorsqu'aucun texte n'est sélectionné et qu'il y a du contenu dans le presse-papiers. L'icône de code colle des extraits de code dans le terminal et les exécute.",
|
||||||
"sshTip": "Cette fonctionnalité est actuellement à l'étape expérimentale.\n\nVeuillez signaler les bugs sur {url} ou rejoindre notre développement.",
|
"sshTip": "Cette fonctionnalité est actuellement à l'étape expérimentale.\n\nVeuillez signaler les bugs sur {url} ou rejoindre notre développement.",
|
||||||
"sshVirtualKeyAutoOff": "Activation automatique des touches virtuelles",
|
"sshVirtualKeyAutoOff": "Activation automatique des touches virtuelles",
|
||||||
"start": "Démarrer",
|
"start": "Démarrer",
|
||||||
|
"stat": "Statistiques",
|
||||||
"stats": "Statistiques",
|
"stats": "Statistiques",
|
||||||
"stop": "Arrêter",
|
"stop": "Arrêter",
|
||||||
"stopped": "Arrêté",
|
"stopped": "Arrêté",
|
||||||
"storage": "Stockage",
|
"storage": "Stockage",
|
||||||
"success": "Succès",
|
|
||||||
"supportFmtArgs": "Les paramètres de mise en forme suivants sont pris en charge :",
|
"supportFmtArgs": "Les paramètres de mise en forme suivants sont pris en charge :",
|
||||||
"suspend": "Suspendre",
|
"suspend": "Suspendre",
|
||||||
"suspendTip": "La fonction de suspension nécessite des privilèges root et le support de systemd.",
|
"suspendTip": "La fonction de suspension nécessite des privilèges root et le support de systemd.",
|
||||||
@@ -296,35 +189,25 @@
|
|||||||
"textScaler": "Mise à l'échelle du texte",
|
"textScaler": "Mise à l'échelle du texte",
|
||||||
"textScalerTip": "1.0 => 100% (taille originale), fonctionne uniquement sur la partie de la police de la page du serveur, il est déconseillé de la modifier.",
|
"textScalerTip": "1.0 => 100% (taille originale), fonctionne uniquement sur la partie de la police de la page du serveur, il est déconseillé de la modifier.",
|
||||||
"theme": "Thème",
|
"theme": "Thème",
|
||||||
"themeMode": "Mode thème",
|
|
||||||
"time": "Temps",
|
"time": "Temps",
|
||||||
"times": "Fois",
|
"times": "Fois",
|
||||||
"total": "Total",
|
"total": "Total",
|
||||||
"traffic": "Trafic",
|
"traffic": "Trafic",
|
||||||
"trySudo": "Essayer d'utiliser sudo",
|
"trySudo": "Essayer d'utiliser sudo",
|
||||||
"ttl": "ttl",
|
"ttl": "TTL",
|
||||||
"unknown": "Inconnu",
|
"unknown": "Inconnu",
|
||||||
"unknownError": "Erreur inconnue",
|
|
||||||
"unkownConvertMode": "Mode de conversion inconnu",
|
"unkownConvertMode": "Mode de conversion inconnu",
|
||||||
"update": "Mettre à jour",
|
"update": "Mettre à jour",
|
||||||
"updateAll": "Tout mettre à jour",
|
|
||||||
"updateIntervalEqual0": "Vous avez défini à 0, la mise à jour ne se fera pas automatiquement.\nImpossible de calculer l'état du CPU.",
|
"updateIntervalEqual0": "Vous avez défini à 0, la mise à jour ne se fera pas automatiquement.\nImpossible de calculer l'état du CPU.",
|
||||||
"updateServerStatusInterval": "Intervalle de mise à jour de l'état du serveur",
|
"updateServerStatusInterval": "Intervalle de mise à jour de l'état du serveur",
|
||||||
"updateTip": "Mise à jour : v1.0.{newest}",
|
|
||||||
"updateTipTooLow": "La version actuelle est trop ancienne, veuillez mettre à jour vers v1.0.{newest}",
|
|
||||||
"upload": "Télécharger",
|
"upload": "Télécharger",
|
||||||
"upsideDown": "À l'envers",
|
"upsideDown": "À l'envers",
|
||||||
"uptime": "Temps d'activité",
|
"uptime": "Temps d'activité",
|
||||||
"urlOrJson": "URL ou JSON",
|
|
||||||
"useCdn": "Utiliser CDN",
|
"useCdn": "Utiliser CDN",
|
||||||
"useCdnTip": "Il est recommandé aux utilisateurs non chinois d'utiliser le CDN. Souhaitez-vous l'utiliser ?",
|
"useCdnTip": "Il est recommandé aux utilisateurs non chinois d'utiliser le CDN. Souhaitez-vous l'utiliser ?",
|
||||||
"useNoPwd": "Aucun mot de passe ne sera utilisé",
|
"useNoPwd": "Aucun mot de passe ne sera utilisé",
|
||||||
"usePodmanByDefault": "Par défaut avec Podman",
|
"usePodmanByDefault": "Par défaut avec Podman",
|
||||||
"used": "Utilisé",
|
"used": "Utilisé",
|
||||||
"user": "Utilisateur",
|
|
||||||
"versionHaveUpdate": "Trouvé : v1.0.{build}, cliquez pour mettre à jour",
|
|
||||||
"versionUnknownUpdate": "Actuelle : v1.0.{build}, cliquez pour vérifier les mises à jour",
|
|
||||||
"versionUpdated": "Actuelle : v1.0.{build}, est à jour",
|
|
||||||
"view": "Vue",
|
"view": "Vue",
|
||||||
"viewErr": "Voir erreur",
|
"viewErr": "Voir erreur",
|
||||||
"virtKeyHelpClipboard": "Copiez dans le presse-papiers si le terminal sélectionné n'est pas vide, sinon collez le contenu du presse-papiers dans le terminal.",
|
"virtKeyHelpClipboard": "Copiez dans le presse-papiers si le terminal sélectionné n'est pas vide, sinon collez le contenu du presse-papiers dans le terminal.",
|
||||||
@@ -335,8 +218,8 @@
|
|||||||
"watchNotPaired": "Aucune Apple Watch associée",
|
"watchNotPaired": "Aucune Apple Watch associée",
|
||||||
"webdavSettingEmpty": "Le paramètre Webdav est vide",
|
"webdavSettingEmpty": "Le paramètre Webdav est vide",
|
||||||
"whenOpenApp": "À l'ouverture de l'application",
|
"whenOpenApp": "À l'ouverture de l'application",
|
||||||
"willTakEeffectImmediately": "Prendra effet immédiatement",
|
|
||||||
"wolTip": "Après avoir configuré le WOL (Wake-on-LAN), une requête WOL est envoyée chaque fois que le serveur est connecté.",
|
"wolTip": "Après avoir configuré le WOL (Wake-on-LAN), une requête WOL est envoyée chaque fois que le serveur est connecté.",
|
||||||
"write": "Écrire",
|
"write": "Écrire",
|
||||||
"writeScriptFailTip": "Échec de l'écriture dans le script, probablement en raison d'un manque de permissions ou que le répertoire n'existe pas."
|
"writeScriptFailTip": "Échec de l'écriture dans le script, probablement en raison d'un manque de permissions ou que le répertoire n'existe pas.",
|
||||||
|
"writeScriptTip": "Après la connexion au serveur, un script sera écrit dans ~/.config/server_box pour surveiller l’état du système. Vous pouvez examiner le contenu du script."
|
||||||
}
|
}
|
||||||
@@ -1,87 +1,42 @@
|
|||||||
{
|
{
|
||||||
"@@locale": "id",
|
"@@locale": "id",
|
||||||
"about": "Tentang",
|
|
||||||
"aboutThanks": "Terima kasih kepada orang -orang berikut yang berpartisipasi.",
|
"aboutThanks": "Terima kasih kepada orang -orang berikut yang berpartisipasi.",
|
||||||
"acceptBeta": "Terima pembaruan versi uji coba",
|
"acceptBeta": "Terima pembaruan versi uji coba",
|
||||||
"add": "Menambahkan",
|
|
||||||
"addAServer": "tambahkan server",
|
|
||||||
"addPrivateKey": "Tambahkan kunci pribadi",
|
|
||||||
"addSystemPrivateKeyTip": "Saat ini tidak memiliki kunci privat, apakah Anda menambahkan kunci yang disertakan dengan sistem (~/.ssh/id_rsa)?",
|
"addSystemPrivateKeyTip": "Saat ini tidak memiliki kunci privat, apakah Anda menambahkan kunci yang disertakan dengan sistem (~/.ssh/id_rsa)?",
|
||||||
"added2List": "Ditambahkan ke Daftar Tugas",
|
"added2List": "Ditambahkan ke Daftar Tugas",
|
||||||
"addr": "Alamat",
|
"addr": "Alamat",
|
||||||
"all": "Semua",
|
|
||||||
"alreadyLastDir": "Sudah di direktori terakhir.",
|
"alreadyLastDir": "Sudah di direktori terakhir.",
|
||||||
"alterUrl": "Alter url",
|
|
||||||
"askContinue": "{msg}, lanjutkan?",
|
|
||||||
"attention": "Perhatian",
|
|
||||||
"authFailTip": "Otentikasi gagal, silakan periksa apakah kata sandi/kunci/host/pengguna, dll, salah.",
|
"authFailTip": "Otentikasi gagal, silakan periksa apakah kata sandi/kunci/host/pengguna, dll, salah.",
|
||||||
"authRequired": "Auth diperlukan",
|
|
||||||
"auto": "Auto",
|
|
||||||
"autoBackupConflict": "Hanya satu pencadangan otomatis yang dapat diaktifkan pada saat yang bersamaan.",
|
"autoBackupConflict": "Hanya satu pencadangan otomatis yang dapat diaktifkan pada saat yang bersamaan.",
|
||||||
"autoCheckUpdate": "Periksa pembaruan otomatis",
|
|
||||||
"autoConnect": "Hubungkan otomatis",
|
"autoConnect": "Hubungkan otomatis",
|
||||||
"autoRun": "Berjalan Otomatis",
|
"autoRun": "Berjalan Otomatis",
|
||||||
"autoUpdateHomeWidget": "Widget Rumah Pembaruan Otomatis",
|
"autoUpdateHomeWidget": "Widget Rumah Pembaruan Otomatis",
|
||||||
"backup": "Cadangan",
|
|
||||||
"backupTip": "Data yang diekspor hanya dienkripsi.\nTolong jaga keamanannya.",
|
"backupTip": "Data yang diekspor hanya dienkripsi.\nTolong jaga keamanannya.",
|
||||||
"backupVersionNotMatch": "Versi cadangan tidak cocok.",
|
"backupVersionNotMatch": "Versi cadangan tidak cocok.",
|
||||||
"battery": "Baterai",
|
"battery": "Baterai",
|
||||||
"beforeConnect": "ServerBox akan menulis dan menjalankan skrip di `~/.config/server_box` setelah koneksi. Untuk detail teknis lebih lanjut, silakan kunjungi [Github]({url}).",
|
|
||||||
"bgRun": "Jalankan di Backgroud",
|
"bgRun": "Jalankan di Backgroud",
|
||||||
"bgRunTip": "Sakelar ini hanya berarti aplikasi akan mencoba berjalan di latar belakang, apakah aplikasi dapat berjalan di latar belakang tergantung pada apakah izin diaktifkan atau tidak. Untuk Android asli, nonaktifkan \"Pengoptimalan Baterai\" di aplikasi ini, dan untuk miui, ubah kebijakan penghematan daya ke \"Tidak Terbatas\".",
|
"bgRunTip": "Sakelar ini hanya berarti aplikasi akan mencoba berjalan di latar belakang, apakah aplikasi dapat berjalan di latar belakang tergantung pada apakah izin diaktifkan atau tidak. Untuk Android asli, nonaktifkan \"Pengoptimalan Baterai\" di aplikasi ini, dan untuk miui, ubah kebijakan penghematan daya ke \"Tidak Terbatas\".",
|
||||||
"bioAuth": "Biosertifikasi",
|
|
||||||
"browser": "Peramban",
|
|
||||||
"bulkImportServers": "Impor server secara massal",
|
|
||||||
"bulkImportServersTip": "Anda dapat menemukan [format]({url}) di sini.",
|
|
||||||
"canPullRefresh": "Anda dapat menarik untuk menyegarkan.",
|
|
||||||
"cancel": "Membatalkan",
|
|
||||||
"choose": "Memilih",
|
|
||||||
"chooseFontFile": "Pilih file font",
|
|
||||||
"choosePrivateKey": "Pilih Kunci Pribadi",
|
|
||||||
"clear": "Jernih",
|
|
||||||
"clipboard": "papan klip",
|
|
||||||
"close": "Menutup",
|
|
||||||
"cmd": "Memerintah",
|
"cmd": "Memerintah",
|
||||||
"cnKeyboardComp": "Kompatibilitas dengan Android China",
|
|
||||||
"cnKeyboardCompTip": "Jika terminal munculkan keyboard aman, Anda bisa mengaktifkannya.",
|
|
||||||
"collapseUI": "Runtuh",
|
|
||||||
"collapseUITip": "Apakah akan menciutkan daftar panjang yang ada di UI secara default atau tidak",
|
"collapseUITip": "Apakah akan menciutkan daftar panjang yang ada di UI secara default atau tidak",
|
||||||
"conn": "Koneksi",
|
"conn": "Koneksi",
|
||||||
"connected": "Terhubung",
|
|
||||||
"container": "Wadah",
|
"container": "Wadah",
|
||||||
"containerName": "Nama kontainer",
|
|
||||||
"containerStatus": "Status wadah",
|
|
||||||
"containerTrySudoTip": "Contohnya: Di dalam aplikasi, pengguna diatur sebagai aaa, tetapi Docker diinstal di bawah pengguna root. Dalam kasus ini, Anda perlu mengaktifkan opsi ini.",
|
"containerTrySudoTip": "Contohnya: Di dalam aplikasi, pengguna diatur sebagai aaa, tetapi Docker diinstal di bawah pengguna root. Dalam kasus ini, Anda perlu mengaktifkan opsi ini.",
|
||||||
"content": "Konten",
|
|
||||||
"convert": "Mengubah",
|
"convert": "Mengubah",
|
||||||
"copy": "Menyalin",
|
|
||||||
"copyPath": "Path Copy",
|
"copyPath": "Path Copy",
|
||||||
"cpuViewAsProgressTip": "Tampilkan tingkat penggunaan setiap CPU dalam gaya bilah kemajuan (gaya lama)",
|
"cpuViewAsProgressTip": "Tampilkan tingkat penggunaan setiap CPU dalam gaya bilah kemajuan (gaya lama)",
|
||||||
"createFile": "Buat file",
|
|
||||||
"createFolder": "Membuat folder",
|
|
||||||
"cursorType": "Jenis kursor",
|
"cursorType": "Jenis kursor",
|
||||||
"customCmd": "Perintah kustom",
|
"customCmd": "Perintah kustom",
|
||||||
"customCmdDocUrl": "https://github.com/lollipopkit/flutter_server_box/wiki#custom-commands",
|
"customCmdDocUrl": "https://github.com/lollipopkit/flutter_server_box/wiki#custom-commands",
|
||||||
"customCmdHint": "\"Nama Perintah\": \"Perintah\"",
|
"customCmdHint": "\"Nama Perintah\": \"Perintah\"",
|
||||||
"dark": "Gelap",
|
|
||||||
"day": "Hari",
|
|
||||||
"debug": "Debug",
|
|
||||||
"decode": "Membaca sandi",
|
"decode": "Membaca sandi",
|
||||||
"decompress": "Dekompresi",
|
"decompress": "Dekompresi",
|
||||||
"delete": "Menghapus",
|
|
||||||
"deleteScripts": "Menghapus skrip server secara bersamaan",
|
|
||||||
"deleteServers": "Penghapusan server secara batch",
|
"deleteServers": "Penghapusan server secara batch",
|
||||||
"deviceName": "Nama perangkat",
|
|
||||||
"dirEmpty": "Pastikan dir kosong.",
|
"dirEmpty": "Pastikan dir kosong.",
|
||||||
"disabled": "Dengan disabilitas",
|
|
||||||
"disconnected": "Terputus",
|
"disconnected": "Terputus",
|
||||||
"disk": "Disk",
|
"disk": "Disk",
|
||||||
"diskIgnorePath": "Abaikan jalan untuk disk",
|
"diskIgnorePath": "Abaikan jalan untuk disk",
|
||||||
"displayCpuIndex": "Tampilkan indeks CPU",
|
"displayCpuIndex": "Tampilkan indeks CPU",
|
||||||
"displayName": "Nama tampilan",
|
|
||||||
"dl2Local": "Unduh {fileName} ke lokal?",
|
"dl2Local": "Unduh {fileName} ke lokal?",
|
||||||
"doc": "Dokumentasi",
|
|
||||||
"dockerEditHost": "Edit Docker_host",
|
|
||||||
"dockerEmptyRunningItems": "Tidak ada wadah yang sedang berjalan.\nHal ini dapat terjadi karena:\n- Pengguna instalasi Docker tidak sama dengan nama pengguna yang dikonfigurasi di dalam Aplikasi.\n- Variabel lingkungan DOCKER_HOST tidak terbaca dengan benar. Anda bisa mendapatkannya dengan menjalankan `echo $DOCKER_HOST` di terminal.",
|
"dockerEmptyRunningItems": "Tidak ada wadah yang sedang berjalan.\nHal ini dapat terjadi karena:\n- Pengguna instalasi Docker tidak sama dengan nama pengguna yang dikonfigurasi di dalam Aplikasi.\n- Variabel lingkungan DOCKER_HOST tidak terbaca dengan benar. Anda bisa mendapatkannya dengan menjalankan `echo $DOCKER_HOST` di terminal.",
|
||||||
"dockerImagesFmt": "{count} gambar",
|
"dockerImagesFmt": "{count} gambar",
|
||||||
"dockerNotInstalled": "Docker tidak terpasang",
|
"dockerNotInstalled": "Docker tidak terpasang",
|
||||||
@@ -89,77 +44,48 @@
|
|||||||
"dockerStatusRunningFmt": "{count} wadah berjalan.",
|
"dockerStatusRunningFmt": "{count} wadah berjalan.",
|
||||||
"doubleColumnMode": "Mode kolom ganda",
|
"doubleColumnMode": "Mode kolom ganda",
|
||||||
"doubleColumnTip": "Opsi ini hanya mengaktifkan fitur, apakah itu benar-benar dapat diaktifkan tergantung pada lebar perangkat",
|
"doubleColumnTip": "Opsi ini hanya mengaktifkan fitur, apakah itu benar-benar dapat diaktifkan tergantung pada lebar perangkat",
|
||||||
"download": "Unduh",
|
|
||||||
"edit": "Edit",
|
|
||||||
"editVirtKeys": "Edit kunci virtual",
|
"editVirtKeys": "Edit kunci virtual",
|
||||||
"editor": "Editor",
|
"editor": "Editor",
|
||||||
"editorHighlightTip": "Performa penyorotan kode saat ini lebih buruk, dan dapat dimatikan secara opsional untuk perbaikan.",
|
"editorHighlightTip": "Performa penyorotan kode saat ini lebih buruk, dan dapat dimatikan secara opsional untuk perbaikan.",
|
||||||
"encode": "Menyandi",
|
"encode": "Menyandi",
|
||||||
"error": "Kesalahan",
|
"envVars": "Variabel lingkungan",
|
||||||
"exampleName": "Nama contoh",
|
|
||||||
"experimentalFeature": "Fitur eksperimental",
|
"experimentalFeature": "Fitur eksperimental",
|
||||||
"export": "Ekspor",
|
|
||||||
"extraArgs": "Args ekstra",
|
"extraArgs": "Args ekstra",
|
||||||
"failed": "Gagal",
|
"fallbackSshDest": "Tujuan SSH mundur",
|
||||||
"fdroidReleaseTip": "Jika Anda mengunduh aplikasi ini dari Fdroid, disarankan untuk mematikan opsi ini.",
|
"fdroidReleaseTip": "Jika Anda mengunduh aplikasi ini dari F-Droid, disarankan untuk mematikan opsi ini.",
|
||||||
"feedback": "Masukan",
|
|
||||||
"feedbackOnGithub": "Jika Anda memiliki pertanyaan, silakan umpan balik tentang GitHub.",
|
|
||||||
"fieldMustNotEmpty": "Bidang -bidang ini tidak boleh kosong.",
|
|
||||||
"fileNotExist": "{file} tidak ada",
|
|
||||||
"fileTooLarge": "File '{file}' terlalu besar {size}, max {sizeMax}",
|
"fileTooLarge": "File '{file}' terlalu besar {size}, max {sizeMax}",
|
||||||
"files": "File",
|
|
||||||
"finished": "Selesai",
|
|
||||||
"followSystem": "Ikuti sistem",
|
"followSystem": "Ikuti sistem",
|
||||||
"font": "Font",
|
"font": "Font",
|
||||||
"fontSize": "Ukuran huruf",
|
"fontSize": "Ukuran huruf",
|
||||||
"forExample": "Sebagai contoh",
|
|
||||||
"force": "sukarela",
|
"force": "sukarela",
|
||||||
"foundNUpdate": "Menemukan {count} pembaruan",
|
|
||||||
"fullScreen": "Mode Layar Penuh",
|
"fullScreen": "Mode Layar Penuh",
|
||||||
"fullScreenJitter": "Jitter layar penuh",
|
"fullScreenJitter": "Jitter layar penuh",
|
||||||
"fullScreenJitterHelp": "Untuk menghindari pembakaran layar",
|
"fullScreenJitterHelp": "Untuk menghindari pembakaran layar",
|
||||||
"fullScreenTip": "Apakah mode layar penuh diaktifkan ketika perangkat diputar ke modus lanskap? Opsi ini hanya berlaku untuk tab server.",
|
"fullScreenTip": "Apakah mode layar penuh diaktifkan ketika perangkat diputar ke modus lanskap? Opsi ini hanya berlaku untuk tab server.",
|
||||||
"getPushTokenFailed": "Tidak bisa mengambil token dorong",
|
|
||||||
"gettingToken": "Mendapatkan token ...",
|
|
||||||
"goBackQ": "Datang kembali?",
|
"goBackQ": "Datang kembali?",
|
||||||
"goto": "Pergi ke",
|
"goto": "Pergi ke",
|
||||||
"hideTitleBar": "Sembunyikan bilah judul",
|
"hideTitleBar": "Sembunyikan bilah judul",
|
||||||
"hideTitleBarTip": "Setelah dinyalakan, tekan dan tahan tiga tombol di sudut kanan atas untuk menyeret.",
|
|
||||||
"highlight": "Sorotan kode",
|
"highlight": "Sorotan kode",
|
||||||
"homeWidgetUrlConfig": "Konfigurasi URL Widget Rumah",
|
"homeWidgetUrlConfig": "Konfigurasi URL Widget Rumah",
|
||||||
"host": "Host",
|
"host": "Host",
|
||||||
"hour": "Jam",
|
|
||||||
"httpFailedWithCode": "Permintaan gagal, kode status: {code}",
|
"httpFailedWithCode": "Permintaan gagal, kode status: {code}",
|
||||||
"icloudSynced": "iCloud disinkronkan dan beberapa pengaturan mungkin memerlukan pengaktifan ulang aplikasi agar dapat diterapkan.",
|
|
||||||
"ignoreCert": "Abaikan sertifikat",
|
"ignoreCert": "Abaikan sertifikat",
|
||||||
"image": "Gambar",
|
"image": "Gambar",
|
||||||
"imagesList": "Daftar gambar",
|
"imagesList": "Daftar gambar",
|
||||||
"import": "Impor",
|
|
||||||
"inAppUpdate": "Perbarui di dalam aplikasi? Jika tidak, unduh menggunakan browser.",
|
|
||||||
"init": "Menginisialisasi",
|
"init": "Menginisialisasi",
|
||||||
"inner": "Batin",
|
"inner": "Batin",
|
||||||
"inputDomainHere": "Input domain di sini",
|
|
||||||
"install": "Install",
|
"install": "Install",
|
||||||
"installDockerWithUrl": "Silakan https://docs.docker.com/engine/install Docker pertama.",
|
"installDockerWithUrl": "Silakan https://docs.docker.com/engine/install Docker pertama.",
|
||||||
"invalid": "Tidak valid",
|
"invalid": "Tidak valid",
|
||||||
"invalidJson": "JSON tidak valid",
|
|
||||||
"invalidVersion": "Versi tidak valid",
|
|
||||||
"invalidVersionHelp": "Pastikan Docker diinstal dengan benar, atau Anda menggunakan versi yang tidak dikompilasi. Jika Anda tidak memiliki masalah di atas, silakan kirimkan masalah pada {url}.",
|
|
||||||
"isBusy": "Sibuk sekarang",
|
|
||||||
"jumpServer": "Lompat server",
|
"jumpServer": "Lompat server",
|
||||||
"keepForeground": "Simpan Aplikasi Foreground!",
|
"keepForeground": "Simpan Aplikasi Foreground!",
|
||||||
"keepStatusWhenErr": "Menyimpan status server terakhir",
|
"keepStatusWhenErr": "Menyimpan status server terakhir",
|
||||||
"keepStatusWhenErrTip": "Hanya ketika terjadi kesalahan saat menjalankan skrip",
|
"keepStatusWhenErrTip": "Hanya ketika terjadi kesalahan saat menjalankan skrip",
|
||||||
"keyAuth": "Auth kunci",
|
"keyAuth": "Auth kunci",
|
||||||
"language": "Bahasa",
|
"letterCache": "Caching huruf",
|
||||||
"languageName": "Indonesia",
|
"letterCacheTip": "Direkomendasikan untuk menonaktifkan, tetapi setelah dinonaktifkan, tidak mungkin untuk memasukkan karakter CJK.",
|
||||||
"lastTry": "Percobaan terakhir",
|
|
||||||
"launchPage": "Halaman peluncuran",
|
|
||||||
"license": "Lisensi",
|
"license": "Lisensi",
|
||||||
"light": "Terang",
|
|
||||||
"loadingFiles": "Memuat file ...",
|
|
||||||
"location": "Lokasi",
|
"location": "Lokasi",
|
||||||
"log": "Catatan",
|
|
||||||
"loss": "kehilangan",
|
"loss": "kehilangan",
|
||||||
"madeWithLove": "Dibuat dengan ❤️ oleh {myGithub}",
|
"madeWithLove": "Dibuat dengan ❤️ oleh {myGithub}",
|
||||||
"manual": "Manual",
|
"manual": "Manual",
|
||||||
@@ -167,60 +93,36 @@
|
|||||||
"maxRetryCount": "Jumlah penyambungan kembali server",
|
"maxRetryCount": "Jumlah penyambungan kembali server",
|
||||||
"maxRetryCountEqual0": "Akan mencoba lagi lagi dan lagi.",
|
"maxRetryCountEqual0": "Akan mencoba lagi lagi dan lagi.",
|
||||||
"min": "Min",
|
"min": "Min",
|
||||||
"minute": "Menit",
|
|
||||||
"mission": "Misi",
|
"mission": "Misi",
|
||||||
"more": "Lebih Banyak",
|
"more": "Lebih Banyak",
|
||||||
"moveOutServerFuncBtnsHelp": "Aktif: dapat ditampilkan di bawah setiap kartu pada halaman Tab Server. Nonaktif: dapat ditampilkan di bagian atas halaman Rincian Server.",
|
"moveOutServerFuncBtnsHelp": "Aktif: dapat ditampilkan di bawah setiap kartu pada halaman Tab Server. Nonaktif: dapat ditampilkan di bagian atas halaman Rincian Server.",
|
||||||
"ms": "MS",
|
"ms": "MS",
|
||||||
"name": "Nama",
|
|
||||||
"needHomeDir": "Jika Anda pengguna Synology, [lihat di sini](https://kb.synology.com/DSM/tutorial/user_enable_home_service). Pengguna sistem lain perlu mencari cara membuat direktori home.",
|
"needHomeDir": "Jika Anda pengguna Synology, [lihat di sini](https://kb.synology.com/DSM/tutorial/user_enable_home_service). Pengguna sistem lain perlu mencari cara membuat direktori home.",
|
||||||
"needRestart": "Perlu memulai ulang aplikasi",
|
"needRestart": "Perlu memulai ulang aplikasi",
|
||||||
"net": "Net",
|
"net": "Jaringan",
|
||||||
"netViewType": "Jenis tampilan bersih",
|
"netViewType": "Jenis tampilan bersih",
|
||||||
"newContainer": "Wadah baru",
|
"newContainer": "Wadah baru",
|
||||||
"noClient": "Tidak ada klien",
|
|
||||||
"noInterface": "Tidak ada antarmuka",
|
|
||||||
"noLineChart": "Jangan gunakan grafik garis",
|
"noLineChart": "Jangan gunakan grafik garis",
|
||||||
"noNotiPerm": "Tidak ada izin notifikasi, mungkin tidak ada indikasi kemajuan saat mengunduh pembaruan aplikasi.",
|
"noLineChartForCpu": "Jangan gunakan diagram garis untuk CPU",
|
||||||
"noOptions": "Tidak ada opsi",
|
|
||||||
"noPrivateKeyTip": "Kunci privat tidak ada, mungkin telah dihapus atau ada kesalahan konfigurasi.",
|
"noPrivateKeyTip": "Kunci privat tidak ada, mungkin telah dihapus atau ada kesalahan konfigurasi.",
|
||||||
"noPromptAgain": "Jangan tanya lagi",
|
"noPromptAgain": "Jangan tanya lagi",
|
||||||
"noResult": "Tidak ada hasil",
|
|
||||||
"noSavedPrivateKey": "Tidak ada kunci pribadi yang disimpan.",
|
|
||||||
"noSavedSnippet": "Tidak ada cuplikan yang disimpan.",
|
|
||||||
"noServerAvailable": "Tidak ada server yang tersedia.",
|
|
||||||
"noTask": "Tidak bertanya",
|
|
||||||
"noUpdateAvailable": "Tidak ada pembaruan yang tersedia",
|
|
||||||
"node": "Node",
|
"node": "Node",
|
||||||
"notAvailable": "Tidak tersedia",
|
"notAvailable": "Tidak tersedia",
|
||||||
"notSelected": "Tidak terpilih",
|
|
||||||
"note": "Catatan",
|
|
||||||
"nullToken": "Token NULL",
|
|
||||||
"ok": "OKE",
|
|
||||||
"onServerDetailPage": "Di halaman detail server",
|
"onServerDetailPage": "Di halaman detail server",
|
||||||
"onlyOneLine": "Hanya tampilkan sebagai satu baris (dapat digulir)",
|
"onlyOneLine": "Hanya tampilkan sebagai satu baris (dapat digulir)",
|
||||||
"onlyWhenCoreBiggerThan8": "Berlaku hanya ketika jumlah inti > 8",
|
"onlyWhenCoreBiggerThan8": "Berlaku hanya ketika jumlah inti > 8",
|
||||||
"open": "Membuka",
|
|
||||||
"openLastPath": "Buka jalur terakhir",
|
"openLastPath": "Buka jalur terakhir",
|
||||||
"openLastPathTip": "Server yang berbeda akan memiliki catatan yang berbeda, dan catatan tersebut adalah jalur menuju pintu keluar",
|
"openLastPathTip": "Server yang berbeda akan memiliki catatan yang berbeda, dan catatan tersebut adalah jalur menuju pintu keluar",
|
||||||
"parseContainerStats": "Memecahkan status okupansi kontainer",
|
|
||||||
"parseContainerStatsTip": "Parsing status okupansi oleh Docker agak lambat",
|
"parseContainerStatsTip": "Parsing status okupansi oleh Docker agak lambat",
|
||||||
"paste": "Tempel",
|
|
||||||
"path": "Jalur",
|
|
||||||
"percentOfSize": "{percent}% dari {size}",
|
"percentOfSize": "{percent}% dari {size}",
|
||||||
"pickFile": "Pilih file",
|
"permission": "Izin",
|
||||||
"pingAvg": "Rata -rata:",
|
"pingAvg": "Rata -rata:",
|
||||||
"pingInputIP": "Harap masukkan IP / domain target.",
|
"pingInputIP": "Harap masukkan IP / domain target.",
|
||||||
"pingNoServer": "Tidak ada server untuk melakukan ping.\nHarap tambahkan server di tab Server.",
|
"pingNoServer": "Tidak ada server untuk melakukan ping.\nHarap tambahkan server di tab Server.",
|
||||||
"pkg": "Pkg",
|
"pkg": "Pkg",
|
||||||
"pkgUpgradeTip": "Harap cadangkan sistem Anda sebelum memperbarui.",
|
|
||||||
"platformNotSupportUpdate": "Platform saat ini tidak mendukung pembaruan aplikasi.\nSilakan bangun dari sumber dan instal.",
|
|
||||||
"plugInType": "Jenis Penyisipan",
|
"plugInType": "Jenis Penyisipan",
|
||||||
"plzEnterHost": "Harap masukkan host.",
|
|
||||||
"plzSelectKey": "Pilih kunci.",
|
|
||||||
"port": "Port",
|
"port": "Port",
|
||||||
"preview": "Pratinjau",
|
"preview": "Pratinjau",
|
||||||
"primaryColorSeed": "Warna utama",
|
|
||||||
"privateKey": "Kunci Pribadi",
|
"privateKey": "Kunci Pribadi",
|
||||||
"process": "Proses",
|
"process": "Proses",
|
||||||
"pushToken": "Dorong token",
|
"pushToken": "Dorong token",
|
||||||
@@ -230,16 +132,11 @@
|
|||||||
"pwd": "Kata sandi",
|
"pwd": "Kata sandi",
|
||||||
"read": "Baca",
|
"read": "Baca",
|
||||||
"reboot": "Reboot",
|
"reboot": "Reboot",
|
||||||
"rememberChoice": "Ingat pilihan",
|
|
||||||
"rememberPwdInMem": "Ingat kata sandi di dalam memori",
|
"rememberPwdInMem": "Ingat kata sandi di dalam memori",
|
||||||
"rememberPwdInMemTip": "Digunakan untuk kontainer, menangguhkan, dll.",
|
"rememberPwdInMemTip": "Digunakan untuk kontainer, menangguhkan, dll.",
|
||||||
"rememberWindowSize": "Ingat ukuran jendela",
|
"rememberWindowSize": "Ingat ukuran jendela",
|
||||||
"remotePath": "Jalur jarak jauh",
|
"remotePath": "Jalur jarak jauh",
|
||||||
"rename": "Ganti nama",
|
|
||||||
"reportBugsOnGithubIssue": "Harap laporkan bug di {url}",
|
|
||||||
"restart": "Mengulang kembali",
|
"restart": "Mengulang kembali",
|
||||||
"restore": "Memulihkan",
|
|
||||||
"restoreSuccess": "Kembalikan kesuksesan. Mulai ulang aplikasi untuk diterapkan.",
|
|
||||||
"result": "Hasil",
|
"result": "Hasil",
|
||||||
"rotateAngel": "Sudut rotasi",
|
"rotateAngel": "Sudut rotasi",
|
||||||
"route": "Routing",
|
"route": "Routing",
|
||||||
@@ -254,14 +151,8 @@
|
|||||||
"serverDetailOrder": "Detail pesanan widget halaman",
|
"serverDetailOrder": "Detail pesanan widget halaman",
|
||||||
"serverFuncBtns": "Tombol fungsi server",
|
"serverFuncBtns": "Tombol fungsi server",
|
||||||
"serverOrder": "Pesanan server",
|
"serverOrder": "Pesanan server",
|
||||||
"serverTabConnecting": "Menghubungkan ...",
|
|
||||||
"serverTabEmpty": "Tidak ada server.\nKlik fab untuk menambahkan satu.",
|
|
||||||
"serverTabFailed": "Gagal",
|
|
||||||
"serverTabLoading": "Memuat...",
|
|
||||||
"serverTabPlzSave": "Harap 'simpan' kunci pribadi ini lagi.",
|
|
||||||
"serverTabUnkown": "Negara yang tidak diketahui",
|
|
||||||
"setting": "Pengaturan",
|
|
||||||
"sftpDlPrepare": "Bersiap untuk terhubung ...",
|
"sftpDlPrepare": "Bersiap untuk terhubung ...",
|
||||||
|
"sftpEditorTip": "Jika kosong, gunakan editor file bawaan aplikasi. Jika ada nilai, gunakan editor server jarak jauh, misalnya `vim` (disarankan untuk mendeteksi secara otomatis sesuai `EDITOR`).",
|
||||||
"sftpRmrDirSummary": "Gunakan `rm -r` untuk menghapus dir di SFTP",
|
"sftpRmrDirSummary": "Gunakan `rm -r` untuk menghapus dir di SFTP",
|
||||||
"sftpSSHConnected": "Sftp terhubung",
|
"sftpSSHConnected": "Sftp terhubung",
|
||||||
"sftpShowFoldersFirst": "Folder ditampilkan lebih dulu",
|
"sftpShowFoldersFirst": "Folder ditampilkan lebih dulu",
|
||||||
@@ -270,17 +161,19 @@
|
|||||||
"size": "Ukuran",
|
"size": "Ukuran",
|
||||||
"snippet": "Snippet",
|
"snippet": "Snippet",
|
||||||
"softWrap": "Pembungkus lembut",
|
"softWrap": "Pembungkus lembut",
|
||||||
|
"specifyDev": "Tentukan perangkat",
|
||||||
|
"specifyDevTip": "Misalnya, statistik lalu lintas jaringan secara default adalah untuk semua perangkat. Anda dapat menentukan perangkat tertentu di sini.",
|
||||||
"speed": "Kecepatan",
|
"speed": "Kecepatan",
|
||||||
"spentTime": "Menghabiskan waktu: {time}",
|
"spentTime": "Menghabiskan waktu: {time}",
|
||||||
"sshTermHelp": "Ketika terminal dapat digulirkan, menggeser secara horizontal dapat memilih teks. Mengklik tombol keyboard mengaktifkan/menonaktifkan keyboard. Ikon file membuka SFTP jalur saat ini. Tombol papan klip menyalin konten saat teks dipilih, dan menempelkan konten dari papan klip ke terminal saat tidak ada teks yang dipilih dan ada konten di papan klip. Ikon kode menempelkan potongan kode ke terminal dan mengeksekusinya.",
|
"sshTermHelp": "Ketika terminal dapat digulirkan, menggeser secara horizontal dapat memilih teks. Mengklik tombol keyboard mengaktifkan/menonaktifkan keyboard. Ikon file membuka SFTP jalur saat ini. Tombol papan klip menyalin konten saat teks dipilih, dan menempelkan konten dari papan klip ke terminal saat tidak ada teks yang dipilih dan ada konten di papan klip. Ikon kode menempelkan potongan kode ke terminal dan mengeksekusinya.",
|
||||||
"sshTip": "Fungsi ini sekarang dalam tahap eksperimen.\n\nHarap laporkan bug di {url} atau bergabunglah dengan pengembangan kami.",
|
"sshTip": "Fungsi ini sekarang dalam tahap eksperimen.\n\nHarap laporkan bug di {url} atau bergabunglah dengan pengembangan kami.",
|
||||||
"sshVirtualKeyAutoOff": "Switching Otomatis Kunci Virtual",
|
"sshVirtualKeyAutoOff": "Switching Otomatis Kunci Virtual",
|
||||||
"start": "Awal",
|
"start": "Awal",
|
||||||
|
"stat": "Statistik",
|
||||||
"stats": "Statistik",
|
"stats": "Statistik",
|
||||||
"stop": "Berhenti",
|
"stop": "Berhenti",
|
||||||
"stopped": "dihentikan",
|
"stopped": "dihentikan",
|
||||||
"storage": "Penyimpanan",
|
"storage": "Penyimpanan",
|
||||||
"success": "Kesuksesan",
|
|
||||||
"supportFmtArgs": "Parameter pemformatan berikut ini didukung:",
|
"supportFmtArgs": "Parameter pemformatan berikut ini didukung:",
|
||||||
"suspend": "Suspend",
|
"suspend": "Suspend",
|
||||||
"suspendTip": "Fungsi penangguhan memerlukan hak akses root dan dukungan systemd.",
|
"suspendTip": "Fungsi penangguhan memerlukan hak akses root dan dukungan systemd.",
|
||||||
@@ -296,35 +189,25 @@
|
|||||||
"textScaler": "Penskalaan font",
|
"textScaler": "Penskalaan font",
|
||||||
"textScalerTip": "1.0 => 100% (ukuran asli), hanya berfungsi pada bagian halaman server font, tidak disarankan untuk diubah.",
|
"textScalerTip": "1.0 => 100% (ukuran asli), hanya berfungsi pada bagian halaman server font, tidak disarankan untuk diubah.",
|
||||||
"theme": " Tema",
|
"theme": " Tema",
|
||||||
"themeMode": "Mode tema",
|
|
||||||
"time": "Waktu",
|
"time": "Waktu",
|
||||||
"times": "Waktu",
|
"times": "Waktu",
|
||||||
"total": "Total",
|
"total": "Total",
|
||||||
"traffic": "Lalu lintas",
|
"traffic": "Lalu lintas",
|
||||||
"trySudo": "Cobalah menggunakan sudo",
|
"trySudo": "Cobalah menggunakan sudo",
|
||||||
"ttl": "ttl",
|
"ttl": "TTL",
|
||||||
"unknown": "Tidak dikenal",
|
"unknown": "Tidak dikenal",
|
||||||
"unknownError": "Kesalahan yang tidak diketahui",
|
|
||||||
"unkownConvertMode": "Mode Konversi Tidak Diketahui",
|
"unkownConvertMode": "Mode Konversi Tidak Diketahui",
|
||||||
"update": "Memperbarui",
|
"update": "Memperbarui",
|
||||||
"updateAll": "Perbarui semua",
|
|
||||||
"updateIntervalEqual0": "Anda mengatur ke 0, tidak akan memperbarui secara otomatis.\nTidak dapat menghitung status CPU.",
|
"updateIntervalEqual0": "Anda mengatur ke 0, tidak akan memperbarui secara otomatis.\nTidak dapat menghitung status CPU.",
|
||||||
"updateServerStatusInterval": "Interval Pembaruan Status Server",
|
"updateServerStatusInterval": "Interval Pembaruan Status Server",
|
||||||
"updateTip": "UPDATE: v1.0.{newest}",
|
|
||||||
"updateTipTooLow": "Versi saat ini terlalu rendah, harap perbarui ke v1.0.{newest}",
|
|
||||||
"upload": "Mengunggah",
|
"upload": "Mengunggah",
|
||||||
"upsideDown": "Terbalik",
|
"upsideDown": "Terbalik",
|
||||||
"uptime": "Uptime",
|
"uptime": "Uptime",
|
||||||
"urlOrJson": "URL atau JSON",
|
|
||||||
"useCdn": "Menggunakan CDN",
|
"useCdn": "Menggunakan CDN",
|
||||||
"useCdnTip": "Pengguna non-Cina disarankan menggunakan CDN. Apakah Anda ingin menggunakannya?",
|
"useCdnTip": "Pengguna non-Cina disarankan menggunakan CDN. Apakah Anda ingin menggunakannya?",
|
||||||
"useNoPwd": "Tidak ada kata sandi yang akan digunakan",
|
"useNoPwd": "Tidak ada kata sandi yang akan digunakan",
|
||||||
"usePodmanByDefault": "Menggunakan Podman sebagai bawaan",
|
"usePodmanByDefault": "Menggunakan Podman sebagai bawaan",
|
||||||
"used": "Digunakan",
|
"used": "Digunakan",
|
||||||
"user": "Username",
|
|
||||||
"versionHaveUpdate": "Ditemukan: v1.0.{build}, klik untuk memperbarui",
|
|
||||||
"versionUnknownUpdate": "Saat ini: v1.0.{build}. Klik untuk memeriksa pembaruan.",
|
|
||||||
"versionUpdated": "Saat ini: v1.0.{build}, mutakhir",
|
|
||||||
"view": "Tampilan",
|
"view": "Tampilan",
|
||||||
"viewErr": "Lihat kesalahan",
|
"viewErr": "Lihat kesalahan",
|
||||||
"virtKeyHelpClipboard": "Salin ke clipboard jika terminal yang dipilih tidak kosong, jika tidak, tempel isi clipboard ke terminal.",
|
"virtKeyHelpClipboard": "Salin ke clipboard jika terminal yang dipilih tidak kosong, jika tidak, tempel isi clipboard ke terminal.",
|
||||||
@@ -335,8 +218,8 @@
|
|||||||
"watchNotPaired": "Tidak ada Apple Watch yang dipasangkan",
|
"watchNotPaired": "Tidak ada Apple Watch yang dipasangkan",
|
||||||
"webdavSettingEmpty": "Pengaturan webdav kosong",
|
"webdavSettingEmpty": "Pengaturan webdav kosong",
|
||||||
"whenOpenApp": "Saat membuka aplikasi",
|
"whenOpenApp": "Saat membuka aplikasi",
|
||||||
"willTakEeffectImmediately": "Akan segera berlaku",
|
|
||||||
"wolTip": "Setelah mengonfigurasi WOL (Wake-on-LAN), permintaan WOL dikirim setiap kali server terhubung.",
|
"wolTip": "Setelah mengonfigurasi WOL (Wake-on-LAN), permintaan WOL dikirim setiap kali server terhubung.",
|
||||||
"write": "Tulis",
|
"write": "Tulis",
|
||||||
"writeScriptFailTip": "Penulisan ke skrip gagal, mungkin karena tidak ada izin atau direktori tidak ada."
|
"writeScriptFailTip": "Penulisan ke skrip gagal, mungkin karena tidak ada izin atau direktori tidak ada.",
|
||||||
|
"writeScriptTip": "Setelah terhubung ke server, sebuah skrip akan ditulis ke ~/.config/server_box untuk memantau status sistem. Anda dapat meninjau konten skrip tersebut."
|
||||||
}
|
}
|
||||||
@@ -1,87 +1,42 @@
|
|||||||
{
|
{
|
||||||
"@@locale": "ja",
|
"@@locale": "ja",
|
||||||
"about": "約",
|
|
||||||
"aboutThanks": "以下の参加者に感謝します。",
|
"aboutThanks": "以下の参加者に感謝します。",
|
||||||
"acceptBeta": "テストバージョンの更新を受け入れる",
|
"acceptBeta": "テストバージョンの更新を受け入れる",
|
||||||
"add": "追加",
|
"addSystemPrivateKeyTip": "現在秘密鍵がありません。システムのデフォルト(~/.ssh/id_rsa)を追加しますか?",
|
||||||
"addAServer": "サーバーを追加する",
|
|
||||||
"addPrivateKey": "プライベートキーを追加",
|
|
||||||
"addSystemPrivateKeyTip": "現在プライベートキーがありません。システムのデフォルト(~/.ssh/id_rsa)を追加しますか?",
|
|
||||||
"added2List": "タスクリストに追加されました",
|
"added2List": "タスクリストに追加されました",
|
||||||
"addr": "住所",
|
"addr": "アドレス",
|
||||||
"all": "すべて",
|
|
||||||
"alreadyLastDir": "すでに最上位のディレクトリです",
|
"alreadyLastDir": "すでに最上位のディレクトリです",
|
||||||
"alterUrl": "代替リンク",
|
|
||||||
"askContinue": "{msg}、続行しますか?",
|
|
||||||
"attention": "注意",
|
|
||||||
"authFailTip": "認証に失敗しました。パスワード/鍵/ホスト/ユーザーなどが間違っていないか確認してください。",
|
"authFailTip": "認証に失敗しました。パスワード/鍵/ホスト/ユーザーなどが間違っていないか確認してください。",
|
||||||
"authRequired": "認証が必要",
|
|
||||||
"auto": "自動",
|
|
||||||
"autoBackupConflict": "自動バックアップは一度に一つしか開始できません",
|
"autoBackupConflict": "自動バックアップは一度に一つしか開始できません",
|
||||||
"autoCheckUpdate": "自動的に更新をチェック",
|
|
||||||
"autoConnect": "自動接続",
|
"autoConnect": "自動接続",
|
||||||
"autoRun": "自動実行",
|
"autoRun": "自動実行",
|
||||||
"autoUpdateHomeWidget": "ホームウィジェットを自動更新",
|
"autoUpdateHomeWidget": "ホームウィジェットを自動更新",
|
||||||
"backup": "バックアップ",
|
|
||||||
"backupTip": "エクスポートされたデータは簡単に暗号化されています。適切に保管してください。",
|
"backupTip": "エクスポートされたデータは簡単に暗号化されています。適切に保管してください。",
|
||||||
"backupVersionNotMatch": "バックアップバージョンが一致しないため、復元できません",
|
"backupVersionNotMatch": "バックアップバージョンが一致しないため、復元できません",
|
||||||
"battery": "バッテリー",
|
"battery": "バッテリー",
|
||||||
"beforeConnect": "ServerBoxは接続後に`~/.config/server_box`にスクリプトを書き込み実行します。詳細な技術情報については[Github]({url})をご覧ください。",
|
|
||||||
"bgRun": "バックグラウンド実行",
|
"bgRun": "バックグラウンド実行",
|
||||||
"bgRunTip": "このスイッチはプログラムがバックグラウンドで実行を試みることを意味しますが、実際にバックグラウンドで実行できるかどうかは、権限が有効になっているかに依存します。ネイティブAndroidでは、このアプリの「バッテリー最適化」をオフにしてください。MIUIでは、省エネモードを「無制限」に変更してください。",
|
"bgRunTip": "このスイッチはプログラムがバックグラウンドで実行を試みることを意味しますが、実際にバックグラウンドで実行できるかどうかは、権限が有効になっているかに依存します。AOSPベースのAndroid ROMでは、このアプリの「バッテリー最適化」をオフにしてください。MIUIでは、省エネモードを「無制限」に変更してください。",
|
||||||
"bioAuth": "生体認証",
|
|
||||||
"browser": "ブラウザ",
|
|
||||||
"bulkImportServers": "サーバーを一括インポートする",
|
|
||||||
"bulkImportServersTip": "[こちら]({url})でフォーマットを見つけることができます",
|
|
||||||
"canPullRefresh": "引っ張って更新できます",
|
|
||||||
"cancel": "キャンセル",
|
|
||||||
"choose": "選択",
|
|
||||||
"chooseFontFile": "フォントファイルを選択",
|
|
||||||
"choosePrivateKey": "プライベートキーを選択",
|
|
||||||
"clear": "クリア",
|
|
||||||
"clipboard": "クリップボード",
|
|
||||||
"close": "閉じる",
|
|
||||||
"cmd": "コマンド",
|
"cmd": "コマンド",
|
||||||
"cnKeyboardComp": "中国のAndroidとの互換性",
|
|
||||||
"cnKeyboardCompTip": "ターミナルがセキュアキーボードを表示した場合、それを有効にできます。",
|
|
||||||
"collapseUI": "UIを折りたたむ",
|
|
||||||
"collapseUITip": "UIの長いリストをデフォルトで折りたたむかどうか",
|
"collapseUITip": "UIの長いリストをデフォルトで折りたたむかどうか",
|
||||||
"conn": "接続",
|
"conn": "接続",
|
||||||
"connected": "接続済み",
|
|
||||||
"container": "コンテナ",
|
"container": "コンテナ",
|
||||||
"containerName": "コンテナ名",
|
|
||||||
"containerStatus": "コンテナの状態",
|
|
||||||
"containerTrySudoTip": "例:アプリ内でユーザーをaaaに設定しているが、Dockerがrootユーザーでインストールされている場合、このオプションを有効にする必要があります",
|
"containerTrySudoTip": "例:アプリ内でユーザーをaaaに設定しているが、Dockerがrootユーザーでインストールされている場合、このオプションを有効にする必要があります",
|
||||||
"content": "コンテンツ",
|
|
||||||
"convert": "変換",
|
"convert": "変換",
|
||||||
"copy": "コピー",
|
|
||||||
"copyPath": "パスをコピー",
|
"copyPath": "パスをコピー",
|
||||||
"cpuViewAsProgressTip": "各CPUの使用率をプログレスバースタイルで表示する(旧スタイル)",
|
"cpuViewAsProgressTip": "各CPUの使用率をプログレスバースタイルで表示する(旧スタイル)",
|
||||||
"createFile": "ファイルを作成",
|
|
||||||
"createFolder": "フォルダーを作成",
|
|
||||||
"cursorType": "カーソルタイプ",
|
"cursorType": "カーソルタイプ",
|
||||||
"customCmd": "カスタムコマンド",
|
"customCmd": "カスタムコマンド",
|
||||||
"customCmdDocUrl": "https://github.com/lollipopkit/flutter_server_box/wiki#custom-commands",
|
"customCmdDocUrl": "https://github.com/lollipopkit/flutter_server_box/wiki#custom-commands",
|
||||||
"customCmdHint": "\"コマンド名\": \"コマンド\"",
|
"customCmdHint": "\"コマンド名\": \"コマンド\"",
|
||||||
"dark": "ダーク",
|
|
||||||
"day": "日",
|
|
||||||
"debug": "デバッグ",
|
|
||||||
"decode": "デコード",
|
"decode": "デコード",
|
||||||
"decompress": "解凍",
|
"decompress": "解凍",
|
||||||
"delete": "削除",
|
|
||||||
"deleteScripts": "サーバースクリプトも削除",
|
|
||||||
"deleteServers": "サーバーを一括削除",
|
"deleteServers": "サーバーを一括削除",
|
||||||
"deviceName": "デバイス名",
|
|
||||||
"dirEmpty": "フォルダーが空であることを確認してください",
|
"dirEmpty": "フォルダーが空であることを確認してください",
|
||||||
"disabled": "無効",
|
|
||||||
"disconnected": "接続が切断されました",
|
"disconnected": "接続が切断されました",
|
||||||
"disk": "ディスク",
|
"disk": "ディスク",
|
||||||
"diskIgnorePath": "無視されたディスクパス",
|
"diskIgnorePath": "無視されたディスクパス",
|
||||||
"displayCpuIndex": "CPUインデックスを表示する",
|
"displayCpuIndex": "CPUインデックスを表示する",
|
||||||
"displayName": "表示名",
|
|
||||||
"dl2Local": "{fileName}をローカルにダウンロードしますか?",
|
"dl2Local": "{fileName}をローカルにダウンロードしますか?",
|
||||||
"doc": "ドキュメント",
|
|
||||||
"dockerEditHost": "DOCKER_HOSTを編集",
|
|
||||||
"dockerEmptyRunningItems": "実行中のコンテナがありません。\nこれは次の理由による可能性があります:\n- Dockerのインストールユーザーとアプリ内の設定されたユーザー名が異なる\n- 環境変数DOCKER_HOSTが正しく読み込まれていない。ターミナルで`echo $DOCKER_HOST`を実行して取得できます。",
|
"dockerEmptyRunningItems": "実行中のコンテナがありません。\nこれは次の理由による可能性があります:\n- Dockerのインストールユーザーとアプリ内の設定されたユーザー名が異なる\n- 環境変数DOCKER_HOSTが正しく読み込まれていない。ターミナルで`echo $DOCKER_HOST`を実行して取得できます。",
|
||||||
"dockerImagesFmt": "合計{count}イメージ",
|
"dockerImagesFmt": "合計{count}イメージ",
|
||||||
"dockerNotInstalled": "Dockerがインストールされていません",
|
"dockerNotInstalled": "Dockerがインストールされていません",
|
||||||
@@ -89,77 +44,48 @@
|
|||||||
"dockerStatusRunningFmt": "{count}個のコンテナが実行中",
|
"dockerStatusRunningFmt": "{count}個のコンテナが実行中",
|
||||||
"doubleColumnMode": "ダブルカラムモード",
|
"doubleColumnMode": "ダブルカラムモード",
|
||||||
"doubleColumnTip": "このオプションは機能を有効にするだけで、実際に有効にできるかどうかはデバイスの幅に依存します",
|
"doubleColumnTip": "このオプションは機能を有効にするだけで、実際に有効にできるかどうかはデバイスの幅に依存します",
|
||||||
"download": "ダウンロード",
|
|
||||||
"edit": "編集",
|
|
||||||
"editVirtKeys": "仮想キーを編集",
|
"editVirtKeys": "仮想キーを編集",
|
||||||
"editor": "エディター",
|
"editor": "エディター",
|
||||||
"editorHighlightTip": "現在のコードハイライトのパフォーマンスはかなり悪いため、改善するために無効にすることを選択できます。",
|
"editorHighlightTip": "現在のコードハイライトのパフォーマンスはかなり悪いため、改善するために無効にすることを選択できます。",
|
||||||
"encode": "エンコード",
|
"encode": "エンコード",
|
||||||
"error": "エラー",
|
"envVars": "環境変数",
|
||||||
"exampleName": "名前例",
|
|
||||||
"experimentalFeature": "実験的な機能",
|
"experimentalFeature": "実験的な機能",
|
||||||
"export": "エクスポート",
|
|
||||||
"extraArgs": "追加引数",
|
"extraArgs": "追加引数",
|
||||||
"failed": "失敗しました",
|
"fallbackSshDest": "フォールバックSSH宛先",
|
||||||
"fdroidReleaseTip": "このアプリをFdroidからダウンロードした場合、このオプションをオフにすることをお勧めします。",
|
"fdroidReleaseTip": "このアプリをF-Droidからダウンロードした場合、このオプションをオフにすることをお勧めします。",
|
||||||
"feedback": "フィードバック",
|
|
||||||
"feedbackOnGithub": "問題がある場合は、GitHubでフィードバックしてください",
|
|
||||||
"fieldMustNotEmpty": "これらの入力フィールドは空にできません。",
|
|
||||||
"fileNotExist": "{file}は存在しません",
|
|
||||||
"fileTooLarge": "ファイル '{file}' は大きすぎます '{size}'、{sizeMax} を超えています",
|
"fileTooLarge": "ファイル '{file}' は大きすぎます '{size}'、{sizeMax} を超えています",
|
||||||
"files": "ファイル",
|
|
||||||
"finished": "完了しました",
|
|
||||||
"followSystem": "システムに従う",
|
"followSystem": "システムに従う",
|
||||||
"font": "フォント",
|
"font": "フォント",
|
||||||
"fontSize": "フォントサイズ",
|
"fontSize": "フォントサイズ",
|
||||||
"forExample": "例えば",
|
|
||||||
"force": "強制",
|
"force": "強制",
|
||||||
"foundNUpdate": "{count}個の更新が見つかりました",
|
|
||||||
"fullScreen": "フルスクリーンモード",
|
"fullScreen": "フルスクリーンモード",
|
||||||
"fullScreenJitter": "フルスクリーンモードのジッター",
|
"fullScreenJitter": "フルスクリーンモードのジッター",
|
||||||
"fullScreenJitterHelp": "焼き付き防止",
|
"fullScreenJitterHelp": "焼き付き防止",
|
||||||
"fullScreenTip": "デバイスが横向きに回転したときにフルスクリーンモードを有効にしますか?このオプションはサーバータブにのみ適用されます。",
|
"fullScreenTip": "デバイスが横向きに回転したときにフルスクリーンモードを有効にしますか?このオプションはサーバータブにのみ適用されます。",
|
||||||
"getPushTokenFailed": "プッシュトークンを取得できませんでした",
|
|
||||||
"gettingToken": "トークンを取得しています...",
|
|
||||||
"goBackQ": "戻りますか?",
|
"goBackQ": "戻りますか?",
|
||||||
"goto": "移動",
|
"goto": "移動",
|
||||||
"hideTitleBar": "タイトルバーを非表示にする",
|
"hideTitleBar": "タイトルバーを非表示にする",
|
||||||
"hideTitleBarTip": "電源を入れた後、右上隅の3つのボタンを押し続けてドラッグしてください。",
|
|
||||||
"highlight": "コードハイライト",
|
"highlight": "コードハイライト",
|
||||||
"homeWidgetUrlConfig": "ホームウィジェットURL設定",
|
"homeWidgetUrlConfig": "ホームウィジェットURL設定",
|
||||||
"host": "ホスト",
|
"host": "ホスト",
|
||||||
"hour": "時間",
|
|
||||||
"httpFailedWithCode": "リクエスト失敗、ステータスコード: {code}",
|
"httpFailedWithCode": "リクエスト失敗、ステータスコード: {code}",
|
||||||
"icloudSynced": "iCloudが同期されました。一部の設定はアプリを再起動する必要があります。",
|
|
||||||
"ignoreCert": "証明書を無視する",
|
"ignoreCert": "証明書を無視する",
|
||||||
"image": "イメージ",
|
"image": "イメージ",
|
||||||
"imagesList": "イメージリスト",
|
"imagesList": "イメージリスト",
|
||||||
"import": "インポート",
|
|
||||||
"inAppUpdate": "アプリ内で更新しますか?それ以外の場合は、ブラウザを使用してダウンロードしてください。",
|
|
||||||
"init": "初期化する",
|
"init": "初期化する",
|
||||||
"inner": "内蔵",
|
"inner": "内蔵",
|
||||||
"inputDomainHere": "ここにドメインを入力",
|
|
||||||
"install": "インストール",
|
"install": "インストール",
|
||||||
"installDockerWithUrl": "最初に https://docs.docker.com/engine/install dockerをインストールしてください",
|
"installDockerWithUrl": "最初に https://docs.docker.com/engine/install dockerをインストールしてください",
|
||||||
"invalid": "無効",
|
"invalid": "無効",
|
||||||
"invalidJson": "無効なJSON",
|
|
||||||
"invalidVersion": "サポートされていないバージョン",
|
|
||||||
"invalidVersionHelp": "dockerが正しくインストールされていること、または自己コンパイルされたバージョンを使用していないことを確認してください。問題がない場合は、{url}で問題を報告してください。",
|
|
||||||
"isBusy": "現在忙しいです",
|
|
||||||
"jumpServer": "ジャンプサーバー",
|
"jumpServer": "ジャンプサーバー",
|
||||||
"keepForeground": "アプリを前面に保ってください!",
|
"keepForeground": "アプリを前面に保ってください!",
|
||||||
"keepStatusWhenErr": "エラー時に前回のサーバーステータスを保持",
|
"keepStatusWhenErr": "エラー時に前回のサーバーステータスを保持",
|
||||||
"keepStatusWhenErrTip": "スクリプトの実行エラーに限ります",
|
"keepStatusWhenErrTip": "スクリプトの実行エラーに限ります",
|
||||||
"keyAuth": "キー認証",
|
"keyAuth": "キー認証",
|
||||||
"language": "言語",
|
"letterCache": "文字キャッシング",
|
||||||
"languageName": "日本語",
|
"letterCacheTip": "無効にすることを推奨しますが、無効にした後はCJK文字を入力することができなくなります。",
|
||||||
"lastTry": "最後の試み",
|
|
||||||
"launchPage": "起動ページ",
|
|
||||||
"license": "オープンソースライセンス",
|
"license": "オープンソースライセンス",
|
||||||
"light": "ライト",
|
|
||||||
"loadingFiles": "ディレクトリを読み込んでいます...",
|
|
||||||
"location": "場所",
|
"location": "場所",
|
||||||
"log": "ログ",
|
|
||||||
"loss": "パケットロス",
|
"loss": "パケットロス",
|
||||||
"madeWithLove": "❤️で作成された {myGithub}",
|
"madeWithLove": "❤️で作成された {myGithub}",
|
||||||
"manual": "マニュアル",
|
"manual": "マニュアル",
|
||||||
@@ -167,61 +93,37 @@
|
|||||||
"maxRetryCount": "サーバーの再接続試行回数",
|
"maxRetryCount": "サーバーの再接続試行回数",
|
||||||
"maxRetryCountEqual0": "無限に再試行します",
|
"maxRetryCountEqual0": "無限に再試行します",
|
||||||
"min": "最小",
|
"min": "最小",
|
||||||
"minute": "分",
|
|
||||||
"mission": "ミッション",
|
"mission": "ミッション",
|
||||||
"more": "もっと",
|
"more": "もっと",
|
||||||
"moveOutServerFuncBtnsHelp": "有効にする:サーバータブの各カードの下に表示されます。無効にする:サーバーの詳細ページの上部に表示されます。",
|
"moveOutServerFuncBtnsHelp": "有効にする:サーバータブの各カードの下に表示されます。無効にする:サーバーの詳細ページの上部に表示されます。",
|
||||||
"ms": "ミリ秒",
|
"ms": "ミリ秒",
|
||||||
"name": "名前",
|
|
||||||
"needHomeDir": "Synologyユーザーの場合は、[こちらをご覧ください](https://kb.synology.com/DSM/tutorial/user_enable_home_service)。他のシステムのユーザーは、ホームディレクトリの作成方法を検索する必要があります。",
|
"needHomeDir": "Synologyユーザーの場合は、[こちらをご覧ください](https://kb.synology.com/DSM/tutorial/user_enable_home_service)。他のシステムのユーザーは、ホームディレクトリの作成方法を検索する必要があります。",
|
||||||
"needRestart": "アプリを再起動する必要があります",
|
"needRestart": "アプリを再起動する必要があります",
|
||||||
"net": "ネットワーク",
|
"net": "ネットワーク",
|
||||||
"netViewType": "ネットワークビュータイプ",
|
"netViewType": "ネットワークビュータイプ",
|
||||||
"newContainer": "新しいコンテナを作成",
|
"newContainer": "新しいコンテナを作成",
|
||||||
"noClient": "SSH接続がありません",
|
|
||||||
"noInterface": "使用可能なインターフェースがありません",
|
|
||||||
"noLineChart": "折れ線グラフを使用しない",
|
"noLineChart": "折れ線グラフを使用しない",
|
||||||
"noNotiPerm": "通知の権限がないため、アプリの更新のダウンロード中に進行状況が表示されない場合があります。",
|
"noLineChartForCpu": "CPUに折れ線グラフを使わない",
|
||||||
"noOptions": "選択肢がありません",
|
"noPrivateKeyTip": "秘密鍵が存在しません。削除されたか、設定ミスがある可能性があります。",
|
||||||
"noPrivateKeyTip": "私有鍵が存在しません。削除されたか、設定ミスがある可能性があります。",
|
|
||||||
"noPromptAgain": "再度確認しない",
|
"noPromptAgain": "再度確認しない",
|
||||||
"noResult": "結果なし",
|
|
||||||
"noSavedPrivateKey": "保存されたプライベートキーがありません。",
|
|
||||||
"noSavedSnippet": "保存されたスニペットがありません。",
|
|
||||||
"noServerAvailable": "使用可能なサーバーがありません。",
|
|
||||||
"noTask": "タスクがありません",
|
|
||||||
"noUpdateAvailable": "利用可能な更新はありません",
|
|
||||||
"node": "ノード",
|
"node": "ノード",
|
||||||
"notAvailable": "利用不可",
|
"notAvailable": "利用不可",
|
||||||
"notSelected": "選択されていません",
|
|
||||||
"note": "メモ",
|
|
||||||
"nullToken": "トークンなし",
|
|
||||||
"ok": "OK",
|
|
||||||
"onServerDetailPage": "サーバーの詳細ページで",
|
"onServerDetailPage": "サーバーの詳細ページで",
|
||||||
"onlyOneLine": "一行のみ表示(スクロール可能)",
|
"onlyOneLine": "一行のみ表示(スクロール可能)",
|
||||||
"onlyWhenCoreBiggerThan8": "コア数が8より大きい場合にのみ有効",
|
"onlyWhenCoreBiggerThan8": "コア数が8より大きい場合にのみ有効",
|
||||||
"open": "開く",
|
|
||||||
"openLastPath": "最後のパスを開く",
|
"openLastPath": "最後のパスを開く",
|
||||||
"openLastPathTip": "異なるサーバーには異なる記録があり、記録されているのは退出時のパスです",
|
"openLastPathTip": "異なるサーバーには異なる記録があり、記録されているのは退出時のパスです",
|
||||||
"parseContainerStats": "コンテナ使用状況を解析",
|
|
||||||
"parseContainerStatsTip": "Dockerの使用状況の解析は比較的遅いです",
|
"parseContainerStatsTip": "Dockerの使用状況の解析は比較的遅いです",
|
||||||
"paste": "貼り付け",
|
|
||||||
"path": "パス",
|
|
||||||
"percentOfSize": "{size} の {percent}%",
|
"percentOfSize": "{size} の {percent}%",
|
||||||
"pickFile": "ファイルを選択",
|
"permission": "権限",
|
||||||
"pingAvg": "平均:",
|
"pingAvg": "平均:",
|
||||||
"pingInputIP": "対象のIPまたはドメインを入力してください",
|
"pingInputIP": "対象のIPまたはドメインを入力してください",
|
||||||
"pingNoServer": "Pingに使用するサーバーがありません\nサーバータブでサーバーを追加してから再試行してください",
|
"pingNoServer": "Pingに使用するサーバーがありません\nサーバータブでサーバーを追加してから再試行してください",
|
||||||
"pkg": "パッケージ管理",
|
"pkg": "パッケージ管理",
|
||||||
"pkgUpgradeTip": "アップデートする前にシステムのバックアップを取ってください。",
|
|
||||||
"platformNotSupportUpdate": "現在のプラットフォームは更新をサポートしていません。最新のソースコードをコンパイルして手動でインストールしてください",
|
|
||||||
"plugInType": "挿入タイプ",
|
"plugInType": "挿入タイプ",
|
||||||
"plzEnterHost": "ホストを入力してください",
|
|
||||||
"plzSelectKey": "プライベートキーを選択してください",
|
|
||||||
"port": "ポート",
|
"port": "ポート",
|
||||||
"preview": "プレビュー",
|
"preview": "プレビュー",
|
||||||
"primaryColorSeed": "プライマリーカラーシード",
|
"privateKey": "秘密鍵",
|
||||||
"privateKey": "プライベートキー",
|
|
||||||
"process": "プロセス",
|
"process": "プロセス",
|
||||||
"pushToken": "プッシュトークン",
|
"pushToken": "プッシュトークン",
|
||||||
"pveIgnoreCertTip": "オプションを有効にすることは推奨されません、セキュリティリスクに注意してください!PVEのデフォルト証明書を使用している場合は、このオプションを有効にする必要があります。",
|
"pveIgnoreCertTip": "オプションを有効にすることは推奨されません、セキュリティリスクに注意してください!PVEのデフォルト証明書を使用している場合は、このオプションを有効にする必要があります。",
|
||||||
@@ -230,16 +132,11 @@
|
|||||||
"pwd": "パスワード",
|
"pwd": "パスワード",
|
||||||
"read": "読み取り",
|
"read": "読み取り",
|
||||||
"reboot": "再起動",
|
"reboot": "再起動",
|
||||||
"rememberChoice": "選択を記憶する",
|
|
||||||
"rememberPwdInMem": "メモリにパスワードを記憶する",
|
"rememberPwdInMem": "メモリにパスワードを記憶する",
|
||||||
"rememberPwdInMemTip": "コンテナ、一時停止などに使用されます。",
|
"rememberPwdInMemTip": "コンテナ、一時停止などに使用されます。",
|
||||||
"rememberWindowSize": "ウィンドウサイズを記憶する",
|
"rememberWindowSize": "ウィンドウサイズを記憶する",
|
||||||
"remotePath": "リモートパス",
|
"remotePath": "リモートパス",
|
||||||
"rename": "名前を変更",
|
|
||||||
"reportBugsOnGithubIssue": "{url}で問題を報告してください",
|
|
||||||
"restart": "再開",
|
"restart": "再開",
|
||||||
"restore": "復元",
|
|
||||||
"restoreSuccess": "復元に成功しました。変更を適用するためにアプリを再起動する必要があります",
|
|
||||||
"result": "結果",
|
"result": "結果",
|
||||||
"rotateAngel": "回転角度",
|
"rotateAngel": "回転角度",
|
||||||
"route": "ルーティング",
|
"route": "ルーティング",
|
||||||
@@ -254,14 +151,8 @@
|
|||||||
"serverDetailOrder": "詳細ページのウィジェット順序",
|
"serverDetailOrder": "詳細ページのウィジェット順序",
|
||||||
"serverFuncBtns": "サーバー機能ボタン",
|
"serverFuncBtns": "サーバー機能ボタン",
|
||||||
"serverOrder": "サーバー順序",
|
"serverOrder": "サーバー順序",
|
||||||
"serverTabConnecting": "接続中...",
|
|
||||||
"serverTabEmpty": "現在サーバーはありません。\n右下のボタンをクリックして追加してください。",
|
|
||||||
"serverTabFailed": "失敗",
|
|
||||||
"serverTabLoading": "読み込み中...",
|
|
||||||
"serverTabPlzSave": "このプライベートキーを再保存してください",
|
|
||||||
"serverTabUnkown": "不明な状態",
|
|
||||||
"setting": "設定",
|
|
||||||
"sftpDlPrepare": "サーバーへの接続を準備中...",
|
"sftpDlPrepare": "サーバーへの接続を準備中...",
|
||||||
|
"sftpEditorTip": "空の場合は、アプリ内蔵のファイルエディタを使用します。値がある場合は、リモートサーバーのエディタ(例:`vim`)を使用します(`EDITOR` に従って自動検出することをお勧めします)。",
|
||||||
"sftpRmrDirSummary": "SFTPで`rm -r`を使用してフォルダーを削除",
|
"sftpRmrDirSummary": "SFTPで`rm -r`を使用してフォルダーを削除",
|
||||||
"sftpSSHConnected": "SFTPに接続されました...",
|
"sftpSSHConnected": "SFTPに接続されました...",
|
||||||
"sftpShowFoldersFirst": "フォルダーを先に表示",
|
"sftpShowFoldersFirst": "フォルダーを先に表示",
|
||||||
@@ -270,17 +161,19 @@
|
|||||||
"size": "サイズ",
|
"size": "サイズ",
|
||||||
"snippet": "スニペット",
|
"snippet": "スニペット",
|
||||||
"softWrap": "ソフトラップ",
|
"softWrap": "ソフトラップ",
|
||||||
|
"specifyDev": "デバイスを指定",
|
||||||
|
"specifyDevTip": "例えば、ネットワークトラフィック統計はデフォルトですべてのデバイスに対するものです。ここで特定のデバイスを指定できます。",
|
||||||
"speed": "速度",
|
"speed": "速度",
|
||||||
"spentTime": "時間を費やしました: {time}",
|
"spentTime": "時間を費やしました: {time}",
|
||||||
"sshTermHelp": "ターミナルがスクロール可能な場合、横にドラッグするとテキストを選択できます。キーボードボタンをクリックするとキーボードのオン/オフが切り替わります。ファイルアイコンは現在のパスSFTPを開きます。クリップボードボタンは、テキストが選択されているときに内容をコピーし、テキストが選択されておらずクリップボードに内容がある場合には、その内容をターミナルに貼り付けます。コードアイコンは、コードスニペットをターミナルに貼り付けて実行します。",
|
"sshTermHelp": "ターミナルがスクロール可能な場合、横にドラッグするとテキストを選択できます。キーボードボタンをクリックするとキーボードのオン/オフが切り替わります。ファイルアイコンは現在のパスSFTPを開きます。クリップボードボタンは、テキストが選択されているときに内容をコピーし、テキストが選択されておらずクリップボードに内容がある場合には、その内容をターミナルに貼り付けます。コードアイコンは、コードスニペットをターミナルに貼り付けて実行します。",
|
||||||
"sshTip": "この機能は現在テスト段階にあります。\n\n問題がある場合は、{url}でフィードバックしてください。",
|
"sshTip": "この機能は現在テスト段階にあります。\n\n問題がある場合は、{url}でフィードバックしてください。",
|
||||||
"sshVirtualKeyAutoOff": "仮想キーの自動オフ",
|
"sshVirtualKeyAutoOff": "仮想キーの自動オフ",
|
||||||
"start": "開始",
|
"start": "開始",
|
||||||
|
"stat": "統計",
|
||||||
"stats": "統計",
|
"stats": "統計",
|
||||||
"stop": "停止",
|
"stop": "停止",
|
||||||
"stopped": "停止しました",
|
"stopped": "停止しました",
|
||||||
"storage": "ストレージ",
|
"storage": "ストレージ",
|
||||||
"success": "成功",
|
|
||||||
"supportFmtArgs": "以下のフォーマット引数がサポートされています:",
|
"supportFmtArgs": "以下のフォーマット引数がサポートされています:",
|
||||||
"suspend": "中断",
|
"suspend": "中断",
|
||||||
"suspendTip": "suspend機能はroot権限とsystemdのサポートが必要です。",
|
"suspendTip": "suspend機能はroot権限とsystemdのサポートが必要です。",
|
||||||
@@ -296,7 +189,6 @@
|
|||||||
"textScaler": "テキストスケーラー",
|
"textScaler": "テキストスケーラー",
|
||||||
"textScalerTip": "1.0 => 100%(デフォルトサイズ)。サーバーページの一部のテキストにのみ適用されます。変更をお勧めしません。",
|
"textScalerTip": "1.0 => 100%(デフォルトサイズ)。サーバーページの一部のテキストにのみ適用されます。変更をお勧めしません。",
|
||||||
"theme": "テーマ",
|
"theme": "テーマ",
|
||||||
"themeMode": "テーマモード",
|
|
||||||
"time": "時間",
|
"time": "時間",
|
||||||
"times": "回",
|
"times": "回",
|
||||||
"total": "合計",
|
"total": "合計",
|
||||||
@@ -304,27 +196,18 @@
|
|||||||
"trySudo": "sudoを試みる",
|
"trySudo": "sudoを試みる",
|
||||||
"ttl": "TTL",
|
"ttl": "TTL",
|
||||||
"unknown": "不明",
|
"unknown": "不明",
|
||||||
"unknownError": "未知のエラー",
|
|
||||||
"unkownConvertMode": "未知の変換モード",
|
"unkownConvertMode": "未知の変換モード",
|
||||||
"update": "更新",
|
"update": "更新",
|
||||||
"updateAll": "すべて更新",
|
|
||||||
"updateIntervalEqual0": "0に設定すると、サーバーの状態は自動的に更新されず、CPU使用率も計算できません。",
|
"updateIntervalEqual0": "0に設定すると、サーバーの状態は自動的に更新されず、CPU使用率も計算できません。",
|
||||||
"updateServerStatusInterval": "サーバー状態の更新間隔",
|
"updateServerStatusInterval": "サーバー状態の更新間隔",
|
||||||
"updateTip": "新バージョン: v1.0.{newest}",
|
|
||||||
"updateTipTooLow": "現在のバージョンが古すぎます。v1.0.{newest}にアップグレードしてください",
|
|
||||||
"upload": "アップロード",
|
"upload": "アップロード",
|
||||||
"upsideDown": "上下逆転",
|
"upsideDown": "上下逆転",
|
||||||
"uptime": "稼働時間",
|
"uptime": "稼働時間",
|
||||||
"urlOrJson": "URLまたはJSON",
|
|
||||||
"useCdn": "CDNの使用",
|
"useCdn": "CDNの使用",
|
||||||
"useCdnTip": "中国以外のユーザーにはCDNの使用が推奨されています。ご利用しますか?",
|
"useCdnTip": "中国以外のユーザーにはCDNの使用が推奨されています。ご利用しますか?",
|
||||||
"useNoPwd": "パスワードなしで使用します",
|
"useNoPwd": "パスワードなしで使用します",
|
||||||
"usePodmanByDefault": "デフォルトでPodmanを使用",
|
"usePodmanByDefault": "デフォルトでPodmanを使用",
|
||||||
"used": "使用済み",
|
"used": "使用済み",
|
||||||
"user": "ユーザー",
|
|
||||||
"versionHaveUpdate": "新しいバージョンが見つかりました:v1.0.{build}、クリックして更新",
|
|
||||||
"versionUnknownUpdate": "現在:v1.0.{build}、更新をチェックするためにクリック",
|
|
||||||
"versionUpdated": "現在:v1.0.{build}、最新バージョンです",
|
|
||||||
"view": "ビュー",
|
"view": "ビュー",
|
||||||
"viewErr": "エラーを表示",
|
"viewErr": "エラーを表示",
|
||||||
"virtKeyHelpClipboard": "端末に選択された文字がある場合は、選択された文字をクリップボードにコピーします。そうでない場合は、クリップボードの内容を端末に貼り付けます。",
|
"virtKeyHelpClipboard": "端末に選択された文字がある場合は、選択された文字をクリップボードにコピーします。そうでない場合は、クリップボードの内容を端末に貼り付けます。",
|
||||||
@@ -335,8 +218,8 @@
|
|||||||
"watchNotPaired": "ペアリングされたApple Watchがありません",
|
"watchNotPaired": "ペアリングされたApple Watchがありません",
|
||||||
"webdavSettingEmpty": "Webdavの設定が空です",
|
"webdavSettingEmpty": "Webdavの設定が空です",
|
||||||
"whenOpenApp": "アプリを開くとき",
|
"whenOpenApp": "アプリを開くとき",
|
||||||
"willTakEeffectImmediately": "変更は即座に有効になります",
|
|
||||||
"wolTip": "WOL(Wake-on-LAN)を設定した後、サーバーに接続するたびにWOLリクエストが送信されます。",
|
"wolTip": "WOL(Wake-on-LAN)を設定した後、サーバーに接続するたびにWOLリクエストが送信されます。",
|
||||||
"write": "書き込み",
|
"write": "書き込み",
|
||||||
"writeScriptFailTip": "スクリプトの書き込みに失敗しました。権限がないかディレクトリが存在しない可能性があります。"
|
"writeScriptFailTip": "スクリプトの書き込みに失敗しました。権限がないかディレクトリが存在しない可能性があります。",
|
||||||
|
"writeScriptTip": "サーバーに接続すると、システムの状態を監視するためのスクリプトが ~/.config/server_box に書き込まれます。スクリプトの内容を確認できます。"
|
||||||
}
|
}
|
||||||
@@ -1,87 +1,42 @@
|
|||||||
{
|
{
|
||||||
"@@locale": "nl",
|
"@@locale": "nl",
|
||||||
"about": "Over",
|
|
||||||
"aboutThanks": "Met dank aan de volgende mensen die hebben deelgenomen aan.",
|
"aboutThanks": "Met dank aan de volgende mensen die hebben deelgenomen aan.",
|
||||||
"acceptBeta": "Accepteer testversie-updates",
|
"acceptBeta": "Accepteer testversie-updates",
|
||||||
"add": "Toevoegen",
|
|
||||||
"addAServer": "een server toevoegen",
|
|
||||||
"addPrivateKey": "Privésleutel toevoegen",
|
|
||||||
"addSystemPrivateKeyTip": "Er is momenteel geen privésleutel, wilt u degene toevoegen die bij het systeem wordt geleverd (~/.ssh/id_rsa)?",
|
"addSystemPrivateKeyTip": "Er is momenteel geen privésleutel, wilt u degene toevoegen die bij het systeem wordt geleverd (~/.ssh/id_rsa)?",
|
||||||
"added2List": "Toegevoegd aan takenlijst",
|
"added2List": "Toegevoegd aan takenlijst",
|
||||||
"addr": "Adres",
|
"addr": "Adres",
|
||||||
"all": "Alle",
|
|
||||||
"alreadyLastDir": "Al in de laatst gebruikte map.",
|
"alreadyLastDir": "Al in de laatst gebruikte map.",
|
||||||
"alterUrl": "Url wijzigen",
|
|
||||||
"askContinue": "{msg}. Doorgaan?",
|
|
||||||
"attention": "Let op",
|
|
||||||
"authFailTip": "Authenticatie mislukt, controleer of het wachtwoord/sleutel/host/gebruiker, enz., incorrect zijn.",
|
"authFailTip": "Authenticatie mislukt, controleer of het wachtwoord/sleutel/host/gebruiker, enz., incorrect zijn.",
|
||||||
"authRequired": "Authenticatie vereist",
|
|
||||||
"auto": "Auto",
|
|
||||||
"autoBackupConflict": "Er kan slechts één automatische back-up tegelijk worden ingeschakeld.",
|
"autoBackupConflict": "Er kan slechts één automatische back-up tegelijk worden ingeschakeld.",
|
||||||
"autoCheckUpdate": "Automatisch controleren op update",
|
|
||||||
"autoConnect": "Automatisch verbinden",
|
"autoConnect": "Automatisch verbinden",
|
||||||
"autoRun": "Automatisch uitvoeren",
|
"autoRun": "Automatisch uitvoeren",
|
||||||
"autoUpdateHomeWidget": "Automatische update van home-widget",
|
"autoUpdateHomeWidget": "Automatische update van home-widget",
|
||||||
"backup": "Back-up",
|
|
||||||
"backupTip": "De geëxporteerde gegevens zijn simpelweg versleuteld. \nBewaar deze aub veilig.",
|
"backupTip": "De geëxporteerde gegevens zijn simpelweg versleuteld. \nBewaar deze aub veilig.",
|
||||||
"backupVersionNotMatch": "Back-upversie komt niet overeen.",
|
"backupVersionNotMatch": "Back-upversie komt niet overeen.",
|
||||||
"battery": "Batterij",
|
"battery": "Batterij",
|
||||||
"beforeConnect": "ServerBox zal na verbinding een script schrijven en uitvoeren in `~/.config/server_box`. Voor meer technische details, bezoek [Github]({url}).",
|
|
||||||
"bgRun": "Uitvoeren op de achtergrond",
|
"bgRun": "Uitvoeren op de achtergrond",
|
||||||
"bgRunTip": "Deze schakelaar betekent alleen dat het programma zal proberen op de achtergrond uit te voeren, of het in de achtergrond kan worden uitgevoerd, hangt af van of de toestemming is ingeschakeld of niet. Voor native Android, schakel \"Batterijoptimalisatie\" uit in deze app, en voor miui, wijzig de energiebesparingsbeleid naar \"Onbeperkt\".",
|
"bgRunTip": "Deze schakelaar betekent alleen dat het programma zal proberen op de achtergrond uit te voeren, of het in de achtergrond kan worden uitgevoerd, hangt af van of de toestemming is ingeschakeld of niet. Voor native Android, schakel \"Batterijoptimalisatie\" uit in deze app, en voor miui, wijzig de energiebesparingsbeleid naar \"Onbeperkt\".",
|
||||||
"bioAuth": "Biometrische authenticatie",
|
|
||||||
"browser": "Browser",
|
|
||||||
"bulkImportServers": "Servers batchgewijs importeren",
|
|
||||||
"bulkImportServersTip": "U kunt het [formaat]({url}) hier vinden.",
|
|
||||||
"canPullRefresh": "Je kunt verversen door te slepen.",
|
|
||||||
"cancel": "Annuleren",
|
|
||||||
"choose": "Kiezen",
|
|
||||||
"chooseFontFile": "Kies een font bestand",
|
|
||||||
"choosePrivateKey": "Privésleutel kiezen",
|
|
||||||
"clear": "Wissen",
|
|
||||||
"clipboard": "Klembord",
|
|
||||||
"close": "Sluiten",
|
|
||||||
"cmd": "Opdracht",
|
"cmd": "Opdracht",
|
||||||
"cnKeyboardComp": "Compatibiliteit met Chinese Android",
|
|
||||||
"cnKeyboardCompTip": "Als de terminal een beveiligd toetsenbord weergeeft, kunt u dit inschakelen.",
|
|
||||||
"collapseUI": "Inklappen",
|
|
||||||
"collapseUITip": "Of lange lijsten in de UI standaard moeten worden ingeklapt",
|
"collapseUITip": "Of lange lijsten in de UI standaard moeten worden ingeklapt",
|
||||||
"conn": "Verbinding",
|
"conn": "Verbinding",
|
||||||
"connected": "Verbonden",
|
|
||||||
"container": "Container",
|
"container": "Container",
|
||||||
"containerName": "Container naam",
|
|
||||||
"containerStatus": "Containerstatus",
|
|
||||||
"containerTrySudoTip": "Bijvoorbeeld: in de app is de gebruiker ingesteld op aaa, maar Docker is geïnstalleerd onder de rootgebruiker. In dit geval moet u deze optie inschakelen.",
|
"containerTrySudoTip": "Bijvoorbeeld: in de app is de gebruiker ingesteld op aaa, maar Docker is geïnstalleerd onder de rootgebruiker. In dit geval moet u deze optie inschakelen.",
|
||||||
"content": "Inhoud",
|
|
||||||
"convert": "Converteren",
|
"convert": "Converteren",
|
||||||
"copy": "Kopiëren",
|
|
||||||
"copyPath": "Pad kopiëren",
|
"copyPath": "Pad kopiëren",
|
||||||
"cpuViewAsProgressTip": "Toon het gebruik van elke CPU in een voortgangsbalkstijl (oude stijl)",
|
"cpuViewAsProgressTip": "Toon het gebruik van elke CPU in een voortgangsbalkstijl (oude stijl)",
|
||||||
"createFile": "Bestand aanmaken",
|
|
||||||
"createFolder": "Map aanmaken",
|
|
||||||
"cursorType": "Cursortype",
|
"cursorType": "Cursortype",
|
||||||
"customCmd": "Aangepaste opdrachten",
|
"customCmd": "Aangepaste opdrachten",
|
||||||
"customCmdDocUrl": "https://github.com/lollipopkit/flutter_server_box/wiki#custom-commands",
|
"customCmdDocUrl": "https://github.com/lollipopkit/flutter_server_box/wiki#custom-commands",
|
||||||
"customCmdHint": "\"Opdrachtnaam\": \"Opdracht\"",
|
"customCmdHint": "\"Opdrachtnaam\": \"Opdracht\"",
|
||||||
"dark": "Donker",
|
|
||||||
"day": "Dag",
|
|
||||||
"debug": "Debuggen",
|
|
||||||
"decode": "Decoderen",
|
"decode": "Decoderen",
|
||||||
"decompress": "Decomprimeren",
|
"decompress": "Decomprimeren",
|
||||||
"delete": "Verwijderen",
|
|
||||||
"deleteScripts": "Verwijder tegelijkertijd serverscripts",
|
|
||||||
"deleteServers": "Servers batchgewijs verwijderen",
|
"deleteServers": "Servers batchgewijs verwijderen",
|
||||||
"deviceName": "Apparaatnaam",
|
|
||||||
"dirEmpty": "Zorg ervoor dat de map leeg is.",
|
"dirEmpty": "Zorg ervoor dat de map leeg is.",
|
||||||
"disabled": "Uitgeschakeld",
|
|
||||||
"disconnected": "Verbroken",
|
"disconnected": "Verbroken",
|
||||||
"disk": "Schijf",
|
"disk": "Schijf",
|
||||||
"diskIgnorePath": "Pad negeren voor schijf",
|
"diskIgnorePath": "Pad negeren voor schijf",
|
||||||
"displayCpuIndex": "Toon de CPU-index",
|
"displayCpuIndex": "Toon de CPU-index",
|
||||||
"displayName": "Weergavenaam",
|
|
||||||
"dl2Local": "Download {fileName} naar lokaal?",
|
"dl2Local": "Download {fileName} naar lokaal?",
|
||||||
"doc": "Documentatie",
|
|
||||||
"dockerEditHost": "DOCKER_HOST bewerken",
|
|
||||||
"dockerEmptyRunningItems": "Er zijn geen actieve containers.\nDit kan komen doordat:\n- De Docker-installatiegebruiker niet overeenkomt met de gebruikersnaam die is geconfigureerd binnen de app.\n- De omgevingsvariabele DOCKER_HOST is niet correct gelezen. U kunt deze krijgen door `echo $DOCKER_HOST` in de terminal uit te voeren.",
|
"dockerEmptyRunningItems": "Er zijn geen actieve containers.\nDit kan komen doordat:\n- De Docker-installatiegebruiker niet overeenkomt met de gebruikersnaam die is geconfigureerd binnen de app.\n- De omgevingsvariabele DOCKER_HOST is niet correct gelezen. U kunt deze krijgen door `echo $DOCKER_HOST` in de terminal uit te voeren.",
|
||||||
"dockerImagesFmt": "{count} afbeeldingen",
|
"dockerImagesFmt": "{count} afbeeldingen",
|
||||||
"dockerNotInstalled": "Docker niet geïnstalleerd",
|
"dockerNotInstalled": "Docker niet geïnstalleerd",
|
||||||
@@ -89,77 +44,48 @@
|
|||||||
"dockerStatusRunningFmt": "{count} container actief.",
|
"dockerStatusRunningFmt": "{count} container actief.",
|
||||||
"doubleColumnMode": "Dubbele kolommodus",
|
"doubleColumnMode": "Dubbele kolommodus",
|
||||||
"doubleColumnTip": "Deze optie schakelt alleen de functie in, of deze daadwerkelijk kan worden ingeschakeld, hangt af van de breedte van het apparaat",
|
"doubleColumnTip": "Deze optie schakelt alleen de functie in, of deze daadwerkelijk kan worden ingeschakeld, hangt af van de breedte van het apparaat",
|
||||||
"download": "Downloaden",
|
|
||||||
"edit": "Bewerken",
|
|
||||||
"editVirtKeys": "Virtuele toetsen bewerken",
|
"editVirtKeys": "Virtuele toetsen bewerken",
|
||||||
"editor": "Editor",
|
"editor": "Editor",
|
||||||
"editorHighlightTip": "De huidige codehighlighting-prestaties zijn slechter en kunnen optioneel worden uitgeschakeld om te verbeteren.",
|
"editorHighlightTip": "De huidige codehighlighting-prestaties zijn slechter en kunnen optioneel worden uitgeschakeld om te verbeteren.",
|
||||||
"encode": "Coderen",
|
"encode": "Coderen",
|
||||||
"error": "Fout",
|
"envVars": "Omgevingsvariabele",
|
||||||
"exampleName": "Voorbeeldnaam",
|
|
||||||
"experimentalFeature": "Experimentele functie",
|
"experimentalFeature": "Experimentele functie",
|
||||||
"export": "Exporteren",
|
|
||||||
"extraArgs": "Extra argumenten",
|
"extraArgs": "Extra argumenten",
|
||||||
"failed": "Mislukt",
|
"fallbackSshDest": "Fallback SSH-bestemming",
|
||||||
"fdroidReleaseTip": "Als u deze app van Fdroid heeft gedownload, wordt aanbevolen deze optie uit te schakelen.",
|
"fdroidReleaseTip": "Als u deze app van F-Droid heeft gedownload, wordt aanbevolen deze optie uit te schakelen.",
|
||||||
"feedback": "Feedback",
|
|
||||||
"feedbackOnGithub": "Als je vragen hebt, geef dan feedback op Github.",
|
|
||||||
"fieldMustNotEmpty": "Deze velden mogen niet leeg zijn.",
|
|
||||||
"fileNotExist": "{file} bestaat niet",
|
|
||||||
"fileTooLarge": "Bestand '{file}' te groot {size}, max {sizeMax}",
|
"fileTooLarge": "Bestand '{file}' te groot {size}, max {sizeMax}",
|
||||||
"files": "Bestanden",
|
|
||||||
"finished": "Voltooid",
|
|
||||||
"followSystem": "Volg systeem",
|
"followSystem": "Volg systeem",
|
||||||
"font": "Lettertype",
|
"font": "Lettertype",
|
||||||
"fontSize": "Lettergrootte",
|
"fontSize": "Lettergrootte",
|
||||||
"forExample": "Bijvoorbeeld",
|
|
||||||
"force": "Forceer",
|
"force": "Forceer",
|
||||||
"foundNUpdate": "{count} update gevonden",
|
|
||||||
"fullScreen": "Volledig schermmodus",
|
"fullScreen": "Volledig schermmodus",
|
||||||
"fullScreenJitter": "Volledig scherm trilling",
|
"fullScreenJitter": "Volledig scherm trilling",
|
||||||
"fullScreenJitterHelp": "Om inbranden van het scherm te voorkomen",
|
"fullScreenJitterHelp": "Om inbranden van het scherm te voorkomen",
|
||||||
"fullScreenTip": "Moet de volledig schermmodus worden ingeschakeld wanneer het apparaat naar de liggende modus wordt gedraaid? Deze optie is alleen van toepassing op het servertabblad.",
|
"fullScreenTip": "Moet de volledig schermmodus worden ingeschakeld wanneer het apparaat naar de liggende modus wordt gedraaid? Deze optie is alleen van toepassing op het servertabblad.",
|
||||||
"getPushTokenFailed": "Kan push-token niet ophalen",
|
|
||||||
"gettingToken": "Token ophalen...",
|
|
||||||
"goBackQ": "Terug gaan?",
|
"goBackQ": "Terug gaan?",
|
||||||
"goto": "Ga naar",
|
"goto": "Ga naar",
|
||||||
"hideTitleBar": "Titelbalk verbergen",
|
"hideTitleBar": "Titelbalk verbergen",
|
||||||
"hideTitleBarTip": "Houd na het inschakelen de drie knoppen in de rechterbovenhoek ingedrukt om te slepen.",
|
|
||||||
"highlight": "Code-highlight",
|
"highlight": "Code-highlight",
|
||||||
"homeWidgetUrlConfig": "Home-widget-url configureren",
|
"homeWidgetUrlConfig": "Home-widget-url configureren",
|
||||||
"host": "Host",
|
"host": "Host",
|
||||||
"hour": "Uur",
|
|
||||||
"httpFailedWithCode": "verzoek mislukt, statuscode: {code}",
|
"httpFailedWithCode": "verzoek mislukt, statuscode: {code}",
|
||||||
"icloudSynced": "iCloud wordt gesynchroniseerd en sommige instellingen vereisen mogelijk een herstart van de app om van kracht te worden.",
|
|
||||||
"ignoreCert": "Certificaat negeren",
|
"ignoreCert": "Certificaat negeren",
|
||||||
"image": "Afbeelding",
|
"image": "Afbeelding",
|
||||||
"imagesList": "Lijst met afbeeldingen",
|
"imagesList": "Lijst met afbeeldingen",
|
||||||
"import": "Importeren",
|
|
||||||
"inAppUpdate": "Bijwerken binnen de app? Anders downloaden via een browser.",
|
|
||||||
"init": "Initialiseren",
|
"init": "Initialiseren",
|
||||||
"inner": "Intern",
|
"inner": "Intern",
|
||||||
"inputDomainHere": "Voer hier domein in",
|
|
||||||
"install": "Installeren",
|
"install": "Installeren",
|
||||||
"installDockerWithUrl": "Installeer eerst docker via https://docs.docker.com/engine/install.",
|
"installDockerWithUrl": "Installeer eerst docker via https://docs.docker.com/engine/install.",
|
||||||
"invalid": "Ongeldig",
|
"invalid": "Ongeldig",
|
||||||
"invalidJson": "Ongeldige JSON",
|
|
||||||
"invalidVersion": "Ongeldige versie",
|
|
||||||
"invalidVersionHelp": "Zorg ervoor dat docker correct is geïnstalleerd, of dat u een niet-zelf-gecompileerde versie gebruikt. Als dat niet het geval is, meld dan een probleem op {url}.",
|
|
||||||
"isBusy": "Is momenteel bezig",
|
|
||||||
"jumpServer": "Spring naar server",
|
"jumpServer": "Spring naar server",
|
||||||
"keepForeground": "Houd de app op de voorgrond!",
|
"keepForeground": "Houd de app op de voorgrond!",
|
||||||
"keepStatusWhenErr": "Behoud de laatste serverstatus",
|
"keepStatusWhenErr": "Behoud de laatste serverstatus",
|
||||||
"keepStatusWhenErrTip": "Alleen in geval van een fout tijdens de scriptuitvoering",
|
"keepStatusWhenErrTip": "Alleen in geval van een fout tijdens de scriptuitvoering",
|
||||||
"keyAuth": "Sleutelauthenticatie",
|
"keyAuth": "Sleutelauthenticatie",
|
||||||
"language": "Taal",
|
"letterCache": "Lettercaching",
|
||||||
"languageName": "Nederlands",
|
"letterCacheTip": "Aanbevolen om uit te schakelen, maar na het uitschakelen is het niet mogelijk om CJK-tekens in te voeren.",
|
||||||
"lastTry": "Laatste poging",
|
|
||||||
"launchPage": "Startpagina",
|
|
||||||
"license": "Licentie",
|
"license": "Licentie",
|
||||||
"light": "Licht",
|
|
||||||
"loadingFiles": "Bestanden laden...",
|
|
||||||
"location": "Locatie",
|
"location": "Locatie",
|
||||||
"log": "Logboek",
|
|
||||||
"loss": "verlies",
|
"loss": "verlies",
|
||||||
"madeWithLove": "Gemaakt met ❤️ door {myGithub}",
|
"madeWithLove": "Gemaakt met ❤️ door {myGithub}",
|
||||||
"manual": "Handleiding",
|
"manual": "Handleiding",
|
||||||
@@ -167,60 +93,36 @@
|
|||||||
"maxRetryCount": "Aantal serverherverbindingen",
|
"maxRetryCount": "Aantal serverherverbindingen",
|
||||||
"maxRetryCountEqual0": "Zal opnieuw blijven proberen.",
|
"maxRetryCountEqual0": "Zal opnieuw blijven proberen.",
|
||||||
"min": "min",
|
"min": "min",
|
||||||
"minute": "Minuut",
|
|
||||||
"mission": "Missie",
|
"mission": "Missie",
|
||||||
"more": "Meer",
|
"more": "Meer",
|
||||||
"moveOutServerFuncBtnsHelp": "Aan: kan worden weergegeven onder elke kaart op de Server-tabbladpagina. Uit: kan worden weergegeven bovenaan de Serverdetails-pagina.",
|
"moveOutServerFuncBtnsHelp": "Aan: kan worden weergegeven onder elke kaart op de Server-tabbladpagina. Uit: kan worden weergegeven bovenaan de Serverdetails-pagina.",
|
||||||
"ms": "ms",
|
"ms": "ms",
|
||||||
"name": "Naam",
|
|
||||||
"needHomeDir": "Als u een Synology-gebruiker bent, [zie hier](https://kb.synology.com/DSM/tutorial/user_enable_home_service). Gebruikers van andere systemen moeten zoeken hoe ze een home directory kunnen creëren.",
|
"needHomeDir": "Als u een Synology-gebruiker bent, [zie hier](https://kb.synology.com/DSM/tutorial/user_enable_home_service). Gebruikers van andere systemen moeten zoeken hoe ze een home directory kunnen creëren.",
|
||||||
"needRestart": "App moet opnieuw worden gestart",
|
"needRestart": "App moet opnieuw worden gestart",
|
||||||
"net": "Netwerk",
|
"net": "Netwerk",
|
||||||
"netViewType": "Netweergavetype",
|
"netViewType": "Netweergavetype",
|
||||||
"newContainer": "Nieuwe container",
|
"newContainer": "Nieuwe container",
|
||||||
"noClient": "Geen client",
|
|
||||||
"noInterface": "Geen interface",
|
|
||||||
"noLineChart": "lijndiagrammen gebruiken",
|
"noLineChart": "lijndiagrammen gebruiken",
|
||||||
"noNotiPerm": "Geen meldingsmachtigingen, mogelijk geen voortgangsindicatie bij het downloaden van app-updates.",
|
"noLineChartForCpu": "Gebruik geen lijndiagrammen voor CPU",
|
||||||
"noOptions": "Geen opties",
|
|
||||||
"noPrivateKeyTip": "De privésleutel bestaat niet, deze is mogelijk verwijderd of er is een configuratiefout.",
|
"noPrivateKeyTip": "De privésleutel bestaat niet, deze is mogelijk verwijderd of er is een configuratiefout.",
|
||||||
"noPromptAgain": "Niet meer vragen",
|
"noPromptAgain": "Niet meer vragen",
|
||||||
"noResult": "Geen resultaat",
|
|
||||||
"noSavedPrivateKey": "Geen opgeslagen privésleutels.",
|
|
||||||
"noSavedSnippet": "Geen opgeslagen fragmenten.",
|
|
||||||
"noServerAvailable": "Geen server beschikbaar.",
|
|
||||||
"noTask": "Geen taak",
|
|
||||||
"noUpdateAvailable": "Geen update beschikbaar",
|
|
||||||
"node": "Node",
|
"node": "Node",
|
||||||
"notAvailable": "Niet beschikbaar",
|
"notAvailable": "Niet beschikbaar",
|
||||||
"notSelected": "Niet geselecteerd",
|
|
||||||
"note": "Opmerking",
|
|
||||||
"nullToken": "Null-token",
|
|
||||||
"ok": "OK",
|
|
||||||
"onServerDetailPage": "Op serverdetailspagina",
|
"onServerDetailPage": "Op serverdetailspagina",
|
||||||
"onlyOneLine": "Alleen als één regel weergeven (scrollbaar)",
|
"onlyOneLine": "Alleen als één regel weergeven (scrollbaar)",
|
||||||
"onlyWhenCoreBiggerThan8": "Alleen effectief wanneer het aantal cores > 8",
|
"onlyWhenCoreBiggerThan8": "Alleen effectief wanneer het aantal cores > 8",
|
||||||
"open": "Openen",
|
|
||||||
"openLastPath": "Open het laatste pad",
|
"openLastPath": "Open het laatste pad",
|
||||||
"openLastPathTip": "Verschillende servers hebben verschillende logs, en de log is het pad naar de uitgang",
|
"openLastPathTip": "Verschillende servers hebben verschillende logs, en de log is het pad naar de uitgang",
|
||||||
"parseContainerStats": "Parseer de containerbezettingstatus",
|
|
||||||
"parseContainerStatsTip": "Het parsen van de bezettingsstatus van Docker is relatief langzaam.",
|
"parseContainerStatsTip": "Het parsen van de bezettingsstatus van Docker is relatief langzaam.",
|
||||||
"paste": "Plakken",
|
|
||||||
"path": "Pad",
|
|
||||||
"percentOfSize": "{percent}% van {size}",
|
"percentOfSize": "{percent}% van {size}",
|
||||||
"pickFile": "Bestand kiezen",
|
"permission": "Machtigingen",
|
||||||
"pingAvg": "Gem:",
|
"pingAvg": "Gem:",
|
||||||
"pingInputIP": "Voer een doel-IP / domein in.",
|
"pingInputIP": "Voer een doel-IP / domein in.",
|
||||||
"pingNoServer": "Geen server om te pingen.\nVoeg een server toe in het servertabblad.",
|
"pingNoServer": "Geen server om te pingen.\nVoeg een server toe in het servertabblad.",
|
||||||
"pkg": "Pkg",
|
"pkg": "Pkg",
|
||||||
"pkgUpgradeTip": "Maak een back-up van uw systeem voordat u een update uitvoert.",
|
|
||||||
"platformNotSupportUpdate": "Huidig platform ondersteunt geen updates in de app.\nBouw het alstublieft uit bron en installeer het.",
|
|
||||||
"plugInType": "Invoegingstype",
|
"plugInType": "Invoegingstype",
|
||||||
"plzEnterHost": "Voer host in a.u.b.",
|
|
||||||
"plzSelectKey": "Selecteer a.u.b. een sleutel.",
|
|
||||||
"port": "Poort",
|
"port": "Poort",
|
||||||
"preview": "Voorbeeld",
|
"preview": "Voorbeeld",
|
||||||
"primaryColorSeed": "Basis kleurzaad",
|
|
||||||
"privateKey": "Privésleutel",
|
"privateKey": "Privésleutel",
|
||||||
"process": "Proces",
|
"process": "Proces",
|
||||||
"pushToken": "Push-token",
|
"pushToken": "Push-token",
|
||||||
@@ -230,16 +132,11 @@
|
|||||||
"pwd": "Wachtwoord",
|
"pwd": "Wachtwoord",
|
||||||
"read": "Lezen",
|
"read": "Lezen",
|
||||||
"reboot": "Herstart",
|
"reboot": "Herstart",
|
||||||
"rememberChoice": "Selectie onthouden",
|
|
||||||
"rememberPwdInMem": "Wachtwoord onthouden in geheugen",
|
"rememberPwdInMem": "Wachtwoord onthouden in geheugen",
|
||||||
"rememberPwdInMemTip": "Gebruikt voor containers, opschorting, enz.",
|
"rememberPwdInMemTip": "Gebruikt voor containers, opschorting, enz.",
|
||||||
"rememberWindowSize": "Venstergrootte onthouden",
|
"rememberWindowSize": "Venstergrootte onthouden",
|
||||||
"remotePath": "Extern pad",
|
"remotePath": "Extern pad",
|
||||||
"rename": "Hernoemen",
|
|
||||||
"reportBugsOnGithubIssue": "Meld alstublieft bugs op {url}",
|
|
||||||
"restart": "Herstarten",
|
"restart": "Herstarten",
|
||||||
"restore": "Herstellen",
|
|
||||||
"restoreSuccess": "Herstellen geslaagd. App herstarten om toe te passen.",
|
|
||||||
"result": "Resultaat",
|
"result": "Resultaat",
|
||||||
"rotateAngel": "Rotatiehoek",
|
"rotateAngel": "Rotatiehoek",
|
||||||
"route": "Route",
|
"route": "Route",
|
||||||
@@ -254,14 +151,8 @@
|
|||||||
"serverDetailOrder": "Volgorde van widget op detailpagina",
|
"serverDetailOrder": "Volgorde van widget op detailpagina",
|
||||||
"serverFuncBtns": "Server functieknoppen",
|
"serverFuncBtns": "Server functieknoppen",
|
||||||
"serverOrder": "Servervolgorde",
|
"serverOrder": "Servervolgorde",
|
||||||
"serverTabConnecting": "Verbinding maken...",
|
|
||||||
"serverTabEmpty": "Er is geen server.\nKlik op fab om er een toe te voegen.",
|
|
||||||
"serverTabFailed": "Mislukt",
|
|
||||||
"serverTabLoading": "Laden...",
|
|
||||||
"serverTabPlzSave": "Sla deze privésleutel opnieuw op.",
|
|
||||||
"serverTabUnkown": "Onbekende status",
|
|
||||||
"setting": "Instellingen",
|
|
||||||
"sftpDlPrepare": "Voorbereiden om verbinding te maken...",
|
"sftpDlPrepare": "Voorbereiden om verbinding te maken...",
|
||||||
|
"sftpEditorTip": "Indien leeg, gebruik de ingebouwde bestandseditor van de app. Indien een waarde aanwezig is, gebruik de editor van de externe server, bijvoorbeeld `vim` (aanbevolen om automatisch te detecteren volgens `EDITOR`).",
|
||||||
"sftpRmrDirSummary": "Gebruik `rm -r` om een map te verwijderen in SFTP.",
|
"sftpRmrDirSummary": "Gebruik `rm -r` om een map te verwijderen in SFTP.",
|
||||||
"sftpSSHConnected": "SFTP Verbonden",
|
"sftpSSHConnected": "SFTP Verbonden",
|
||||||
"sftpShowFoldersFirst": "Mappen eerst weergeven",
|
"sftpShowFoldersFirst": "Mappen eerst weergeven",
|
||||||
@@ -270,17 +161,19 @@
|
|||||||
"size": "Grootte",
|
"size": "Grootte",
|
||||||
"snippet": "Fragment",
|
"snippet": "Fragment",
|
||||||
"softWrap": "Zachte wrap",
|
"softWrap": "Zachte wrap",
|
||||||
|
"specifyDev": "Apparaat specificeren",
|
||||||
|
"specifyDevTip": "Bijvoorbeeld, netwerkverkeersstatistieken zijn standaard voor alle apparaten. Hier kunt u een specifiek apparaat opgeven.",
|
||||||
"speed": "Snelheid",
|
"speed": "Snelheid",
|
||||||
"spentTime": "Gebruikte tijd: {time}",
|
"spentTime": "Gebruikte tijd: {time}",
|
||||||
"sshTermHelp": "Wanneer het terminal scrollbaar is, kan horizontaal slepen tekst selecteren. Klikken op de toetsenbordknop schakelt het toetsenbord aan/uit. Het bestandsicoon opent de huidige pad SFTP. De klembordknop kopieert de inhoud wanneer tekst is geselecteerd en plakt inhoud van het klembord in de terminal wanneer geen tekst is geselecteerd en er inhoud op het klembord staat. Het code-icoon plakt codefragmenten in de terminal en voert ze uit.",
|
"sshTermHelp": "Wanneer het terminal scrollbaar is, kan horizontaal slepen tekst selecteren. Klikken op de toetsenbordknop schakelt het toetsenbord aan/uit. Het bestandsicoon opent de huidige pad SFTP. De klembordknop kopieert de inhoud wanneer tekst is geselecteerd en plakt inhoud van het klembord in de terminal wanneer geen tekst is geselecteerd en er inhoud op het klembord staat. Het code-icoon plakt codefragmenten in de terminal en voert ze uit.",
|
||||||
"sshTip": "Deze functie bevindt zich momenteel in de experimentele fase.\n\nMeld alstublieft bugs op {url} of sluit je aan bij onze ontwikkeling.",
|
"sshTip": "Deze functie bevindt zich momenteel in de experimentele fase.\n\nMeld alstublieft bugs op {url} of sluit je aan bij onze ontwikkeling.",
|
||||||
"sshVirtualKeyAutoOff": "Automatisch schakelen van virtuele toetsen",
|
"sshVirtualKeyAutoOff": "Automatisch schakelen van virtuele toetsen",
|
||||||
"start": "Starten",
|
"start": "Starten",
|
||||||
|
"stat": "Statistieken",
|
||||||
"stats": "Statistieken",
|
"stats": "Statistieken",
|
||||||
"stop": "Stoppen",
|
"stop": "Stoppen",
|
||||||
"stopped": "Gestopt",
|
"stopped": "Gestopt",
|
||||||
"storage": "Opslag",
|
"storage": "Opslag",
|
||||||
"success": "Succes",
|
|
||||||
"supportFmtArgs": "De volgende opmaakparameters worden ondersteund:",
|
"supportFmtArgs": "De volgende opmaakparameters worden ondersteund:",
|
||||||
"suspend": "Ophangen",
|
"suspend": "Ophangen",
|
||||||
"suspendTip": "De opschortfunctie vereist rootrechten en systemd-ondersteuning.",
|
"suspendTip": "De opschortfunctie vereist rootrechten en systemd-ondersteuning.",
|
||||||
@@ -296,35 +189,25 @@
|
|||||||
"textScaler": "Tekstschaler",
|
"textScaler": "Tekstschaler",
|
||||||
"textScalerTip": "1.0 => 100% (oorspronkelijke grootte), werkt alleen op het gedeelte van de serverpagina van het lettertype, niet aanbevolen om te wijzigen.",
|
"textScalerTip": "1.0 => 100% (oorspronkelijke grootte), werkt alleen op het gedeelte van de serverpagina van het lettertype, niet aanbevolen om te wijzigen.",
|
||||||
"theme": "Thema",
|
"theme": "Thema",
|
||||||
"themeMode": "Themamodus",
|
|
||||||
"time": "Tijd",
|
"time": "Tijd",
|
||||||
"times": "Keer",
|
"times": "Keer",
|
||||||
"total": "Totaal",
|
"total": "Totaal",
|
||||||
"traffic": "Verkeer",
|
"traffic": "Verkeer",
|
||||||
"trySudo": "Probeer sudo te gebruiken",
|
"trySudo": "Probeer sudo te gebruiken",
|
||||||
"ttl": "ttl",
|
"ttl": "TTL",
|
||||||
"unknown": "Onbekend",
|
"unknown": "Onbekend",
|
||||||
"unknownError": "Onbekende fout",
|
|
||||||
"unkownConvertMode": "Onbekende conversiemodus",
|
"unkownConvertMode": "Onbekende conversiemodus",
|
||||||
"update": "Bijwerken",
|
"update": "Bijwerken",
|
||||||
"updateAll": "Alles bijwerken",
|
|
||||||
"updateIntervalEqual0": "Het staat op 0, het zal niet automatisch bijwerken\nCPU status kan niet berekend worden.",
|
"updateIntervalEqual0": "Het staat op 0, het zal niet automatisch bijwerken\nCPU status kan niet berekend worden.",
|
||||||
"updateServerStatusInterval": "Interne server status bijwerking interval",
|
"updateServerStatusInterval": "Interne server status bijwerking interval",
|
||||||
"updateTip": "Bijwerking: v1.0.{newest}",
|
|
||||||
"updateTipTooLow": "Huidige versie is te oud, werk aub bij naar versie v1.0.{newest}",
|
|
||||||
"upload": "Upload",
|
"upload": "Upload",
|
||||||
"upsideDown": "Ondersteboven",
|
"upsideDown": "Ondersteboven",
|
||||||
"uptime": "Uptime",
|
"uptime": "Uptime",
|
||||||
"urlOrJson": "URL of JSON",
|
|
||||||
"useCdn": "Gebruikt CDN",
|
"useCdn": "Gebruikt CDN",
|
||||||
"useCdnTip": "Niet-chinese gebruikers worden aangeraden om deze CDN te gebruiken. Wil je dat?",
|
"useCdnTip": "Niet-chinese gebruikers worden aangeraden om deze CDN te gebruiken. Wil je dat?",
|
||||||
"useNoPwd": "Er zal geen wachtwoord gebruikt worden",
|
"useNoPwd": "Er zal geen wachtwoord gebruikt worden",
|
||||||
"usePodmanByDefault": "Valt terug op Podman",
|
"usePodmanByDefault": "Valt terug op Podman",
|
||||||
"used": "Gebruikt",
|
"used": "Gebruikt",
|
||||||
"user": "Gebruiker",
|
|
||||||
"versionHaveUpdate": "Gevonden: v1.0.{build}, klik om bij te werken",
|
|
||||||
"versionUnknownUpdate": "Huidig: v1.0.{build}, klik om te kijken naar een nieuwere versie",
|
|
||||||
"versionUpdated": "Huidig: v1.0.{build}, is bijgewerkt naar de laatste versie",
|
|
||||||
"view": "Weergave",
|
"view": "Weergave",
|
||||||
"viewErr": "Zie foutmelding",
|
"viewErr": "Zie foutmelding",
|
||||||
"virtKeyHelpClipboard": "Kopiëren naar het klembord als de geselecteerde terminal niet leeg is, anders de inhoud van het klembord plakken in de terminal.",
|
"virtKeyHelpClipboard": "Kopiëren naar het klembord als de geselecteerde terminal niet leeg is, anders de inhoud van het klembord plakken in de terminal.",
|
||||||
@@ -335,8 +218,8 @@
|
|||||||
"watchNotPaired": "Geen gekoppelde Apple Watch",
|
"watchNotPaired": "Geen gekoppelde Apple Watch",
|
||||||
"webdavSettingEmpty": "Webdav-instelling is leeg",
|
"webdavSettingEmpty": "Webdav-instelling is leeg",
|
||||||
"whenOpenApp": "Bij het openen van de app",
|
"whenOpenApp": "Bij het openen van de app",
|
||||||
"willTakEeffectImmediately": "Zal onmiddellijk van kracht worden",
|
|
||||||
"wolTip": "Na het configureren van WOL (Wake-on-LAN), wordt elke keer dat de server wordt verbonden een WOL-verzoek verzonden.",
|
"wolTip": "Na het configureren van WOL (Wake-on-LAN), wordt elke keer dat de server wordt verbonden een WOL-verzoek verzonden.",
|
||||||
"write": "Schrijven",
|
"write": "Schrijven",
|
||||||
"writeScriptFailTip": "Het schrijven naar het script is mislukt, mogelijk door gebrek aan rechten of omdat de map niet bestaat."
|
"writeScriptFailTip": "Het schrijven naar het script is mislukt, mogelijk door gebrek aan rechten of omdat de map niet bestaat.",
|
||||||
|
"writeScriptTip": "Na het verbinden met de server wordt een script geschreven naar ~/.config/server_box om de systeemstatus te monitoren. U kunt de inhoud van het script controleren."
|
||||||
}
|
}
|
||||||
@@ -1,87 +1,42 @@
|
|||||||
{
|
{
|
||||||
"@@locale": "pt",
|
"@@locale": "pt",
|
||||||
"about": "Sobre",
|
|
||||||
"aboutThanks": "Agradecimentos a todos os participantes.",
|
"aboutThanks": "Agradecimentos a todos os participantes.",
|
||||||
"acceptBeta": "Aceitar atualizações da versão de teste",
|
"acceptBeta": "Aceitar atualizações da versão de teste",
|
||||||
"add": "Adicionar",
|
|
||||||
"addAServer": "Adicionar um servidor",
|
|
||||||
"addPrivateKey": "Adicionar uma chave privada",
|
|
||||||
"addSystemPrivateKeyTip": "Atualmente, não há nenhuma chave privada. Gostaria de adicionar a chave do sistema (~/.ssh/id_rsa)?",
|
"addSystemPrivateKeyTip": "Atualmente, não há nenhuma chave privada. Gostaria de adicionar a chave do sistema (~/.ssh/id_rsa)?",
|
||||||
"added2List": "Adicionado à lista de tarefas",
|
"added2List": "Adicionado à lista de tarefas",
|
||||||
"addr": "Endereço",
|
"addr": "Endereço",
|
||||||
"all": "Todos",
|
|
||||||
"alreadyLastDir": "Já é o diretório mais alto",
|
"alreadyLastDir": "Já é o diretório mais alto",
|
||||||
"alterUrl": "URL alternativa",
|
|
||||||
"askContinue": "{msg}, continuar?",
|
|
||||||
"attention": "Atenção",
|
|
||||||
"authFailTip": "Autenticação falhou, por favor verifique se a senha/chave/host/usuário, etc., estão incorretos.",
|
"authFailTip": "Autenticação falhou, por favor verifique se a senha/chave/host/usuário, etc., estão incorretos.",
|
||||||
"authRequired": "Autenticação necessária",
|
|
||||||
"auto": "Automático",
|
|
||||||
"autoBackupConflict": "Apenas um backup automático pode ser ativado por vez",
|
"autoBackupConflict": "Apenas um backup automático pode ser ativado por vez",
|
||||||
"autoCheckUpdate": "Verificação automática de atualização",
|
|
||||||
"autoConnect": "Conexão automática",
|
"autoConnect": "Conexão automática",
|
||||||
"autoRun": "Execução automática",
|
"autoRun": "Execução automática",
|
||||||
"autoUpdateHomeWidget": "Atualização automática do widget da tela inicial",
|
"autoUpdateHomeWidget": "Atualização automática do widget da tela inicial",
|
||||||
"backup": "Backup",
|
|
||||||
"backupTip": "Os dados exportados são criptografados de forma simples, por favor, guarde-os com segurança.",
|
"backupTip": "Os dados exportados são criptografados de forma simples, por favor, guarde-os com segurança.",
|
||||||
"backupVersionNotMatch": "Versão de backup não compatível, não é possível restaurar",
|
"backupVersionNotMatch": "Versão de backup não compatível, não é possível restaurar",
|
||||||
"battery": "Bateria",
|
"battery": "Bateria",
|
||||||
"beforeConnect": "O ServerBox escreverá e executará um script em `~/.config/server_box` após a conexão. Para mais detalhes técnicos, por favor visite [Github]({url}).",
|
|
||||||
"bgRun": "Execução em segundo plano",
|
"bgRun": "Execução em segundo plano",
|
||||||
"bgRunTip": "Este interruptor indica que o programa tentará rodar em segundo plano, mas a capacidade de fazer isso depende das permissões concedidas. No Android nativo, desative a 'Otimização de bateria' para este app, no MIUI, altere a estratégia de economia de energia para 'Sem restrições'.",
|
"bgRunTip": "Este interruptor indica que o programa tentará rodar em segundo plano, mas a capacidade de fazer isso depende das permissões concedidas. No Android nativo, desative a 'Otimização de bateria' para este app, no MIUI, altere a estratégia de economia de energia para 'Sem restrições'.",
|
||||||
"bioAuth": "Autenticação biométrica",
|
|
||||||
"browser": "Navegador",
|
|
||||||
"bulkImportServers": "Importar servidores em lote",
|
|
||||||
"bulkImportServersTip": "Você pode encontrar o [formato]({url}) aqui.",
|
|
||||||
"canPullRefresh": "Pode puxar para atualizar",
|
|
||||||
"cancel": "Cancelar",
|
|
||||||
"choose": "Escolher",
|
|
||||||
"chooseFontFile": "Escolher arquivo de fonte",
|
|
||||||
"choosePrivateKey": "Escolher chave privada",
|
|
||||||
"clear": "Limpar",
|
|
||||||
"clipboard": "Área de transferência",
|
|
||||||
"close": "Fechar",
|
|
||||||
"cmd": "Comando",
|
"cmd": "Comando",
|
||||||
"cnKeyboardComp": "Compatibilidade com Android chinês",
|
|
||||||
"cnKeyboardCompTip": "Se o terminal abrir um teclado seguro, você pode ativá-lo.",
|
|
||||||
"collapseUI": "Colapsar",
|
|
||||||
"collapseUITip": "Deve colapsar listas longas na UI por padrão?",
|
"collapseUITip": "Deve colapsar listas longas na UI por padrão?",
|
||||||
"conn": "Conectar",
|
"conn": "Conectar",
|
||||||
"connected": "Conectado",
|
|
||||||
"container": "Contêiner",
|
"container": "Contêiner",
|
||||||
"containerName": "Nome do contêiner",
|
|
||||||
"containerStatus": "Estado do contêiner",
|
|
||||||
"containerTrySudoTip": "Por exemplo: se o usuário for definido como aaa dentro do app, mas o Docker estiver instalado sob o usuário root, esta opção precisará ser ativada",
|
"containerTrySudoTip": "Por exemplo: se o usuário for definido como aaa dentro do app, mas o Docker estiver instalado sob o usuário root, esta opção precisará ser ativada",
|
||||||
"content": "Conteúdo",
|
|
||||||
"convert": "Converter",
|
"convert": "Converter",
|
||||||
"copy": "Copiar",
|
|
||||||
"copyPath": "Copiar caminho",
|
"copyPath": "Copiar caminho",
|
||||||
"cpuViewAsProgressTip": "Exiba a taxa de uso de cada CPU em estilo de barra de progresso (estilo antigo)",
|
"cpuViewAsProgressTip": "Exiba a taxa de uso de cada CPU em estilo de barra de progresso (estilo antigo)",
|
||||||
"createFile": "Criar arquivo",
|
|
||||||
"createFolder": "Criar pasta",
|
|
||||||
"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": "\"Nome do comando\": \"Comando\"",
|
"customCmdHint": "\"Nome do comando\": \"Comando\"",
|
||||||
"dark": "Escuro",
|
|
||||||
"day": "Dia",
|
|
||||||
"debug": "Debugar",
|
|
||||||
"decode": "Decodificar",
|
"decode": "Decodificar",
|
||||||
"decompress": "Descomprimir",
|
"decompress": "Descomprimir",
|
||||||
"delete": "Excluir",
|
|
||||||
"deleteScripts": "Excluir scripts do servidor simultaneamente",
|
|
||||||
"deleteServers": "Excluir servidores em lote",
|
"deleteServers": "Excluir servidores em lote",
|
||||||
"deviceName": "Nome do dispositivo",
|
|
||||||
"dirEmpty": "Certifique-se de que a pasta está vazia",
|
"dirEmpty": "Certifique-se de que a pasta está vazia",
|
||||||
"disabled": "Desativado",
|
|
||||||
"disconnected": "Desconectado",
|
"disconnected": "Desconectado",
|
||||||
"disk": "Disco",
|
"disk": "Disco",
|
||||||
"diskIgnorePath": "Caminhos de disco ignorados",
|
"diskIgnorePath": "Caminhos de disco ignorados",
|
||||||
"displayCpuIndex": "Exiba o índice de CPU",
|
"displayCpuIndex": "Exiba o índice de CPU",
|
||||||
"displayName": "Nome de exibição",
|
|
||||||
"dl2Local": "Baixar {fileName} para o local?",
|
"dl2Local": "Baixar {fileName} para o local?",
|
||||||
"doc": "Documentação",
|
|
||||||
"dockerEditHost": "Editar DOCKER_HOST",
|
|
||||||
"dockerEmptyRunningItems": "Não há contêineres em execução.\nIsso pode ser porque:\n- O usuário que instalou o Docker difere do usuário configurado no app\n- A variável de ambiente DOCKER_HOST não foi lida corretamente. Você pode verificar isso executando `echo $DOCKER_HOST` no terminal.",
|
"dockerEmptyRunningItems": "Não há contêineres em execução.\nIsso pode ser porque:\n- O usuário que instalou o Docker difere do usuário configurado no app\n- A variável de ambiente DOCKER_HOST não foi lida corretamente. Você pode verificar isso executando `echo $DOCKER_HOST` no terminal.",
|
||||||
"dockerImagesFmt": "Total de {count} imagens",
|
"dockerImagesFmt": "Total de {count} imagens",
|
||||||
"dockerNotInstalled": "Docker não instalado",
|
"dockerNotInstalled": "Docker não instalado",
|
||||||
@@ -89,77 +44,48 @@
|
|||||||
"dockerStatusRunningFmt": "{count} contêiner(es) em execução",
|
"dockerStatusRunningFmt": "{count} contêiner(es) em execução",
|
||||||
"doubleColumnMode": "Modo de coluna dupla",
|
"doubleColumnMode": "Modo de coluna dupla",
|
||||||
"doubleColumnTip": "Esta opção apenas ativa a funcionalidade, se ela será ativada depende também da largura do dispositivo",
|
"doubleColumnTip": "Esta opção apenas ativa a funcionalidade, se ela será ativada depende também da largura do dispositivo",
|
||||||
"download": "Baixar",
|
|
||||||
"edit": "Editar",
|
|
||||||
"editVirtKeys": "Editar teclas virtuais",
|
"editVirtKeys": "Editar teclas virtuais",
|
||||||
"editor": "Editor",
|
"editor": "Editor",
|
||||||
"editorHighlightTip": "O desempenho do destaque de código atualmente é ruim, pode optar por desativá-lo para melhorar.",
|
"editorHighlightTip": "O desempenho do destaque de código atualmente é ruim, pode optar por desativá-lo para melhorar.",
|
||||||
"encode": "Codificar",
|
"encode": "Codificar",
|
||||||
"error": "Erro",
|
"envVars": "Variável de ambiente",
|
||||||
"exampleName": "Exemplo de nome",
|
|
||||||
"experimentalFeature": "Recurso experimental",
|
"experimentalFeature": "Recurso experimental",
|
||||||
"export": "Exportar",
|
|
||||||
"extraArgs": "Argumentos extras",
|
"extraArgs": "Argumentos extras",
|
||||||
"failed": "Falhou",
|
"fallbackSshDest": "Destino SSH de fallback",
|
||||||
"fdroidReleaseTip": "Se você baixou este aplicativo do Fdroid, é recomendado desativar esta opção.",
|
"fdroidReleaseTip": "Se você baixou este aplicativo do F-Droid, é recomendado desativar esta opção.",
|
||||||
"feedback": "Feedback",
|
|
||||||
"feedbackOnGithub": "Se você tem qualquer problema, por favor, dê feedback no GitHub",
|
|
||||||
"fieldMustNotEmpty": "Estes campos não podem estar vazios.",
|
|
||||||
"fileNotExist": "{file} não existe",
|
|
||||||
"fileTooLarge": "Arquivo '{file}' muito grande '{size}', excedendo {sizeMax}",
|
"fileTooLarge": "Arquivo '{file}' muito grande '{size}', excedendo {sizeMax}",
|
||||||
"files": "Arquivos",
|
|
||||||
"finished": "Finalizado",
|
|
||||||
"followSystem": "Seguir sistema",
|
"followSystem": "Seguir sistema",
|
||||||
"font": "Fonte",
|
"font": "Fonte",
|
||||||
"fontSize": "Tamanho da fonte",
|
"fontSize": "Tamanho da fonte",
|
||||||
"forExample": "Por exemplo",
|
|
||||||
"force": "Forçar",
|
"force": "Forçar",
|
||||||
"foundNUpdate": "Encontradas {count} atualizações",
|
|
||||||
"fullScreen": "Modo tela cheia",
|
"fullScreen": "Modo tela cheia",
|
||||||
"fullScreenJitter": "Tremulação em tela cheia",
|
"fullScreenJitter": "Tremulação em tela cheia",
|
||||||
"fullScreenJitterHelp": "Prevenir burn-in de tela",
|
"fullScreenJitterHelp": "Prevenir burn-in de tela",
|
||||||
"fullScreenTip": "Deve ser ativado o modo de tela cheia quando o dispositivo é girado para o modo paisagem? Esta opção aplica-se apenas à aba do servidor.",
|
"fullScreenTip": "Deve ser ativado o modo de tela cheia quando o dispositivo é girado para o modo paisagem? Esta opção aplica-se apenas à aba do servidor.",
|
||||||
"getPushTokenFailed": "Não foi possível obter token de notificação",
|
|
||||||
"gettingToken": "Obtendo Token...",
|
|
||||||
"goBackQ": "Voltar?",
|
"goBackQ": "Voltar?",
|
||||||
"goto": "Ir para",
|
"goto": "Ir para",
|
||||||
"hideTitleBar": "Ocultar barra de título",
|
"hideTitleBar": "Ocultar barra de título",
|
||||||
"hideTitleBarTip": "Após ligar, segure os três botões no canto superior direito para arrastar.",
|
|
||||||
"highlight": "Destaque de código",
|
"highlight": "Destaque de código",
|
||||||
"homeWidgetUrlConfig": "Configuração de URL do widget da tela inicial",
|
"homeWidgetUrlConfig": "Configuração de URL do widget da tela inicial",
|
||||||
"host": "Host",
|
"host": "Host",
|
||||||
"hour": "Hora",
|
|
||||||
"httpFailedWithCode": "Falha na solicitação, código de status: {code}",
|
"httpFailedWithCode": "Falha na solicitação, código de status: {code}",
|
||||||
"icloudSynced": "iCloud sincronizado, algumas configurações podem precisar de reinicialização do app para serem aplicadas.",
|
|
||||||
"ignoreCert": "Ignorar certificado",
|
"ignoreCert": "Ignorar certificado",
|
||||||
"image": "Imagem",
|
"image": "Imagem",
|
||||||
"imagesList": "Lista de imagens",
|
"imagesList": "Lista de imagens",
|
||||||
"import": "Importar",
|
|
||||||
"inAppUpdate": "Atualizar dentro do app? Caso contrário, baixe usando um navegador.",
|
|
||||||
"init": "Inicializar",
|
"init": "Inicializar",
|
||||||
"inner": "Interno",
|
"inner": "Interno",
|
||||||
"inputDomainHere": "Insira o domínio aqui",
|
|
||||||
"install": "Instalar",
|
"install": "Instalar",
|
||||||
"installDockerWithUrl": "Por favor, instale o Docker primeiro em https://docs.docker.com/engine/install",
|
"installDockerWithUrl": "Por favor, instale o Docker primeiro em https://docs.docker.com/engine/install",
|
||||||
"invalid": "Inválido",
|
"invalid": "Inválido",
|
||||||
"invalidJson": "JSON inválido",
|
|
||||||
"invalidVersion": "Versão não suportada",
|
|
||||||
"invalidVersionHelp": "Por favor, assegure que o Docker está corretamente instalado, ou que não está usando uma versão compilada por si mesmo. Se não houver problemas, por favor, reporte em {url}.",
|
|
||||||
"isBusy": "Ocupado no momento",
|
|
||||||
"jumpServer": "Servidor de salto",
|
"jumpServer": "Servidor de salto",
|
||||||
"keepForeground": "Por favor, mantenha o app em primeiro plano!",
|
"keepForeground": "Por favor, mantenha o app em primeiro plano!",
|
||||||
"keepStatusWhenErr": "Manter o status anterior do servidor",
|
"keepStatusWhenErr": "Manter o status anterior do servidor",
|
||||||
"keepStatusWhenErrTip": "Limitado a erros de execução de scripts",
|
"keepStatusWhenErrTip": "Limitado a erros de execução de scripts",
|
||||||
"keyAuth": "Autenticação por chave",
|
"keyAuth": "Autenticação por chave",
|
||||||
"language": "Idioma",
|
"letterCache": "Cache de letras",
|
||||||
"languageName": "Português",
|
"letterCacheTip": "Recomendado desativar, mas após desativar, será impossível inserir caracteres CJK.",
|
||||||
"lastTry": "Última tentativa",
|
|
||||||
"launchPage": "Página de lançamento",
|
|
||||||
"license": "Licença de código aberto",
|
"license": "Licença de código aberto",
|
||||||
"light": "Claro",
|
|
||||||
"loadingFiles": "Carregando diretórios...",
|
|
||||||
"location": "Localização",
|
"location": "Localização",
|
||||||
"log": "Log",
|
|
||||||
"loss": "Taxa de perda",
|
"loss": "Taxa de perda",
|
||||||
"madeWithLove": "Feito com ❤️ por {myGithub}",
|
"madeWithLove": "Feito com ❤️ por {myGithub}",
|
||||||
"manual": "Manual",
|
"manual": "Manual",
|
||||||
@@ -167,60 +93,36 @@
|
|||||||
"maxRetryCount": "Número de tentativas de reconexão com o servidor",
|
"maxRetryCount": "Número de tentativas de reconexão com o servidor",
|
||||||
"maxRetryCountEqual0": "Irá tentar indefinidamente",
|
"maxRetryCountEqual0": "Irá tentar indefinidamente",
|
||||||
"min": "Mínimo",
|
"min": "Mínimo",
|
||||||
"minute": "Minuto",
|
|
||||||
"mission": "Missão",
|
"mission": "Missão",
|
||||||
"more": "Mais",
|
"more": "Mais",
|
||||||
"moveOutServerFuncBtnsHelp": "Ativado: Mostra abaixo de cada cartão na aba do servidor. Desativado: Mostra no topo da página de detalhes do servidor.",
|
"moveOutServerFuncBtnsHelp": "Ativado: Mostra abaixo de cada cartão na aba do servidor. Desativado: Mostra no topo da página de detalhes do servidor.",
|
||||||
"ms": "ms",
|
"ms": "ms",
|
||||||
"name": "Nome",
|
|
||||||
"needHomeDir": "Se você é usuário de Synology, [veja aqui](https://kb.synology.com/DSM/tutorial/user_enable_home_service). Usuários de outros sistemas precisam pesquisar como criar um diretório home.",
|
"needHomeDir": "Se você é usuário de Synology, [veja aqui](https://kb.synology.com/DSM/tutorial/user_enable_home_service). Usuários de outros sistemas precisam pesquisar como criar um diretório home.",
|
||||||
"needRestart": "Necessita reiniciar o app",
|
"needRestart": "Necessita reiniciar o app",
|
||||||
"net": "Rede",
|
"net": "Rede",
|
||||||
"netViewType": "Tipo de visualização de rede",
|
"netViewType": "Tipo de visualização de rede",
|
||||||
"newContainer": "Novo contêiner",
|
"newContainer": "Novo contêiner",
|
||||||
"noClient": "Sem conexão SSH",
|
|
||||||
"noInterface": "Sem interface disponível",
|
|
||||||
"noLineChart": "Não usar gráficos de linha",
|
"noLineChart": "Não usar gráficos de linha",
|
||||||
"noNotiPerm": "Sem permissão de notificação, possivelmente sem indicação de progresso ao baixar atualizações de aplicativos.",
|
"noLineChartForCpu": "Não utilizar gráficos de linhas para a CPU",
|
||||||
"noOptions": "Sem opções",
|
|
||||||
"noPrivateKeyTip": "A chave privada não existe, pode ter sido deletada ou há um erro de configuração.",
|
"noPrivateKeyTip": "A chave privada não existe, pode ter sido deletada ou há um erro de configuração.",
|
||||||
"noPromptAgain": "Não perguntar novamente",
|
"noPromptAgain": "Não perguntar novamente",
|
||||||
"noResult": "Sem resultados",
|
|
||||||
"noSavedPrivateKey": "Nenhuma chave privada salva.",
|
|
||||||
"noSavedSnippet": "Nenhum snippet de código salvo.",
|
|
||||||
"noServerAvailable": "Nenhum servidor disponível.",
|
|
||||||
"noTask": "Sem tarefas",
|
|
||||||
"noUpdateAvailable": "Sem atualizações disponíveis",
|
|
||||||
"node": "Nó",
|
"node": "Nó",
|
||||||
"notAvailable": "Indisponível",
|
"notAvailable": "Indisponível",
|
||||||
"notSelected": "Não selecionado",
|
|
||||||
"note": "Nota",
|
|
||||||
"nullToken": "Token nulo",
|
|
||||||
"ok": "OK",
|
|
||||||
"onServerDetailPage": "Na página de detalhes do servidor",
|
"onServerDetailPage": "Na página de detalhes do servidor",
|
||||||
"onlyOneLine": "Exibir apenas como uma linha (rolável)",
|
"onlyOneLine": "Exibir apenas como uma linha (rolável)",
|
||||||
"onlyWhenCoreBiggerThan8": "Efetivo apenas quando o número de núcleos > 8",
|
"onlyWhenCoreBiggerThan8": "Efetivo apenas quando o número de núcleos > 8",
|
||||||
"open": "Abrir",
|
|
||||||
"openLastPath": "Abrir o último caminho",
|
"openLastPath": "Abrir o último caminho",
|
||||||
"openLastPathTip": "Registros diferentes para servidores diferentes, e registra o caminho ao sair",
|
"openLastPathTip": "Registros diferentes para servidores diferentes, e registra o caminho ao sair",
|
||||||
"parseContainerStats": "Analisar status de uso do contêiner",
|
|
||||||
"parseContainerStatsTip": "Análise de status do Docker pode ser lenta",
|
"parseContainerStatsTip": "Análise de status do Docker pode ser lenta",
|
||||||
"paste": "Colar",
|
|
||||||
"path": "Caminho",
|
|
||||||
"percentOfSize": "{percent}% de {size}",
|
"percentOfSize": "{percent}% de {size}",
|
||||||
"pickFile": "Escolher arquivo",
|
"permission": "Permissões",
|
||||||
"pingAvg": "Média:",
|
"pingAvg": "Média:",
|
||||||
"pingInputIP": "Por favor, insira o IP ou domínio alvo",
|
"pingInputIP": "Por favor, insira o IP ou domínio alvo",
|
||||||
"pingNoServer": "Nenhum servidor disponível para Ping\nPor favor, adicione um servidor na aba de servidores e tente novamente",
|
"pingNoServer": "Nenhum servidor disponível para Ping\nPor favor, adicione um servidor na aba de servidores e tente novamente",
|
||||||
"pkg": "Gerenciamento de pacotes",
|
"pkg": "Gerenciamento de pacotes",
|
||||||
"pkgUpgradeTip": "Por favor, faça backup do seu sistema antes de atualizar.",
|
|
||||||
"platformNotSupportUpdate": "Atualização não suportada na plataforma atual, por favor, instale manualmente a versão mais recente do código-fonte",
|
|
||||||
"plugInType": "Tipo de Inserção",
|
"plugInType": "Tipo de Inserção",
|
||||||
"plzEnterHost": "Por favor, insira o host",
|
|
||||||
"plzSelectKey": "Por favor, selecione uma chave privada",
|
|
||||||
"port": "Porta",
|
"port": "Porta",
|
||||||
"preview": "Pré-visualização",
|
"preview": "Pré-visualização",
|
||||||
"primaryColorSeed": "Semente da cor primária",
|
|
||||||
"privateKey": "Chave privada",
|
"privateKey": "Chave privada",
|
||||||
"process": "Processo",
|
"process": "Processo",
|
||||||
"pushToken": "Token de notificação push",
|
"pushToken": "Token de notificação push",
|
||||||
@@ -230,16 +132,11 @@
|
|||||||
"pwd": "Senha",
|
"pwd": "Senha",
|
||||||
"read": "Leitura",
|
"read": "Leitura",
|
||||||
"reboot": "Reiniciar",
|
"reboot": "Reiniciar",
|
||||||
"rememberChoice": "Lembrar da seleção",
|
|
||||||
"rememberPwdInMem": "Lembrar senha na memória",
|
"rememberPwdInMem": "Lembrar senha na memória",
|
||||||
"rememberPwdInMemTip": "Usado para contêineres, suspensão, etc.",
|
"rememberPwdInMemTip": "Usado para contêineres, suspensão, etc.",
|
||||||
"rememberWindowSize": "Lembrar o tamanho da janela",
|
"rememberWindowSize": "Lembrar o tamanho da janela",
|
||||||
"remotePath": "Caminho remoto",
|
"remotePath": "Caminho remoto",
|
||||||
"rename": "Renomear",
|
|
||||||
"reportBugsOnGithubIssue": "Por favor, reporte problemas em {url}",
|
|
||||||
"restart": "Reiniciar",
|
"restart": "Reiniciar",
|
||||||
"restore": "Restaurar",
|
|
||||||
"restoreSuccess": "Restauração bem-sucedida, é necessário reiniciar o app para aplicar as mudanças",
|
|
||||||
"result": "Resultado",
|
"result": "Resultado",
|
||||||
"rotateAngel": "Ângulo de rotação",
|
"rotateAngel": "Ângulo de rotação",
|
||||||
"route": "Roteamento",
|
"route": "Roteamento",
|
||||||
@@ -254,14 +151,8 @@
|
|||||||
"serverDetailOrder": "Ordem dos componentes na página de detalhes do servidor",
|
"serverDetailOrder": "Ordem dos componentes na página de detalhes do servidor",
|
||||||
"serverFuncBtns": "Botões de função do servidor",
|
"serverFuncBtns": "Botões de função do servidor",
|
||||||
"serverOrder": "Ordem do servidor",
|
"serverOrder": "Ordem do servidor",
|
||||||
"serverTabConnecting": "Conectando...",
|
|
||||||
"serverTabEmpty": "Não há servidores no momento.\nClique no botão inferior direito para adicionar um.",
|
|
||||||
"serverTabFailed": "Falha",
|
|
||||||
"serverTabLoading": "Carregando...",
|
|
||||||
"serverTabPlzSave": "Por favor, salve esta chave privada novamente",
|
|
||||||
"serverTabUnkown": "Estado desconhecido",
|
|
||||||
"setting": "Configurações",
|
|
||||||
"sftpDlPrepare": "Preparando para conectar ao servidor...",
|
"sftpDlPrepare": "Preparando para conectar ao servidor...",
|
||||||
|
"sftpEditorTip": "Se vazio, use o editor de arquivos integrado do aplicativo. Se houver um valor, use o editor do servidor remoto, por exemplo, `vim` (recomendado detectar automaticamente de acordo com `EDITOR`).",
|
||||||
"sftpRmrDirSummary": "Usar `rm -r` em SFTP para excluir pastas",
|
"sftpRmrDirSummary": "Usar `rm -r` em SFTP para excluir pastas",
|
||||||
"sftpSSHConnected": "SFTP conectado...",
|
"sftpSSHConnected": "SFTP conectado...",
|
||||||
"sftpShowFoldersFirst": "Mostrar pastas primeiro",
|
"sftpShowFoldersFirst": "Mostrar pastas primeiro",
|
||||||
@@ -270,17 +161,19 @@
|
|||||||
"size": "Tamanho",
|
"size": "Tamanho",
|
||||||
"snippet": "Snippet",
|
"snippet": "Snippet",
|
||||||
"softWrap": "Quebra de linha suave",
|
"softWrap": "Quebra de linha suave",
|
||||||
|
"specifyDev": "Especificar dispositivo",
|
||||||
|
"specifyDevTip": "Por exemplo, as estatísticas de tráfego de rede são por padrão para todos os dispositivos. Você pode especificar um dispositivo específico aqui.",
|
||||||
"speed": "Velocidade",
|
"speed": "Velocidade",
|
||||||
"spentTime": "Tempo gasto: {time}",
|
"spentTime": "Tempo gasto: {time}",
|
||||||
"sshTermHelp": "Quando o terminal é rolável, arrastar horizontalmente pode selecionar texto. Clicar no botão do teclado ativa/desativa o teclado. O ícone de arquivo abre o SFTP do caminho atual. O botão da área de transferência copia o conteúdo quando o texto é selecionado e cola o conteúdo da área de transferência no terminal quando nenhum texto é selecionado e há conteúdo na área de transferência. O ícone de código cola trechos de código no terminal e os executa.",
|
"sshTermHelp": "Quando o terminal é rolável, arrastar horizontalmente pode selecionar texto. Clicar no botão do teclado ativa/desativa o teclado. O ícone de arquivo abre o SFTP do caminho atual. O botão da área de transferência copia o conteúdo quando o texto é selecionado e cola o conteúdo da área de transferência no terminal quando nenhum texto é selecionado e há conteúdo na área de transferência. O ícone de código cola trechos de código no terminal e os executa.",
|
||||||
"sshTip": "Esta funcionalidade está em fase de teste.\n\nPor favor, reporte problemas em {url} ou junte-se a nós no desenvolvimento.",
|
"sshTip": "Esta funcionalidade está em fase de teste.\n\nPor favor, reporte problemas em {url} ou junte-se a nós no desenvolvimento.",
|
||||||
"sshVirtualKeyAutoOff": "Desativação automática das teclas virtuais",
|
"sshVirtualKeyAutoOff": "Desativação automática das teclas virtuais",
|
||||||
"start": "Iniciar",
|
"start": "Iniciar",
|
||||||
|
"stat": "Estatísticas",
|
||||||
"stats": "Estatísticas",
|
"stats": "Estatísticas",
|
||||||
"stop": "Parar",
|
"stop": "Parar",
|
||||||
"stopped": "Parado",
|
"stopped": "Parado",
|
||||||
"storage": "Armazenamento",
|
"storage": "Armazenamento",
|
||||||
"success": "Sucesso",
|
|
||||||
"supportFmtArgs": "Suporta os seguintes argumentos formatados:",
|
"supportFmtArgs": "Suporta os seguintes argumentos formatados:",
|
||||||
"suspend": "Suspender",
|
"suspend": "Suspender",
|
||||||
"suspendTip": "A função de suspensão requer permissões de root e suporte do systemd.",
|
"suspendTip": "A função de suspensão requer permissões de root e suporte do systemd.",
|
||||||
@@ -296,35 +189,25 @@
|
|||||||
"textScaler": "Escala de texto",
|
"textScaler": "Escala de texto",
|
||||||
"textScalerTip": "1.0 => 100% (tamanho original), afeta apenas algumas fontes na página do servidor, não é recomendado alterar.",
|
"textScalerTip": "1.0 => 100% (tamanho original), afeta apenas algumas fontes na página do servidor, não é recomendado alterar.",
|
||||||
"theme": "Tema",
|
"theme": "Tema",
|
||||||
"themeMode": "Modo do tema",
|
|
||||||
"time": "Tempo",
|
"time": "Tempo",
|
||||||
"times": "Vezes",
|
"times": "Vezes",
|
||||||
"total": "Total",
|
"total": "Total",
|
||||||
"traffic": "Tráfego",
|
"traffic": "Tráfego",
|
||||||
"trySudo": "Tentar usar sudo",
|
"trySudo": "Tentar usar sudo",
|
||||||
"ttl": "Tempo de vida do cache",
|
"ttl": "TTL",
|
||||||
"unknown": "Desconhecido",
|
"unknown": "Desconhecido",
|
||||||
"unknownError": "Erro desconhecido",
|
|
||||||
"unkownConvertMode": "Modo de conversão desconhecido",
|
"unkownConvertMode": "Modo de conversão desconhecido",
|
||||||
"update": "Atualizar",
|
"update": "Atualizar",
|
||||||
"updateAll": "Atualizar tudo",
|
|
||||||
"updateIntervalEqual0": "Se definido como 0, o estado do servidor não será atualizado automaticamente.\nE o uso da CPU não poderá ser calculado.",
|
"updateIntervalEqual0": "Se definido como 0, o estado do servidor não será atualizado automaticamente.\nE o uso da CPU não poderá ser calculado.",
|
||||||
"updateServerStatusInterval": "Intervalo de atualização do estado do servidor",
|
"updateServerStatusInterval": "Intervalo de atualização do estado do servidor",
|
||||||
"updateTip": "Nova versão: v1.0.{newest}",
|
|
||||||
"updateTipTooLow": "Versão atual muito antiga, por favor, atualize para v1.0.{newest}",
|
|
||||||
"upload": "Upload",
|
"upload": "Upload",
|
||||||
"upsideDown": "Inverter verticalmente",
|
"upsideDown": "Inverter verticalmente",
|
||||||
"uptime": "Tempo de atividade",
|
"uptime": "Tempo de atividade",
|
||||||
"urlOrJson": "URL ou JSON",
|
|
||||||
"useCdn": "Utilizando CDN",
|
"useCdn": "Utilizando CDN",
|
||||||
"useCdnTip": "Recomenda-se que usuários não chineses usem CDN. Gostaria de usá-lo?",
|
"useCdnTip": "Recomenda-se que usuários não chineses usem CDN. Gostaria de usá-lo?",
|
||||||
"useNoPwd": "Será usado sem senha",
|
"useNoPwd": "Será usado sem senha",
|
||||||
"usePodmanByDefault": "Usar Podman por padrão",
|
"usePodmanByDefault": "Usar Podman por padrão",
|
||||||
"used": "Usado",
|
"used": "Usado",
|
||||||
"user": "Usuário",
|
|
||||||
"versionHaveUpdate": "Nova versão encontrada: v1.0.{build}, clique para atualizar",
|
|
||||||
"versionUnknownUpdate": "Versão atual: v1.0.{build}, clique para verificar atualizações",
|
|
||||||
"versionUpdated": "Versão atual: v1.0.{build}, já está atualizado",
|
|
||||||
"view": "Visualização",
|
"view": "Visualização",
|
||||||
"viewErr": "Ver erro",
|
"viewErr": "Ver erro",
|
||||||
"virtKeyHelpClipboard": "Se houver texto selecionado no terminal, copia para a área de transferência, caso contrário, cola o conteúdo da área de transferência no terminal.",
|
"virtKeyHelpClipboard": "Se houver texto selecionado no terminal, copia para a área de transferência, caso contrário, cola o conteúdo da área de transferência no terminal.",
|
||||||
@@ -335,8 +218,8 @@
|
|||||||
"watchNotPaired": "Não há Apple Watch pareado",
|
"watchNotPaired": "Não há Apple Watch pareado",
|
||||||
"webdavSettingEmpty": "Configurações de Webdav estão vazias",
|
"webdavSettingEmpty": "Configurações de Webdav estão vazias",
|
||||||
"whenOpenApp": "Ao abrir o app",
|
"whenOpenApp": "Ao abrir o app",
|
||||||
"willTakEeffectImmediately": "As alterações serão aplicadas imediatamente",
|
|
||||||
"wolTip": "Após configurar o WOL (Wake-on-LAN), um pedido de WOL é enviado cada vez que o servidor é conectado.",
|
"wolTip": "Após configurar o WOL (Wake-on-LAN), um pedido de WOL é enviado cada vez que o servidor é conectado.",
|
||||||
"write": "Escrita",
|
"write": "Escrita",
|
||||||
"writeScriptFailTip": "Falha ao escrever no script, possivelmente devido à falta de permissões ou o diretório não existe."
|
"writeScriptFailTip": "Falha ao escrever no script, possivelmente devido à falta de permissões ou o diretório não existe.",
|
||||||
|
"writeScriptTip": "Após conectar ao servidor, um script será escrito em ~/.config/server_box para monitorar o status do sistema. Você pode revisar o conteúdo do script."
|
||||||
}
|
}
|
||||||
@@ -1,87 +1,42 @@
|
|||||||
{
|
{
|
||||||
"@@locale": "ru",
|
"@@locale": "ru",
|
||||||
"about": "о",
|
|
||||||
"aboutThanks": "Благодарности всем участникам.",
|
"aboutThanks": "Благодарности всем участникам.",
|
||||||
"acceptBeta": "Принять обновления тестовой версии",
|
"acceptBeta": "Принять обновления тестовой версии",
|
||||||
"add": "добавить",
|
|
||||||
"addAServer": "добавить сервер",
|
|
||||||
"addPrivateKey": "добавить приватный ключ",
|
|
||||||
"addSystemPrivateKeyTip": "В данный момент приватные ключи отсутствуют. Добавить системный приватный ключ (~/.ssh/id_rsa)?",
|
"addSystemPrivateKeyTip": "В данный момент приватные ключи отсутствуют. Добавить системный приватный ключ (~/.ssh/id_rsa)?",
|
||||||
"added2List": "добавлено в список задач",
|
"added2List": "добавлено в список задач",
|
||||||
"addr": "Адрес",
|
"addr": "Адрес",
|
||||||
"all": "все",
|
|
||||||
"alreadyLastDir": "Уже в корневом каталоге",
|
"alreadyLastDir": "Уже в корневом каталоге",
|
||||||
"alterUrl": "альтернативная ссылка",
|
|
||||||
"askContinue": "{msg}, продолжить?",
|
|
||||||
"attention": "внимание",
|
|
||||||
"authFailTip": "Аутентификация не удалась, пожалуйста, проверьте, правильны ли пароль/ключ/хост/пользователь и т.д.",
|
"authFailTip": "Аутентификация не удалась, пожалуйста, проверьте, правильны ли пароль/ключ/хост/пользователь и т.д.",
|
||||||
"authRequired": "требуется аутентификация",
|
|
||||||
"auto": "авто",
|
|
||||||
"autoBackupConflict": "Может быть включено только одно автоматическое резервное копирование",
|
"autoBackupConflict": "Может быть включено только одно автоматическое резервное копирование",
|
||||||
"autoCheckUpdate": "автоматическая проверка обновлений",
|
|
||||||
"autoConnect": "автоматическое подключение",
|
"autoConnect": "автоматическое подключение",
|
||||||
"autoRun": "автозапуск",
|
"autoRun": "автозапуск",
|
||||||
"autoUpdateHomeWidget": "автоматическое обновление виджета на главном экране",
|
"autoUpdateHomeWidget": "автоматическое обновление виджета на главном экране",
|
||||||
"backup": "резервное копирование",
|
|
||||||
"backupTip": "Экспортированные данные зашифрованы простым способом, пожалуйста, храните их в безопасности.",
|
"backupTip": "Экспортированные данные зашифрованы простым способом, пожалуйста, храните их в безопасности.",
|
||||||
"backupVersionNotMatch": "Версия резервной копии не совпадает, восстановление невозможно",
|
"backupVersionNotMatch": "Версия резервной копии не совпадает, восстановление невозможно",
|
||||||
"battery": "батарея",
|
"battery": "батарея",
|
||||||
"beforeConnect": "ServerBox запишет и выполнит скрипт в `~/.config/server_box` после подключения. Для получения дополнительных технических подробностей, пожалуйста, посетите [Github]({url}).",
|
|
||||||
"bgRun": "работа в фоновом режиме",
|
"bgRun": "работа в фоновом режиме",
|
||||||
"bgRunTip": "Этот переключатель означает, что программа будет пытаться работать в фоновом режиме, но фактическое выполнение зависит от того, включено ли разрешение. Для нативного Android отключите «Оптимизацию батареи» для этого приложения, для MIUI измените стратегию энергосбережения на «Без ограничений».",
|
"bgRunTip": "Этот переключатель означает, что программа будет пытаться работать в фоновом режиме, но фактическое выполнение зависит от того, включено ли разрешение. Для нативного Android отключите «Оптимизацию батареи» для этого приложения, для MIUI измените стратегию энергосбережения на «Без ограничений».",
|
||||||
"bioAuth": "биометрическая аутентификация",
|
|
||||||
"browser": "Браузер",
|
|
||||||
"bulkImportServers": "Пакетный импорт серверов",
|
|
||||||
"bulkImportServersTip": "[Формат]({url}) можно найти здесь.",
|
|
||||||
"canPullRefresh": "можно обновить, потянув вниз",
|
|
||||||
"cancel": "отмена",
|
|
||||||
"choose": "выбрать",
|
|
||||||
"chooseFontFile": "выбрать файл шрифта",
|
|
||||||
"choosePrivateKey": "выбрать приватный ключ",
|
|
||||||
"clear": "очистить",
|
|
||||||
"clipboard": "буфер обмена",
|
|
||||||
"close": "закрыть",
|
|
||||||
"cmd": "команда",
|
"cmd": "команда",
|
||||||
"cnKeyboardComp": "Совместимость с китайским Android",
|
|
||||||
"cnKeyboardCompTip": "Если терминал отображает безопасную клавиатуру, вы можете ее активировать.",
|
|
||||||
"collapseUI": "свернуть",
|
|
||||||
"collapseUITip": "Свернуть длинные списки в UI по умолчанию",
|
"collapseUITip": "Свернуть длинные списки в UI по умолчанию",
|
||||||
"conn": "подключение",
|
"conn": "подключение",
|
||||||
"connected": "подключено",
|
|
||||||
"container": "контейнер",
|
"container": "контейнер",
|
||||||
"containerName": "имя контейнера",
|
|
||||||
"containerStatus": "статус контейнера",
|
|
||||||
"containerTrySudoTip": "Например: если пользователь в приложении установлен как aaa, но Docker установлен под пользователем root, тогда нужно включить эту опцию",
|
"containerTrySudoTip": "Например: если пользователь в приложении установлен как aaa, но Docker установлен под пользователем root, тогда нужно включить эту опцию",
|
||||||
"content": "Содержимое",
|
|
||||||
"convert": "конвертировать",
|
"convert": "конвертировать",
|
||||||
"copy": "копировать",
|
|
||||||
"copyPath": "копировать путь",
|
"copyPath": "копировать путь",
|
||||||
"cpuViewAsProgressTip": "Отобразите уровень использования каждого процессора в виде индикатора выполнения (старый стиль)",
|
"cpuViewAsProgressTip": "Отобразите уровень использования каждого процессора в виде индикатора выполнения (старый стиль)",
|
||||||
"createFile": "создать файл",
|
|
||||||
"createFolder": "создать папку",
|
|
||||||
"cursorType": "Тип курсора",
|
"cursorType": "Тип курсора",
|
||||||
"customCmd": "Пользовательские команды",
|
"customCmd": "Пользовательские команды",
|
||||||
"customCmdDocUrl": "https://github.com/lollipopkit/flutter_server_box/wiki#custom-commands",
|
"customCmdDocUrl": "https://github.com/lollipopkit/flutter_server_box/wiki#custom-commands",
|
||||||
"customCmdHint": "\"Имя команды\": \"Команда\"",
|
"customCmdHint": "\"Имя команды\": \"Команда\"",
|
||||||
"dark": "темная",
|
|
||||||
"day": "День",
|
|
||||||
"debug": "отладка",
|
|
||||||
"decode": "декодировать",
|
"decode": "декодировать",
|
||||||
"decompress": "разархивировать",
|
"decompress": "разархивировать",
|
||||||
"delete": "удалить",
|
|
||||||
"deleteScripts": "удалить скрипты с сервера",
|
|
||||||
"deleteServers": "удалить серверы пакетно",
|
"deleteServers": "удалить серверы пакетно",
|
||||||
"deviceName": "Название устройства",
|
|
||||||
"dirEmpty": "Пожалуйста, убедитесь, что папка пуста",
|
"dirEmpty": "Пожалуйста, убедитесь, что папка пуста",
|
||||||
"disabled": "отключено",
|
|
||||||
"disconnected": "отключено",
|
"disconnected": "отключено",
|
||||||
"disk": "диск",
|
"disk": "диск",
|
||||||
"diskIgnorePath": "путь игнорирования диска",
|
"diskIgnorePath": "путь игнорирования диска",
|
||||||
"displayCpuIndex": "Показать индекс ЦПУ",
|
"displayCpuIndex": "Показать индекс ЦПУ",
|
||||||
"displayName": "отображаемое имя",
|
|
||||||
"dl2Local": "Загрузить {fileName} на локальный диск?",
|
"dl2Local": "Загрузить {fileName} на локальный диск?",
|
||||||
"doc": "Документация",
|
|
||||||
"dockerEditHost": "редактировать DOCKER_HOST",
|
|
||||||
"dockerEmptyRunningItems": "Нет запущенных контейнеров.\nЭто может быть из-за:\n- пользователя Docker, отличного от пользователя, настроенного в приложении\n- переменной окружения DOCKER_HOST, которая не была правильно считана. Вы можете выполнить `echo $DOCKER_HOST` в терминале, чтобы увидеть ее значение.",
|
"dockerEmptyRunningItems": "Нет запущенных контейнеров.\nЭто может быть из-за:\n- пользователя Docker, отличного от пользователя, настроенного в приложении\n- переменной окружения DOCKER_HOST, которая не была правильно считана. Вы можете выполнить `echo $DOCKER_HOST` в терминале, чтобы увидеть ее значение.",
|
||||||
"dockerImagesFmt": "Всего {count} образов",
|
"dockerImagesFmt": "Всего {count} образов",
|
||||||
"dockerNotInstalled": "Docker не установлен",
|
"dockerNotInstalled": "Docker не установлен",
|
||||||
@@ -89,77 +44,48 @@
|
|||||||
"dockerStatusRunningFmt": "{count} контейнеров запущено",
|
"dockerStatusRunningFmt": "{count} контейнеров запущено",
|
||||||
"doubleColumnMode": "режим двойной колонки",
|
"doubleColumnMode": "режим двойной колонки",
|
||||||
"doubleColumnTip": "Эта опция лишь включает функцию; фактическое применение зависит от ширины устройства",
|
"doubleColumnTip": "Эта опция лишь включает функцию; фактическое применение зависит от ширины устройства",
|
||||||
"download": "скачать",
|
|
||||||
"edit": "редактировать",
|
|
||||||
"editVirtKeys": "редактировать виртуальные клавиши",
|
"editVirtKeys": "редактировать виртуальные клавиши",
|
||||||
"editor": "редактор",
|
"editor": "редактор",
|
||||||
"editorHighlightTip": "Текущая производительность подсветки кода неудовлетворительна, можно отключить для улучшения.",
|
"editorHighlightTip": "Текущая производительность подсветки кода неудовлетворительна, можно отключить для улучшения.",
|
||||||
"encode": "кодировать",
|
"encode": "кодировать",
|
||||||
"error": "ошибка",
|
"envVars": "Переменная окружения",
|
||||||
"exampleName": "пример имени",
|
|
||||||
"experimentalFeature": "экспериментальная функция",
|
"experimentalFeature": "экспериментальная функция",
|
||||||
"export": "экспорт",
|
|
||||||
"extraArgs": "дополнительные аргументы",
|
"extraArgs": "дополнительные аргументы",
|
||||||
"failed": "неудача",
|
"fallbackSshDest": "Резервное место назначения SSH",
|
||||||
"fdroidReleaseTip": "Если вы скачали это приложение с Fdroid, рекомендуется отключить эту опцию.",
|
"fdroidReleaseTip": "Если вы скачали это приложение с F-Droid, рекомендуется отключить эту опцию.",
|
||||||
"feedback": "обратная связь",
|
|
||||||
"feedbackOnGithub": "Если у вас есть какие-либо вопросы, пожалуйста, отправьте отзыв на GitHub",
|
|
||||||
"fieldMustNotEmpty": "Эти поля не могут быть пустыми.",
|
|
||||||
"fileNotExist": "{file} не существует",
|
|
||||||
"fileTooLarge": "Файл '{file}' слишком большой '{size}', превышает {sizeMax}",
|
"fileTooLarge": "Файл '{file}' слишком большой '{size}', превышает {sizeMax}",
|
||||||
"files": "файлы",
|
|
||||||
"finished": "завершено",
|
|
||||||
"followSystem": "следовать за системой",
|
"followSystem": "следовать за системой",
|
||||||
"font": "шрифт",
|
"font": "шрифт",
|
||||||
"fontSize": "размер шрифта",
|
"fontSize": "размер шрифта",
|
||||||
"forExample": "Например",
|
|
||||||
"force": "принудительно",
|
"force": "принудительно",
|
||||||
"foundNUpdate": "Найдено {count} обновлений",
|
|
||||||
"fullScreen": "полноэкранный режим",
|
"fullScreen": "полноэкранный режим",
|
||||||
"fullScreenJitter": "дрожание в полноэкранном режиме",
|
"fullScreenJitter": "дрожание в полноэкранном режиме",
|
||||||
"fullScreenJitterHelp": "предотвращение выгорания экрана",
|
"fullScreenJitterHelp": "предотвращение выгорания экрана",
|
||||||
"fullScreenTip": "Следует ли включить полноэкранный режим, когда устройство поворачивается в альбомный режим? Эта опция применяется только к вкладке сервера.",
|
"fullScreenTip": "Следует ли включить полноэкранный режим, когда устройство поворачивается в альбомный режим? Эта опция применяется только к вкладке сервера.",
|
||||||
"getPushTokenFailed": "Не удалось получить токен уведомлений",
|
|
||||||
"gettingToken": "Получение токена...",
|
|
||||||
"goBackQ": "Вернуться?",
|
"goBackQ": "Вернуться?",
|
||||||
"goto": "перейти к",
|
"goto": "перейти к",
|
||||||
"hideTitleBar": "Скрыть заголовок",
|
"hideTitleBar": "Скрыть заголовок",
|
||||||
"hideTitleBarTip": "После включения удерживайте три кнопки в правом верхнем углу, чтобы перетаскивать.",
|
|
||||||
"highlight": "подсветка кода",
|
"highlight": "подсветка кода",
|
||||||
"homeWidgetUrlConfig": "конфигурация URL виджета домашнего экрана",
|
"homeWidgetUrlConfig": "конфигурация URL виджета домашнего экрана",
|
||||||
"host": "хост",
|
"host": "хост",
|
||||||
"hour": "Час",
|
|
||||||
"httpFailedWithCode": "Ошибка запроса, код: {code}",
|
"httpFailedWithCode": "Ошибка запроса, код: {code}",
|
||||||
"icloudSynced": "Синхронизация с iCloud выполнена, некоторые настройки могут потребовать перезапуска приложения для вступления в силу.",
|
|
||||||
"ignoreCert": "Игнорировать сертификат",
|
"ignoreCert": "Игнорировать сертификат",
|
||||||
"image": "образ",
|
"image": "образ",
|
||||||
"imagesList": "список образов",
|
"imagesList": "список образов",
|
||||||
"import": "импорт",
|
|
||||||
"inAppUpdate": "Обновить в приложении? В противном случае загрузите с помощью браузера.",
|
|
||||||
"init": "Инициализировать",
|
"init": "Инициализировать",
|
||||||
"inner": "встроенный",
|
"inner": "встроенный",
|
||||||
"inputDomainHere": "введите домен здесь",
|
|
||||||
"install": "установить",
|
"install": "установить",
|
||||||
"installDockerWithUrl": "Пожалуйста, сначала установите Docker по адресу https://docs.docker.com/engine/install",
|
"installDockerWithUrl": "Пожалуйста, сначала установите Docker по адресу https://docs.docker.com/engine/install",
|
||||||
"invalid": "Недействительный",
|
"invalid": "Недействительный",
|
||||||
"invalidJson": "неверный JSON",
|
|
||||||
"invalidVersion": "неподдерживаемая версия",
|
|
||||||
"invalidVersionHelp": "Пожалуйста, убедитесь, что docker установлен корректно, или что используется не собственно собранная версия. Если проблема не в этом, пожалуйста, отправьте отчет о проблеме на {url}.",
|
|
||||||
"isBusy": "занято",
|
|
||||||
"jumpServer": "прыжковый сервер",
|
"jumpServer": "прыжковый сервер",
|
||||||
"keepForeground": "Пожалуйста, держите приложение в фокусе!",
|
"keepForeground": "Пожалуйста, держите приложение в фокусе!",
|
||||||
"keepStatusWhenErr": "Сохранять статус сервера при ошибке",
|
"keepStatusWhenErr": "Сохранять статус сервера при ошибке",
|
||||||
"keepStatusWhenErrTip": "Применимо только в случае ошибки выполнения скрипта",
|
"keepStatusWhenErrTip": "Применимо только в случае ошибки выполнения скрипта",
|
||||||
"keyAuth": "аутентификация по ключу",
|
"keyAuth": "аутентификация по ключу",
|
||||||
"language": "язык",
|
"letterCache": "Кэширование букв",
|
||||||
"languageName": "Русский",
|
"letterCacheTip": "Рекомендуется отключить, но после отключения будет невозможно вводить символы CJK.",
|
||||||
"lastTry": "последняя попытка",
|
|
||||||
"launchPage": "стартовая страница",
|
|
||||||
"license": "лицензия",
|
"license": "лицензия",
|
||||||
"light": "светлая",
|
|
||||||
"loadingFiles": "Загрузка файлов...",
|
|
||||||
"location": "местоположение",
|
"location": "местоположение",
|
||||||
"log": "лог",
|
|
||||||
"loss": "потери пакетов",
|
"loss": "потери пакетов",
|
||||||
"madeWithLove": "Создано с ❤️ by {myGithub}",
|
"madeWithLove": "Создано с ❤️ by {myGithub}",
|
||||||
"manual": "вручную",
|
"manual": "вручную",
|
||||||
@@ -167,60 +93,36 @@
|
|||||||
"maxRetryCount": "максимальное количество попыток переподключения к серверу",
|
"maxRetryCount": "максимальное количество попыток переподключения к серверу",
|
||||||
"maxRetryCountEqual0": "будет бесконечно пытаться переподключиться",
|
"maxRetryCountEqual0": "будет бесконечно пытаться переподключиться",
|
||||||
"min": "минимум",
|
"min": "минимум",
|
||||||
"minute": "Минута",
|
|
||||||
"mission": "задача",
|
"mission": "задача",
|
||||||
"more": "больше",
|
"more": "больше",
|
||||||
"moveOutServerFuncBtnsHelp": "Включено: кнопки функций сервера отображаются под каждой карточкой на вкладке сервера. Выключено: отображается в верхней части страницы деталей сервера.",
|
"moveOutServerFuncBtnsHelp": "Включено: кнопки функций сервера отображаются под каждой карточкой на вкладке сервера. Выключено: отображается в верхней части страницы деталей сервера.",
|
||||||
"ms": "мс",
|
"ms": "мс",
|
||||||
"name": "имя",
|
|
||||||
"needHomeDir": "Если вы пользователь Synology, [смотрите здесь](https://kb.synology.com/DSM/tutorial/user_enable_home_service). Пользователям других систем нужно искать, как создать домашний каталог.",
|
"needHomeDir": "Если вы пользователь Synology, [смотрите здесь](https://kb.synology.com/DSM/tutorial/user_enable_home_service). Пользователям других систем нужно искать, как создать домашний каталог.",
|
||||||
"needRestart": "требуется перезапуск приложения",
|
"needRestart": "требуется перезапуск приложения",
|
||||||
"net": "сеть",
|
"net": "сеть",
|
||||||
"netViewType": "тип визуализации сети",
|
"netViewType": "тип визуализации сети",
|
||||||
"newContainer": "создать контейнер",
|
"newContainer": "создать контейнер",
|
||||||
"noClient": "нет SSH соединения",
|
|
||||||
"noInterface": "нет доступных интерфейсов",
|
|
||||||
"noLineChart": "Не использовать линейные графики",
|
"noLineChart": "Не использовать линейные графики",
|
||||||
"noNotiPerm": "Нет разрешения на уведомления, возможно отсутствие индикации прогресса при загрузке обновлений приложений.",
|
"noLineChartForCpu": "Не используйте линейные графики для ЦП",
|
||||||
"noOptions": "нет доступных опций",
|
|
||||||
"noPrivateKeyTip": "Приватный ключ не существует, возможно, он был удален или есть ошибка в настройках.",
|
"noPrivateKeyTip": "Приватный ключ не существует, возможно, он был удален или есть ошибка в настройках.",
|
||||||
"noPromptAgain": "Больше не спрашивать",
|
"noPromptAgain": "Больше не спрашивать",
|
||||||
"noResult": "нет результатов",
|
|
||||||
"noSavedPrivateKey": "Нет сохраненных приватных ключей.",
|
|
||||||
"noSavedSnippet": "Нет сохраненных фрагментов кода.",
|
|
||||||
"noServerAvailable": "Нет доступных серверов.",
|
|
||||||
"noTask": "нет задач",
|
|
||||||
"noUpdateAvailable": "нет доступных обновлений",
|
|
||||||
"node": "Узел",
|
"node": "Узел",
|
||||||
"notAvailable": "Недоступно",
|
"notAvailable": "Недоступно",
|
||||||
"notSelected": "не выбрано",
|
|
||||||
"note": "заметка",
|
|
||||||
"nullToken": "нет токена",
|
|
||||||
"ok": "ок",
|
|
||||||
"onServerDetailPage": "на странице деталей сервера",
|
"onServerDetailPage": "на странице деталей сервера",
|
||||||
"onlyOneLine": "Отображать только в одной строке (прокручиваемо)",
|
"onlyOneLine": "Отображать только в одной строке (прокручиваемо)",
|
||||||
"onlyWhenCoreBiggerThan8": "Действует только при количестве ядер > 8",
|
"onlyWhenCoreBiggerThan8": "Действует только при количестве ядер > 8",
|
||||||
"open": "открыть",
|
|
||||||
"openLastPath": "открыть последний путь",
|
"openLastPath": "открыть последний путь",
|
||||||
"openLastPathTip": "Для разных серверов будут сохранены разные записи, записывается путь при выходе",
|
"openLastPathTip": "Для разных серверов будут сохранены разные записи, записывается путь при выходе",
|
||||||
"parseContainerStats": "анализ статуса использования контейнера",
|
|
||||||
"parseContainerStatsTip": "Анализ статуса использования Docker может быть медленным",
|
"parseContainerStatsTip": "Анализ статуса использования Docker может быть медленным",
|
||||||
"paste": "вставить",
|
|
||||||
"path": "путь",
|
|
||||||
"percentOfSize": "{percent}% от {size}",
|
"percentOfSize": "{percent}% от {size}",
|
||||||
"pickFile": "выбрать файл",
|
"permission": "Разрешения",
|
||||||
"pingAvg": "Среднее:",
|
"pingAvg": "Среднее:",
|
||||||
"pingInputIP": "Пожалуйста, введите целевой IP или доменное имя",
|
"pingInputIP": "Пожалуйста, введите целевой IP или доменное имя",
|
||||||
"pingNoServer": "Нет доступных серверов для Ping\nПожалуйста, добавьте серверы на вкладке серверов и попробуйте снова",
|
"pingNoServer": "Нет доступных серверов для Ping\nПожалуйста, добавьте серверы на вкладке серверов и попробуйте снова",
|
||||||
"pkg": "менеджер пакетов",
|
"pkg": "менеджер пакетов",
|
||||||
"pkgUpgradeTip": "Пожалуйста, сделайте резервную копию системы перед обновлением.",
|
|
||||||
"platformNotSupportUpdate": "Текущая платформа не поддерживает обновления, пожалуйста, вручную установите последнюю версию из исходного кода",
|
|
||||||
"plugInType": "Тип вставки",
|
"plugInType": "Тип вставки",
|
||||||
"plzEnterHost": "Пожалуйста, введите хост",
|
|
||||||
"plzSelectKey": "Пожалуйста, выберите ключ",
|
|
||||||
"port": "порт",
|
"port": "порт",
|
||||||
"preview": "предпросмотр",
|
"preview": "предпросмотр",
|
||||||
"primaryColorSeed": "основной цветовой тон",
|
|
||||||
"privateKey": "приватный ключ",
|
"privateKey": "приватный ключ",
|
||||||
"process": "процесс",
|
"process": "процесс",
|
||||||
"pushToken": "токен уведомлений",
|
"pushToken": "токен уведомлений",
|
||||||
@@ -230,16 +132,11 @@
|
|||||||
"pwd": "пароль",
|
"pwd": "пароль",
|
||||||
"read": "чтение",
|
"read": "чтение",
|
||||||
"reboot": "перезагрузка",
|
"reboot": "перезагрузка",
|
||||||
"rememberChoice": "Запомнить выбор",
|
|
||||||
"rememberPwdInMem": "Запомнить пароль в памяти",
|
"rememberPwdInMem": "Запомнить пароль в памяти",
|
||||||
"rememberPwdInMemTip": "Используется для контейнеров, приостановки и т. д.",
|
"rememberPwdInMemTip": "Используется для контейнеров, приостановки и т. д.",
|
||||||
"rememberWindowSize": "Запомнить размер окна",
|
"rememberWindowSize": "Запомнить размер окна",
|
||||||
"remotePath": "удаленный путь",
|
"remotePath": "удаленный путь",
|
||||||
"rename": "переименовать",
|
|
||||||
"reportBugsOnGithubIssue": "Пожалуйста, сообщайте о проблемах на {url}",
|
|
||||||
"restart": "перезапустить",
|
"restart": "перезапустить",
|
||||||
"restore": "восстановить",
|
|
||||||
"restoreSuccess": "Восстановление успешно, требуется перезапуск приложения для применения изменений",
|
|
||||||
"result": "результат",
|
"result": "результат",
|
||||||
"rotateAngel": "угол поворота",
|
"rotateAngel": "угол поворота",
|
||||||
"route": "Маршрутизация",
|
"route": "Маршрутизация",
|
||||||
@@ -254,14 +151,8 @@
|
|||||||
"serverDetailOrder": "порядок элементов на странице деталей сервера",
|
"serverDetailOrder": "порядок элементов на странице деталей сервера",
|
||||||
"serverFuncBtns": "кнопки функций сервера",
|
"serverFuncBtns": "кнопки функций сервера",
|
||||||
"serverOrder": "порядок серверов",
|
"serverOrder": "порядок серверов",
|
||||||
"serverTabConnecting": "подключение...",
|
|
||||||
"serverTabEmpty": "Серверов пока нет.\nНажмите кнопку в правом нижнем углу, чтобы добавить.",
|
|
||||||
"serverTabFailed": "ошибка",
|
|
||||||
"serverTabLoading": "загрузка...",
|
|
||||||
"serverTabPlzSave": "Пожалуйста, сохраните этот приватный ключ еще раз",
|
|
||||||
"serverTabUnkown": "неизвестно",
|
|
||||||
"setting": "настройки",
|
|
||||||
"sftpDlPrepare": "Подготовка к подключению к серверу...",
|
"sftpDlPrepare": "Подготовка к подключению к серверу...",
|
||||||
|
"sftpEditorTip": "Если пусто, используйте встроенный редактор файлов приложения. Если значение указано, используйте редактор удаленного сервера, например, `vim` (рекомендуется автоматически определять согласно `EDITOR`).",
|
||||||
"sftpRmrDirSummary": "Использовать `rm -r` в SFTP для удаления папок",
|
"sftpRmrDirSummary": "Использовать `rm -r` в SFTP для удаления папок",
|
||||||
"sftpSSHConnected": "SFTP подключен...",
|
"sftpSSHConnected": "SFTP подключен...",
|
||||||
"sftpShowFoldersFirst": "показывать папки в начале",
|
"sftpShowFoldersFirst": "показывать папки в начале",
|
||||||
@@ -270,17 +161,19 @@
|
|||||||
"size": "размер",
|
"size": "размер",
|
||||||
"snippet": "фрагмент",
|
"snippet": "фрагмент",
|
||||||
"softWrap": "Мягкий перенос",
|
"softWrap": "Мягкий перенос",
|
||||||
|
"specifyDev": "Указать устройство",
|
||||||
|
"specifyDevTip": "Например, статистика сетевого трафика по умолчанию относится ко всем устройствам. Здесь вы можете указать конкретное устройство.",
|
||||||
"speed": "скорость",
|
"speed": "скорость",
|
||||||
"spentTime": "Затрачено времени: {time}",
|
"spentTime": "Затрачено времени: {time}",
|
||||||
"sshTermHelp": "Когда терминал можно прокручивать, горизонтальное перетаскивание позволяет выделить текст. Нажатие на кнопку клавиатуры включает/выключает клавиатуру. Иконка файла открывает текущий путь SFTP. Кнопка буфера обмена копирует содержимое, когда текст выделен, и вставляет содержимое из буфера обмена в терминал, когда текст не выделен, а в буфере есть содержимое. Иконка кода вставляет фрагменты кода в терминал и выполняет их.",
|
"sshTermHelp": "Когда терминал можно прокручивать, горизонтальное перетаскивание позволяет выделить текст. Нажатие на кнопку клавиатуры включает/выключает клавиатуру. Иконка файла открывает текущий путь SFTP. Кнопка буфера обмена копирует содержимое, когда текст выделен, и вставляет содержимое из буфера обмена в терминал, когда текст не выделен, а в буфере есть содержимое. Иконка кода вставляет фрагменты кода в терминал и выполняет их.",
|
||||||
"sshTip": "Эта функция находится в стадии тестирования.\n\nПожалуйста, отправляйте отчеты о проблемах на {url} или присоединяйтесь к нашей разработке.",
|
"sshTip": "Эта функция находится в стадии тестирования.\n\nПожалуйста, отправляйте отчеты о проблемах на {url} или присоединяйтесь к нашей разработке.",
|
||||||
"sshVirtualKeyAutoOff": "автоматическое отключение виртуальных клавиш",
|
"sshVirtualKeyAutoOff": "автоматическое отключение виртуальных клавиш",
|
||||||
"start": "старт",
|
"start": "старт",
|
||||||
|
"stat": "Статистика",
|
||||||
"stats": "статистика",
|
"stats": "статистика",
|
||||||
"stop": "остановить",
|
"stop": "остановить",
|
||||||
"stopped": "остановлено",
|
"stopped": "остановлено",
|
||||||
"storage": "Хранение",
|
"storage": "Хранение",
|
||||||
"success": "успех",
|
|
||||||
"supportFmtArgs": "Поддерживаются следующие форматы аргументов:",
|
"supportFmtArgs": "Поддерживаются следующие форматы аргументов:",
|
||||||
"suspend": "приостановить",
|
"suspend": "приостановить",
|
||||||
"suspendTip": "Функция приостановки требует прав root и поддержки systemd.",
|
"suspendTip": "Функция приостановки требует прав root и поддержки systemd.",
|
||||||
@@ -296,35 +189,25 @@
|
|||||||
"textScaler": "масштабирование текста",
|
"textScaler": "масштабирование текста",
|
||||||
"textScalerTip": "1.0 => 100% (исходный размер), применяется только к части шрифтов на странице сервера, изменение не рекомендуется.",
|
"textScalerTip": "1.0 => 100% (исходный размер), применяется только к части шрифтов на странице сервера, изменение не рекомендуется.",
|
||||||
"theme": "тема",
|
"theme": "тема",
|
||||||
"themeMode": "режим темы",
|
|
||||||
"time": "время",
|
"time": "время",
|
||||||
"times": "раз",
|
"times": "раз",
|
||||||
"total": "всего",
|
"total": "всего",
|
||||||
"traffic": "трафик",
|
"traffic": "трафик",
|
||||||
"trySudo": "попробовать использовать sudo",
|
"trySudo": "попробовать использовать sudo",
|
||||||
"ttl": "время жизни кеша",
|
"ttl": "TTL",
|
||||||
"unknown": "неизвестно",
|
"unknown": "неизвестно",
|
||||||
"unknownError": "неизвестная ошибка",
|
|
||||||
"unkownConvertMode": "неизвестный режим конвертации",
|
"unkownConvertMode": "неизвестный режим конвертации",
|
||||||
"update": "обновление",
|
"update": "обновление",
|
||||||
"updateAll": "обновить все",
|
|
||||||
"updateIntervalEqual0": "Если установлено в 0, статус сервера не будет автоматически обновляться.\nТакже не будет рассчитано использование CPU.",
|
"updateIntervalEqual0": "Если установлено в 0, статус сервера не будет автоматически обновляться.\nТакже не будет рассчитано использование CPU.",
|
||||||
"updateServerStatusInterval": "интервал обновления статуса сервера",
|
"updateServerStatusInterval": "интервал обновления статуса сервера",
|
||||||
"updateTip": "Новая версия: v1.0.{newest}",
|
|
||||||
"updateTipTooLow": "Текущая версия слишком старая, пожалуйста, обновитесь до v1.0.{newest}",
|
|
||||||
"upload": "загрузить",
|
"upload": "загрузить",
|
||||||
"upsideDown": "перевернуть",
|
"upsideDown": "перевернуть",
|
||||||
"uptime": "время работы",
|
"uptime": "время работы",
|
||||||
"urlOrJson": "ссылка или JSON",
|
|
||||||
"useCdn": "Использование CDN",
|
"useCdn": "Использование CDN",
|
||||||
"useCdnTip": "Не китайским пользователям рекомендуется использовать CDN. Хотели бы вы его использовать?",
|
"useCdnTip": "Не китайским пользователям рекомендуется использовать CDN. Хотели бы вы его использовать?",
|
||||||
"useNoPwd": "будет использоваться без пароля",
|
"useNoPwd": "будет использоваться без пароля",
|
||||||
"usePodmanByDefault": "использовать Podman по умолчанию",
|
"usePodmanByDefault": "использовать Podman по умолчанию",
|
||||||
"used": "использовано",
|
"used": "использовано",
|
||||||
"user": "пользователь",
|
|
||||||
"versionHaveUpdate": "Найдена новая версия: v1.0.{build}, нажмите для обновления",
|
|
||||||
"versionUnknownUpdate": "Текущая: v1.0.{build}, нажмите для проверки обновлений",
|
|
||||||
"versionUpdated": "Текущая: v1.0.{build}, последняя версия",
|
|
||||||
"view": "Вид",
|
"view": "Вид",
|
||||||
"viewErr": "просмотр ошибок",
|
"viewErr": "просмотр ошибок",
|
||||||
"virtKeyHelpClipboard": "Если в терминале выделен текст, то он копируется в буфер обмена, в противном случае содержимое буфера вставляется в терминал.",
|
"virtKeyHelpClipboard": "Если в терминале выделен текст, то он копируется в буфер обмена, в противном случае содержимое буфера вставляется в терминал.",
|
||||||
@@ -335,8 +218,8 @@
|
|||||||
"watchNotPaired": "Apple Watch не сопряжены",
|
"watchNotPaired": "Apple Watch не сопряжены",
|
||||||
"webdavSettingEmpty": "Настройки Webdav пусты",
|
"webdavSettingEmpty": "Настройки Webdav пусты",
|
||||||
"whenOpenApp": "при открытии приложения",
|
"whenOpenApp": "при открытии приложения",
|
||||||
"willTakEeffectImmediately": "Изменения вступят в силу немедленно",
|
|
||||||
"wolTip": "После настройки WOL (Wake-on-LAN) при каждом подключении к серверу отправляется запрос WOL.",
|
"wolTip": "После настройки WOL (Wake-on-LAN) при каждом подключении к серверу отправляется запрос WOL.",
|
||||||
"write": "запись",
|
"write": "запись",
|
||||||
"writeScriptFailTip": "Запись в скрипт не удалась, возможно, из-за отсутствия прав или директории не существует."
|
"writeScriptFailTip": "Запись в скрипт не удалась, возможно, из-за отсутствия прав или директории не существует.",
|
||||||
|
"writeScriptTip": "После подключения к серверу скрипт будет записан в ~/.config/server_box для мониторинга состояния системы. Вы можете проверить содержимое скрипта."
|
||||||
}
|
}
|
||||||
225
lib/l10n/app_tr.arb
Normal file
@@ -0,0 +1,225 @@
|
|||||||
|
{
|
||||||
|
"@@locale": "tr",
|
||||||
|
"aboutThanks": "Katılım gösteren aşağıdaki kişilere teşekkür ederiz.",
|
||||||
|
"acceptBeta": "Beta sürüm güncellemelerini kabul et",
|
||||||
|
"addSystemPrivateKeyTip": "Şu anda özel anahtar yok, sistemle geleni (~/.ssh/id_rsa) eklemek ister misiniz?",
|
||||||
|
"added2List": "Görev listesine eklendi",
|
||||||
|
"addr": "Adres",
|
||||||
|
"alreadyLastDir": "Zaten son klasörde.",
|
||||||
|
"authFailTip": "Kimlik doğrulama başarısız, kimlik bilgilerinin doğru olup olmadığını kontrol edin",
|
||||||
|
"autoBackupConflict": "Aynı anda yalnızca bir otomatik yedekleme etkinleştirilebilir.",
|
||||||
|
"autoConnect": "Otomatik bağlan",
|
||||||
|
"autoRun": "Otomatik çalıştır",
|
||||||
|
"autoUpdateHomeWidget": "Ana widget'ı otomatik güncelle",
|
||||||
|
"backupTip": "Dışa aktarılan veriler zayıf bir şekilde şifrelenmiştir. \nLütfen güvenli bir yerde saklayın.",
|
||||||
|
"backupVersionNotMatch": "Yedekleme sürümü uyumlu değil.",
|
||||||
|
"battery": "Pil",
|
||||||
|
"bgRun": "Arka planda çalıştır",
|
||||||
|
"bgRunTip": "Bu anahtar yalnızca programın arka planda çalışmayı deneyeceğini ifade eder. Arka planda çalışıp çalışamayacağı, iznin etkinleştirilip etkinleştirilmediğine bağlıdır. AOSP tabanlı Android ROM'larda, bu uygulamada \"Pil Optimizasyonunu\" devre dışı bırakın. MIUI / HyperOS için, güç tasarrufu politikasını \"Sınırsız\" olarak değiştirin.",
|
||||||
|
"cmd": "Komut",
|
||||||
|
"collapseUITip": "UI'daki uzun listeleri varsayılan olarak gizleyip gizlememeyi belirler",
|
||||||
|
"conn": "Bağlantı",
|
||||||
|
"container": "Konteyner",
|
||||||
|
"containerTrySudoTip": "Örneğin: Uygulamada kullanıcı aaa olarak ayarlanmış, ancak Docker root kullanıcısı altında kurulmuş. Bu durumda, bu seçeneği etkinleştirmeniz gerekir.",
|
||||||
|
"convert": "Dönüştür",
|
||||||
|
"copyPath": "Yolu kopyala",
|
||||||
|
"cpuViewAsProgressTip": "Her CPU'nun kullanımını bir ilerleme çubuğu tarzında görüntüle (eski tarz)",
|
||||||
|
"cursorType": "İmleç türü",
|
||||||
|
"customCmd": "Özel komutlar",
|
||||||
|
"customCmdDocUrl": "https://github.com/lollipopkit/flutter_server_box/wiki#custom-commands",
|
||||||
|
"customCmdHint": "\"Komut Adı\": \"Komut\"",
|
||||||
|
"decode": "Çöz",
|
||||||
|
"decompress": "Sıkıştırmayı aç",
|
||||||
|
"deleteServers": "Toplu sunucu silme",
|
||||||
|
"dirEmpty": "Klasörün boş olduğundan emin olun.",
|
||||||
|
"disconnected": "Bağlantı kesildi",
|
||||||
|
"disk": "Disk",
|
||||||
|
"diskIgnorePath": "Disk için göz ardı edilen yol",
|
||||||
|
"displayCpuIndex": "CPU dizinini görüntüle",
|
||||||
|
"dl2Local": "{fileName} dosyasını yerel olarak indirmek istiyor musunuz?",
|
||||||
|
"dockerEmptyRunningItems": "Çalışan konteyner yok.\nBu şu sebeplerden kaynaklanabilir:\n- Docker kurulumu kullanıcı adı, uygulamada yapılandırılan kullanıcı adıyla aynı değil.\n- DOCKER_HOST ortam değişkeni doğru okunmadı. Terminalde `echo $DOCKER_HOST` komutunu çalıştırarak elde edebilirsiniz.",
|
||||||
|
"dockerImagesFmt": "{count} görüntü",
|
||||||
|
"dockerNotInstalled": "Docker kurulu değil",
|
||||||
|
"dockerStatusRunningAndStoppedFmt": "{runningCount} çalışıyor, {stoppedCount} konteyner durduruldu.",
|
||||||
|
"dockerStatusRunningFmt": "{count} konteyner çalışıyor.",
|
||||||
|
"doubleColumnMode": "Çift sütun modu",
|
||||||
|
"doubleColumnTip": "Bu seçenek yalnızca özelliği etkinleştirir, gerçekten etkinleştirilebilir olup olmadığını cihazın genişliği belirler",
|
||||||
|
"editVirtKeys": "Sanal tuşları düzenle",
|
||||||
|
"editor": "Editör",
|
||||||
|
"editorHighlightTip": "Mevcut kod vurgulama performansı ideal değil ve isteğe bağlı olarak kapatılabilir.",
|
||||||
|
"encode": "Kodla",
|
||||||
|
"envVars": "Ortam değişkeni",
|
||||||
|
"experimentalFeature": "Deneysel özellik",
|
||||||
|
"extraArgs": "Ek argümanlar",
|
||||||
|
"fallbackSshDest": "Yedek SSH hedefi",
|
||||||
|
"fdroidReleaseTip": "Bu uygulamayı F-Droid'den indirdiyseniz, bu seçeneği kapatmanız önerilir.",
|
||||||
|
"fileTooLarge": "'{file}' dosyası çok büyük {size}, maksimum {sizeMax}",
|
||||||
|
"followSystem": "Sistemi takip et",
|
||||||
|
"font": "Yazı tipi",
|
||||||
|
"fontSize": "Yazı tipi boyutu",
|
||||||
|
"force": "Zorla",
|
||||||
|
"fullScreen": "Tam ekran modu",
|
||||||
|
"fullScreenJitter": "Tam ekran titremesi",
|
||||||
|
"fullScreenJitterHelp": "Ekran yanıklarını önlemek için",
|
||||||
|
"fullScreenTip": "Cihaz yatay moda döndürüldüğünde tam ekran modu etkinleştirilsin mi? Bu seçenek yalnızca sunucu sekmesi için geçerlidir.",
|
||||||
|
"goBackQ": "Geri dön?",
|
||||||
|
"goto": "Git",
|
||||||
|
"hideTitleBar": "Başlık çubuğunu gizle",
|
||||||
|
"highlight": "Kod vurgulama",
|
||||||
|
"homeWidgetUrlConfig": "Ana sayfa widget URL'sini yapılandır",
|
||||||
|
"host": "Sunucu",
|
||||||
|
"httpFailedWithCode": "İstek başarısız oldu, durum kodu: {code}",
|
||||||
|
"ignoreCert": "Sertifikayı yoksay",
|
||||||
|
"image": "Resim",
|
||||||
|
"imagesList": "Resim listesi",
|
||||||
|
"init": "Başlat",
|
||||||
|
"inner": "İç",
|
||||||
|
"install": "Kur",
|
||||||
|
"installDockerWithUrl": "Lütfen önce Docker'ı https://docs.docker.com/engine/install adresinden kurun.",
|
||||||
|
"invalid": "Geçersiz",
|
||||||
|
"jumpServer": "Atlama sunucusu",
|
||||||
|
"keepForeground": "Uygulama ön planda kalsın!",
|
||||||
|
"keepStatusWhenErr": "Son sunucu durumunu koru",
|
||||||
|
"keepStatusWhenErrTip": "Yalnızca betik yürütme sırasında bir hata oluştuğunda",
|
||||||
|
"keyAuth": "Anahtar Doğrulama",
|
||||||
|
"letterCache": "Harf önbelleği",
|
||||||
|
"letterCacheTip": "Devre dışı bırakılması önerilir, ancak devre dışı bırakıldıktan sonra CJK karakterleri girilemez.",
|
||||||
|
"license": "Lisans",
|
||||||
|
"location": "Konum",
|
||||||
|
"loss": "kayıp",
|
||||||
|
"madeWithLove": "{myGithub} tarafından ❤️ ile yapıldı",
|
||||||
|
"manual": "Kılavuz",
|
||||||
|
"max": "maks",
|
||||||
|
"maxRetryCount": "Sunucu yeniden bağlanma sayısı",
|
||||||
|
"maxRetryCountEqual0": "Sürekli olarak tekrar denenecek.",
|
||||||
|
"min": "min",
|
||||||
|
"mission": "Görev",
|
||||||
|
"more": "Daha fazla",
|
||||||
|
"moveOutServerFuncBtnsHelp": "Açık: Sunucu sekmesi sayfasındaki her kartın altında görüntülenebilir. Kapalı: Sunucu Detayları sayfasının üst kısmında görüntülenebilir.",
|
||||||
|
"ms": "ms",
|
||||||
|
"needHomeDir": "Synology kullanıcısıysanız, [buraya bakın](https://kb.synology.com/DSM/tutorial/user_enable_home_service). Diğer sistem kullanıcılarının bir ana dizin oluşturmayı öğrenmeleri gerekir.",
|
||||||
|
"needRestart": "Uygulamanın yeniden başlatılması gerekiyor",
|
||||||
|
"net": "Ağ",
|
||||||
|
"netViewType": "Ağ görünümü türü",
|
||||||
|
"newContainer": "Yeni konteyner",
|
||||||
|
"noLineChart": "Çizgi grafik kullanma",
|
||||||
|
"noLineChartForCpu": "CPU için çizgi grafik kullanma",
|
||||||
|
"noPrivateKeyTip": "Özel anahtar mevcut değil, silinmiş olabilir veya bir yapılandırma hatası vardır.",
|
||||||
|
"noPromptAgain": "Tekrar hatırlatma",
|
||||||
|
"node": "Düğüm",
|
||||||
|
"notAvailable": "Kullanılamaz",
|
||||||
|
"onServerDetailPage": "Sunucu detay sayfasında",
|
||||||
|
"onlyOneLine": "Yalnızca bir satır olarak göster (kaydırılabilir)",
|
||||||
|
"onlyWhenCoreBiggerThan8": "Yalnızca çekirdek sayısı 8'den fazla olduğunda çalışır",
|
||||||
|
"openLastPath": "Son yolu aç",
|
||||||
|
"openLastPathTip": "Farklı sunucuların farklı günlükleri olacaktır ve çıkış yolu log dosyasıdır",
|
||||||
|
"parseContainerStatsTip": "Docker'ın işgal durumunu analiz etmek nispeten yavaştır.",
|
||||||
|
"percentOfSize": "{size}'nin %{percent}'i",
|
||||||
|
"permission": "İzinler",
|
||||||
|
"pingAvg": "Ortalama:",
|
||||||
|
"pingInputIP": "Lütfen bir hedef IP / etki alanı girin.",
|
||||||
|
"pingNoServer": "Ping yapılacak sunucu yok.\nLütfen sunucu sekmesine bir sunucu ekleyin.",
|
||||||
|
"pkg": "Paket",
|
||||||
|
"plugInType": "Takma Türü",
|
||||||
|
"port": "Port",
|
||||||
|
"preview": "Önizleme",
|
||||||
|
"privateKey": "Özel Anahtar",
|
||||||
|
"process": "Süreç",
|
||||||
|
"pushToken": "Push token",
|
||||||
|
"pveIgnoreCertTip": "Etkinleştirilmesi önerilmez, güvenlik risklerine dikkat edin! PVE'nin varsayılan sertifikasını kullanıyorsanız, bu seçeneği etkinleştirmeniz gerekir.",
|
||||||
|
"pveLoginFailed": "Giriş başarısız. Linux PAM girişi için sunucu yapılandırmasındaki kullanıcı adı/şifre ile kimlik doğrulaması yapılamadı.",
|
||||||
|
"pveVersionLow": "Bu özellik şu anda test aşamasında ve sadece PVE 8+ üzerinde test edilmiştir. Lütfen dikkatli kullanın.",
|
||||||
|
"pwd": "Şifre",
|
||||||
|
"read": "Oku",
|
||||||
|
"reboot": "Yeniden başlat",
|
||||||
|
"rememberPwdInMem": "Şifreyi bellekte sakla",
|
||||||
|
"rememberPwdInMemTip": "Konteynerler, askıya alma vb. için kullanılır.",
|
||||||
|
"rememberWindowSize": "Pencere boyutunu hatırla",
|
||||||
|
"remotePath": "Uzak yol",
|
||||||
|
"restart": "Yeniden başlat",
|
||||||
|
"result": "Sonuç",
|
||||||
|
"rotateAngel": "Dönme açısı",
|
||||||
|
"route": "Yönlendirme",
|
||||||
|
"run": "Çalıştır",
|
||||||
|
"running": "Çalışıyor",
|
||||||
|
"save": "Kaydet",
|
||||||
|
"saved": "Kaydedildi",
|
||||||
|
"second": "s",
|
||||||
|
"sensors": "Sensör",
|
||||||
|
"sequence": "Dizi",
|
||||||
|
"server": "Sunucu",
|
||||||
|
"serverDetailOrder": "Ayrıntı sayfası widget sırası",
|
||||||
|
"serverFuncBtns": "Sunucu işlev düğmeleri",
|
||||||
|
"serverOrder": "Sunucu sırası",
|
||||||
|
"sftpDlPrepare": "Bağlantı hazırlığı yapılıyor...",
|
||||||
|
"sftpEditorTip": "Boşsa, uygulamanın yerleşik dosya düzenleyicisini kullanın. Bir değer varsa, uzak sunucunun düzenleyicisini kullanın, örneğin, `vim` (otomatik olarak `EDITOR`'a göre algılamanız önerilir).",
|
||||||
|
"sftpRmrDirSummary": "SFTP'de bir klasörü silmek için `rm -r` kullanın.",
|
||||||
|
"sftpSSHConnected": "SFTP Bağlantısı",
|
||||||
|
"sftpShowFoldersFirst": "Önce klasörleri göster",
|
||||||
|
"showDistLogo": "Dağıtım logosunu göster",
|
||||||
|
"shutdown": "Kapat",
|
||||||
|
"size": "Boyut",
|
||||||
|
"snippet": "Parça",
|
||||||
|
"softWrap": "Yumuşak kaydırma",
|
||||||
|
"specifyDev": "Cihazı belirle",
|
||||||
|
"specifyDevTip": "Örneğin, ağ trafiği istatistikleri varsayılan olarak tüm cihazlar içindir. Burada belirli bir cihazı belirtebilirsiniz.",
|
||||||
|
"speed": "Hız",
|
||||||
|
"spentTime": "Harcanan zaman: {time}",
|
||||||
|
"sshTermHelp": "Terminal kaydırılabilir olduğunda, yatay sürükleme metni seçebilir. Klavye düğmesine tıklamak klavyeyi açar/kapatır. Dosya simgesi mevcut yolu SFTP'de açar. Pano düğmesi metin seçildiğinde içeriği kopyalar ve metin seçilmediğinde ve panoda içerik olduğunda panodaki içeriği terminale yapıştırır. Kod simgesi kod parçacıklarını terminale yapıştırır ve çalıştırır.",
|
||||||
|
"sshTip": "Bu işlev şu anda deneme aşamasındadır.\n\nLütfen hataları {url} adresine bildirin veya geliştirmemize katılın.",
|
||||||
|
"sshVirtualKeyAutoOff": "Sanal tuşların otomatik geçişi",
|
||||||
|
"start": "Başlat",
|
||||||
|
"stat": "İstatistik",
|
||||||
|
"stats": "İstatistikler",
|
||||||
|
"stop": "Durdur",
|
||||||
|
"stopped": "Durduruldu",
|
||||||
|
"storage": "Depolama",
|
||||||
|
"supportFmtArgs": "Aşağıdaki biçimlendirme parametreleri desteklenir:",
|
||||||
|
"suspend": "Askıya al",
|
||||||
|
"suspendTip": "Askıya alma işlevi kök izinleri ve systemd desteği gerektirir.",
|
||||||
|
"switchTo": "{val} öğesine geç",
|
||||||
|
"sync": "Senkronize et",
|
||||||
|
"syncTip": "Bazı değişikliklerin etkili olması için yeniden başlatma gerekebilir.",
|
||||||
|
"system": "Sistem",
|
||||||
|
"tag": "Etiketler",
|
||||||
|
"temperature": "Sıcaklık",
|
||||||
|
"termFontSizeTip": "Bu ayar terminal boyutunu (genişlik ve yükseklik) etkileyecektir. Terminal sayfasında yakınlaştırarak mevcut oturumun yazı tipi boyutunu ayarlayabilirsiniz.",
|
||||||
|
"terminal": "Terminal",
|
||||||
|
"test": "Test",
|
||||||
|
"textScaler": "Metin ölçekleyici",
|
||||||
|
"textScalerTip": "1.0 => %100 (orijinal boyut), yalnızca sunucu sayfası kısmındaki yazı tipine çalışır, değiştirilmesi önerilmez.",
|
||||||
|
"theme": "Tema",
|
||||||
|
"time": "Zaman",
|
||||||
|
"times": "Zamanlar",
|
||||||
|
"total": "Toplam",
|
||||||
|
"traffic": "Trafik",
|
||||||
|
"trySudo": "Sudo kullanmayı deneyin",
|
||||||
|
"ttl": "TTL",
|
||||||
|
"unknown": "Bilinmeyen",
|
||||||
|
"unkownConvertMode": "Bilinmeyen dönüştürme modu",
|
||||||
|
"update": "Güncelle",
|
||||||
|
"updateIntervalEqual0": "0 olarak ayarladınız, otomatik olarak güncellenmeyecek.\nCPU durumunu hesaplayamıyor.",
|
||||||
|
"updateServerStatusInterval": "Sunucu durumu güncelleme aralığı",
|
||||||
|
"upload": "Yükle",
|
||||||
|
"upsideDown": "Ters",
|
||||||
|
"uptime": "Çalışma süresi",
|
||||||
|
"useCdn": "CDN kullanılıyor",
|
||||||
|
"useCdnTip": "Çin dışındaki kullanıcıların CDN kullanması önerilir. Kullanmak ister misiniz?",
|
||||||
|
"useNoPwd": "Şifre kullanılmayacak",
|
||||||
|
"usePodmanByDefault": "Varsayılan olarak Podman kullan",
|
||||||
|
"used": "Kullanıldı",
|
||||||
|
"view": "Görünüm",
|
||||||
|
"viewErr": "Hataya bakın",
|
||||||
|
"virtKeyHelpClipboard": "Seçilen terminal boş değilse panoya kopyalayın, aksi takdirde panodaki içeriği terminale yapıştırın.",
|
||||||
|
"virtKeyHelpIME": "Klavye aç/kapat",
|
||||||
|
"virtKeyHelpSFTP": "Geçerli dizini SFTP'de açın.",
|
||||||
|
"waitConnection": "Lütfen bağlantının kurulmasını bekleyin.",
|
||||||
|
"wakeLock": "Uyanık tut",
|
||||||
|
"watchNotPaired": "Eşlenmiş Apple Watch yok",
|
||||||
|
"webdavSettingEmpty": "WebDav ayarı boş",
|
||||||
|
"whenOpenApp": "Uygulamayı açarken",
|
||||||
|
"wolTip": "WOL (Wake-on-LAN) yapılandırıldıktan sonra, her sunucuya bağlandığınızda bir WOL isteği gönderilir.",
|
||||||
|
"write": "Yaz",
|
||||||
|
"writeScriptFailTip": "Komut dosyasına yazma başarısız oldu, muhtemelen izin eksikliğinden veya dizin mevcut olmadığından kaynaklanıyor olabilir.",
|
||||||
|
"writeScriptTip": "Sunucuya bağlandıktan sonra, sistem durumunu izlemek için ~/.config/server_box'a bir komut dosyası yazılacaktır. Komut dosyası içeriğini inceleyebilirsiniz."
|
||||||
|
}
|
||||||
@@ -1,165 +1,91 @@
|
|||||||
{
|
{
|
||||||
"@@locale": "zh",
|
"@@locale": "zh",
|
||||||
"about": "关于",
|
|
||||||
"aboutThanks": "感谢以下参与的各位。",
|
"aboutThanks": "感谢以下参与的各位。",
|
||||||
"acceptBeta": "接受测试版更新推送",
|
"acceptBeta": "接受测试版更新推送",
|
||||||
"add": "新增",
|
|
||||||
"addAServer": "添加服务器",
|
|
||||||
"addPrivateKey": "添加一个私钥",
|
|
||||||
"addSystemPrivateKeyTip": "当前没有任何私钥,是否添加系统自带的(~/.ssh/id_rsa)?",
|
"addSystemPrivateKeyTip": "当前没有任何私钥,是否添加系统自带的(~/.ssh/id_rsa)?",
|
||||||
"added2List": "已添加至任务列表",
|
"added2List": "已添加至任务列表",
|
||||||
"addr": "地址",
|
"addr": "地址",
|
||||||
"all": "所有",
|
|
||||||
"alreadyLastDir": "已经是最上层目录了",
|
"alreadyLastDir": "已经是最上层目录了",
|
||||||
"alterUrl": "备选链接",
|
|
||||||
"askContinue": "{msg},继续吗?",
|
|
||||||
"attention": "注意",
|
|
||||||
"authFailTip": "认证失败,请检查密码/密钥/主机/用户等是否错误",
|
"authFailTip": "认证失败,请检查密码/密钥/主机/用户等是否错误",
|
||||||
"authRequired": "需要认证",
|
|
||||||
"auto": "自动",
|
|
||||||
"autoBackupConflict": "只能同时开启一个自动备份",
|
"autoBackupConflict": "只能同时开启一个自动备份",
|
||||||
"autoCheckUpdate": "自动检查更新",
|
|
||||||
"autoConnect": "自动连接",
|
"autoConnect": "自动连接",
|
||||||
"autoRun": "自动运行",
|
"autoRun": "自动运行",
|
||||||
"autoUpdateHomeWidget": "自动更新桌面小部件",
|
"autoUpdateHomeWidget": "自动更新桌面小部件",
|
||||||
"backup": "备份",
|
|
||||||
"backupTip": "导出的数据仅进行了简单加密,请妥善保管。",
|
"backupTip": "导出的数据仅进行了简单加密,请妥善保管。",
|
||||||
"backupVersionNotMatch": "备份版本不匹配,无法恢复",
|
"backupVersionNotMatch": "备份版本不匹配,无法恢复",
|
||||||
"battery": "电池",
|
"battery": "电池",
|
||||||
"beforeConnect": "ServerBox 将在连接后,在 `~/.config/server_box` 写入脚本并执行,更多的技术细节请访问 [Github]({url})。",
|
|
||||||
"bgRun": "后台运行",
|
"bgRun": "后台运行",
|
||||||
"bgRunTip": "此开关只代表程序会尝试在后台运行,具体能否后台运行取决于是否开启了权限。原生 Android 请关闭本 App 的“电池优化”,MIUI 请修改省电策略为“无限制”。",
|
"bgRunTip": "此开关只代表程序会尝试在后台运行,具体能否后台运行取决于是否开启了权限。原生 Android 请关闭本 App 的“电池优化”,MIUI / HyperOS 请修改省电策略为“无限制”。",
|
||||||
"bioAuth": "生物认证",
|
|
||||||
"browser": "浏览器",
|
|
||||||
"bulkImportServers": "批量导入服务器",
|
|
||||||
"bulkImportServersTip": "可以在这里找到[格式]({url})",
|
|
||||||
"canPullRefresh": "可以下拉刷新",
|
|
||||||
"cancel": "取消",
|
|
||||||
"choose": "选择",
|
|
||||||
"chooseFontFile": "选择字体文件",
|
|
||||||
"choosePrivateKey": "选择私钥",
|
|
||||||
"clear": "清除",
|
|
||||||
"clipboard": "剪切板",
|
|
||||||
"close": "关闭",
|
|
||||||
"cmd": "命令",
|
"cmd": "命令",
|
||||||
"cnKeyboardComp": "中国Android兼容性",
|
"collapseUITip": "是否默认折叠 UI 中的长列表",
|
||||||
"cnKeyboardCompTip": "如果终端弹出安全键盘,可以开启",
|
|
||||||
"collapseUI": "折叠",
|
|
||||||
"collapseUITip": "是否默认折叠UI中存在的长列表",
|
|
||||||
"conn": "连接",
|
"conn": "连接",
|
||||||
"connected": "已连接",
|
|
||||||
"container": "容器",
|
"container": "容器",
|
||||||
"containerName": "容器名",
|
"containerTrySudoTip": "例如:在应用内将用户设置为 aaa,但是 Docker 安装在root用户下,这时就需要启用此选项",
|
||||||
"containerStatus": "容器状态",
|
|
||||||
"containerTrySudoTip": "例如:在应用内将用户设置为aaa,但是Docker安装在root用户下,这时就需要启用此选项",
|
|
||||||
"content": "内容",
|
|
||||||
"convert": "转换",
|
"convert": "转换",
|
||||||
"copy": "复制",
|
|
||||||
"copyPath": "复制路径",
|
"copyPath": "复制路径",
|
||||||
"cpuViewAsProgressTip": "以进度条样式显示每个CPU的使用率(旧版样式)",
|
"cpuViewAsProgressTip": "以进度条样式显示每个 CPU 的使用率(旧版样式)",
|
||||||
"createFile": "创建文件",
|
|
||||||
"createFolder": "创建文件夹",
|
|
||||||
"cursorType": "光标类型",
|
"cursorType": "光标类型",
|
||||||
"customCmd": "自定义命令",
|
"customCmd": "自定义命令",
|
||||||
"customCmdDocUrl": "https://github.com/lollipopkit/flutter_server_box/wiki/主页#自定义命令",
|
"customCmdDocUrl": "https://github.com/lollipopkit/flutter_server_box/wiki/主页#自定义命令",
|
||||||
"customCmdHint": "\"命令名称\": \"命令\"",
|
"customCmdHint": "\"命令名称\": \"命令\"",
|
||||||
"dark": "暗",
|
|
||||||
"day": "天",
|
|
||||||
"debug": "调试",
|
|
||||||
"decode": "解码",
|
"decode": "解码",
|
||||||
"decompress": "解压缩",
|
"decompress": "解压缩",
|
||||||
"delete": "删除",
|
|
||||||
"deleteScripts": "同时删除服务器脚本",
|
|
||||||
"deleteServers": "批量删除服务器",
|
"deleteServers": "批量删除服务器",
|
||||||
"deviceName": "设备名",
|
|
||||||
"dirEmpty": "请确保文件夹为空",
|
"dirEmpty": "请确保文件夹为空",
|
||||||
"disabled": "已禁用",
|
|
||||||
"disconnected": "连接断开",
|
"disconnected": "连接断开",
|
||||||
"disk": "硬盘",
|
"disk": "磁盘",
|
||||||
"diskIgnorePath": "忽略的磁盘路径",
|
"diskIgnorePath": "忽略的磁盘路径",
|
||||||
"displayCpuIndex": "显示CPU索引",
|
"displayCpuIndex": "显示 CPU 索引",
|
||||||
"displayName": "显示名称",
|
|
||||||
"dl2Local": "下载 {fileName} 到本地?",
|
"dl2Local": "下载 {fileName} 到本地?",
|
||||||
"doc": "文档",
|
"dockerEmptyRunningItems": "没有正在运行的容器。\n这可能是因为:\n- Docker 安装用户与 App 内配置的用户名不同\n- 环境变量 DOCKER_HOST 没有被正确读取。可以通过在终端内运行 `echo $DOCKER_HOST` 来获取。",
|
||||||
"dockerEditHost": "编辑 DOCKER_HOST",
|
|
||||||
"dockerEmptyRunningItems": "没有正在运行的容器。\n这可能是因为:\n- Docker 安装用户与 App 内配置的用户名不同\n- 环境变量 DOCKER_HOST 没有被正确读取。你可以通过在终端内运行 `echo $DOCKER_HOST` 来获取。",
|
|
||||||
"dockerImagesFmt": "共 {count} 个镜像",
|
"dockerImagesFmt": "共 {count} 个镜像",
|
||||||
"dockerNotInstalled": "Docker 未安装",
|
"dockerNotInstalled": "Docker 未安装",
|
||||||
"dockerStatusRunningAndStoppedFmt": "{runningCount}个正在运行, {stoppedCount}个已停止",
|
"dockerStatusRunningAndStoppedFmt": "{runningCount} 个正在运行, {stoppedCount} 个已停止",
|
||||||
"dockerStatusRunningFmt": "{count}个容器正在运行",
|
"dockerStatusRunningFmt": "{count} 个容器正在运行",
|
||||||
"doubleColumnMode": "双列模式",
|
"doubleColumnMode": "双列模式",
|
||||||
"doubleColumnTip": "此选项仅开启功能,实际是否能开启还取决于设备的宽度",
|
"doubleColumnTip": "此选项仅开启功能,实际是否能开启还取决于设备的宽度",
|
||||||
"download": "下载",
|
|
||||||
"edit": "编辑",
|
|
||||||
"editVirtKeys": "编辑虚拟按键",
|
"editVirtKeys": "编辑虚拟按键",
|
||||||
"editor": "编辑器",
|
"editor": "编辑器",
|
||||||
"editorHighlightTip": "目前的代码高亮性能较为糟糕,可以选择关闭以改善。",
|
"editorHighlightTip": "目前的代码高亮性能较为糟糕,可以选择关闭以改善。",
|
||||||
"encode": "编码",
|
"encode": "编码",
|
||||||
"error": "错误",
|
"envVars": "环境变量",
|
||||||
"exampleName": "名称示例",
|
|
||||||
"experimentalFeature": "实验性功能",
|
"experimentalFeature": "实验性功能",
|
||||||
"export": "导出",
|
|
||||||
"extraArgs": "额外参数",
|
"extraArgs": "额外参数",
|
||||||
"failed": "失败",
|
"fallbackSshDest": "备选 SSH 目标",
|
||||||
"fdroidReleaseTip": "如果你是从 Fdroid 下载的本应用,推荐关闭此选项",
|
"fdroidReleaseTip": "如果你是从 F-Droid 下载的本应用,推荐关闭此选项",
|
||||||
"feedback": "反馈",
|
|
||||||
"feedbackOnGithub": "如果你有任何问题,请在GitHub反馈",
|
|
||||||
"fieldMustNotEmpty": "这些输入框不能为空。",
|
|
||||||
"fileNotExist": "{file} 不存在",
|
|
||||||
"fileTooLarge": "文件 '{file}' 过大 '{size}',超过了 {sizeMax}",
|
"fileTooLarge": "文件 '{file}' 过大 '{size}',超过了 {sizeMax}",
|
||||||
"files": "文件",
|
|
||||||
"finished": "已完成",
|
|
||||||
"followSystem": "跟随系统",
|
"followSystem": "跟随系统",
|
||||||
"font": "字体",
|
"font": "字体",
|
||||||
"fontSize": "字体大小",
|
"fontSize": "字体大小",
|
||||||
"forExample": "例如",
|
|
||||||
"force": "强制",
|
"force": "强制",
|
||||||
"foundNUpdate": "找到 {count} 个更新",
|
|
||||||
"fullScreen": "全屏模式",
|
"fullScreen": "全屏模式",
|
||||||
"fullScreenJitter": "全屏模式抖动",
|
"fullScreenJitter": "全屏模式抖动",
|
||||||
"fullScreenJitterHelp": "防止烧屏",
|
"fullScreenJitterHelp": "防止烧屏",
|
||||||
"fullScreenTip": "当设备旋转为横屏时,是否开启全屏模式。此选项仅作用于服务器Tab页。",
|
"fullScreenTip": "当设备旋转为横屏时,是否开启全屏模式。此选项仅作用于服务器 Tab 页。",
|
||||||
"getPushTokenFailed": "未能获取到推送token",
|
|
||||||
"gettingToken": "正在获取Token...",
|
|
||||||
"goBackQ": "返回?",
|
"goBackQ": "返回?",
|
||||||
"goto": "前往",
|
"goto": "前往",
|
||||||
"hideTitleBar": "隐藏标题栏",
|
"hideTitleBar": "隐藏标题栏",
|
||||||
"hideTitleBarTip": "开启后请按住右上角三个按钮来拖动",
|
|
||||||
"highlight": "代码高亮",
|
"highlight": "代码高亮",
|
||||||
"homeWidgetUrlConfig": "桌面部件链接配置",
|
"homeWidgetUrlConfig": "桌面部件链接配置",
|
||||||
"host": "主机",
|
"host": "主机",
|
||||||
"hour": "小时",
|
|
||||||
"httpFailedWithCode": "请求失败, 状态码: {code}",
|
"httpFailedWithCode": "请求失败, 状态码: {code}",
|
||||||
"icloudSynced": "iCloud已同步,某些设置可能需要重启才能生效。",
|
|
||||||
"ignoreCert": "忽略证书",
|
"ignoreCert": "忽略证书",
|
||||||
"image": "镜像",
|
"image": "镜像",
|
||||||
"imagesList": "镜像列表",
|
"imagesList": "镜像列表",
|
||||||
"import": "导入",
|
|
||||||
"inAppUpdate": "在App内更新?否则使用浏览器下载",
|
|
||||||
"init": "初始化",
|
"init": "初始化",
|
||||||
"inner": "内置",
|
"inner": "内置",
|
||||||
"inputDomainHere": "在这里输入域名",
|
|
||||||
"install": "安装",
|
"install": "安装",
|
||||||
"installDockerWithUrl": "请先 https://docs.docker.com/engine/install docker",
|
"installDockerWithUrl": "请先 https://docs.docker.com/engine/install docker",
|
||||||
"invalid": "无效",
|
"invalid": "无效",
|
||||||
"invalidJson": "无效的 JSON",
|
|
||||||
"invalidVersion": "不支持的版本",
|
|
||||||
"invalidVersionHelp": "请确保正确安装了docker,或者使用的非自编译版本。如果没有以上问题,请在 {url} 提交问题。",
|
|
||||||
"isBusy": "当前正忙",
|
|
||||||
"jumpServer": "跳板服务器",
|
"jumpServer": "跳板服务器",
|
||||||
"keepForeground": "请保持应用处于前台!",
|
"keepForeground": "请保持应用处于前台!",
|
||||||
"keepStatusWhenErr": "保留上次的服务器状态",
|
"keepStatusWhenErr": "保留上次的服务器状态",
|
||||||
"keepStatusWhenErrTip": "仅限于执行脚本出错",
|
"keepStatusWhenErrTip": "仅限于执行脚本出错",
|
||||||
"keyAuth": "密钥认证",
|
"keyAuth": "密钥认证",
|
||||||
"language": "语言",
|
"letterCache": "输入法字符缓存",
|
||||||
"languageName": "简体中文",
|
"letterCacheTip": "推荐关闭,但是关闭后无法输入 CJK 等文字",
|
||||||
"lastTry": "最后尝试",
|
"license": "证书",
|
||||||
"launchPage": "启动页",
|
|
||||||
"license": "开源证书",
|
|
||||||
"light": "亮",
|
|
||||||
"loadingFiles": "正在加载目录。。。",
|
|
||||||
"location": "位置",
|
"location": "位置",
|
||||||
"log": "日志",
|
|
||||||
"loss": "丢包率",
|
"loss": "丢包率",
|
||||||
"madeWithLove": "用❤️制作 by {myGithub}",
|
"madeWithLove": "用❤️制作 by {myGithub}",
|
||||||
"manual": "手动",
|
"manual": "手动",
|
||||||
@@ -167,79 +93,50 @@
|
|||||||
"maxRetryCount": "服务器尝试重连次数",
|
"maxRetryCount": "服务器尝试重连次数",
|
||||||
"maxRetryCountEqual0": "会无限重试",
|
"maxRetryCountEqual0": "会无限重试",
|
||||||
"min": "最小",
|
"min": "最小",
|
||||||
"minute": "分钟",
|
|
||||||
"mission": "任务",
|
"mission": "任务",
|
||||||
"more": "更多",
|
"more": "更多",
|
||||||
"moveOutServerFuncBtnsHelp": "开启:可以在服务器 Tab 页的每个卡片下方显示。关闭:在服务器详情页顶部显示。",
|
"moveOutServerFuncBtnsHelp": "开启:可以在服务器 Tab 页的每个卡片下方显示。关闭:在服务器详情页顶部显示。",
|
||||||
"ms": "毫秒",
|
"ms": "毫秒",
|
||||||
"name": "名称",
|
|
||||||
"needHomeDir": "如果你是群晖用户,[看这里](https://kb.synology.cn/zh-cn/DSM/tutorial/ssh_could_not_chdir_to_home_directory)。其他系统用户,需搜索如何创建家目录(home directory).",
|
"needHomeDir": "如果你是群晖用户,[看这里](https://kb.synology.cn/zh-cn/DSM/tutorial/ssh_could_not_chdir_to_home_directory)。其他系统用户,需搜索如何创建家目录(home directory).",
|
||||||
"needRestart": "需要重启 App",
|
"needRestart": "需要重启 App",
|
||||||
"net": "网络",
|
"net": "网络",
|
||||||
"netViewType": "网络视图类型",
|
"netViewType": "网络视图类型",
|
||||||
"newContainer": "新建容器",
|
"newContainer": "新建容器",
|
||||||
"noClient": "没有SSH连接",
|
|
||||||
"noInterface": "没有可用的接口",
|
|
||||||
"noLineChart": "不使用折线图",
|
"noLineChart": "不使用折线图",
|
||||||
"noNotiPerm": "无通知权限,可能下载App更新时无进度提示。",
|
"noLineChartForCpu": "CPU 不使用折线图",
|
||||||
"noOptions": "无可选项",
|
|
||||||
"noPrivateKeyTip": "私钥不存在,可能已被删除/配置错误",
|
"noPrivateKeyTip": "私钥不存在,可能已被删除/配置错误",
|
||||||
"noPromptAgain": "不再提示",
|
"noPromptAgain": "不再提示",
|
||||||
"noResult": "无结果",
|
|
||||||
"noSavedPrivateKey": "没有已保存的私钥。",
|
|
||||||
"noSavedSnippet": "没有已保存的代码片段。",
|
|
||||||
"noServerAvailable": "没有可用的服务器。",
|
|
||||||
"noTask": "没有任务",
|
|
||||||
"noUpdateAvailable": "没有可用更新",
|
|
||||||
"node": "节点",
|
"node": "节点",
|
||||||
"notAvailable": "不可用",
|
"notAvailable": "不可用",
|
||||||
"notSelected": "未选择",
|
|
||||||
"note": "备注",
|
|
||||||
"nullToken": "无Token",
|
|
||||||
"ok": "好",
|
|
||||||
"onServerDetailPage": "在服务器详情页",
|
"onServerDetailPage": "在服务器详情页",
|
||||||
"onlyOneLine": "仅显示为一行(可滚动)",
|
"onlyOneLine": "仅显示为一行(可滚动)",
|
||||||
"onlyWhenCoreBiggerThan8": "仅当核心数>8时生效",
|
"onlyWhenCoreBiggerThan8": "仅当核心数大于 8 时生效",
|
||||||
"open": "打开",
|
|
||||||
"openLastPath": "打开上次的路径",
|
"openLastPath": "打开上次的路径",
|
||||||
"openLastPathTip": "不同的服务器会有不同的记录,且记录的是退出时的路径",
|
"openLastPathTip": "不同的服务器会有不同的记录,且记录的是退出时的路径",
|
||||||
"parseContainerStats": "解析容器占用状态",
|
"parseContainerStatsTip": "Docker 解析占用状态较为缓慢",
|
||||||
"parseContainerStatsTip": "Docker解析占用状态较为缓慢",
|
|
||||||
"paste": "粘贴",
|
|
||||||
"path": "路径",
|
|
||||||
"percentOfSize": "{size} 的 {percent}%",
|
"percentOfSize": "{size} 的 {percent}%",
|
||||||
"pickFile": "选择文件",
|
"permission": "权限",
|
||||||
"pingAvg": "平均:",
|
"pingAvg": "平均:",
|
||||||
"pingInputIP": "请输入目标IP或域名",
|
"pingInputIP": "请输入目标IP或域名",
|
||||||
"pingNoServer": "没有服务器可用于Ping\n请在服务器tab添加服务器后再试",
|
"pingNoServer": "没有服务器可用于 Ping\n请在服务器 tab 添加服务器后再试",
|
||||||
"pkg": "包管理",
|
"pkg": "包管理",
|
||||||
"pkgUpgradeTip": "请在更新前备份系统。",
|
|
||||||
"platformNotSupportUpdate": "当前平台不支持更新,请编译最新源码后手动安装",
|
|
||||||
"plugInType": "插入类型",
|
"plugInType": "插入类型",
|
||||||
"plzEnterHost": "请输入主机",
|
|
||||||
"plzSelectKey": "请选择私钥",
|
|
||||||
"port": "端口",
|
"port": "端口",
|
||||||
"preview": "预览",
|
"preview": "预览",
|
||||||
"primaryColorSeed": "主题色种子",
|
|
||||||
"privateKey": "私钥",
|
"privateKey": "私钥",
|
||||||
"process": "进程",
|
"process": "进程",
|
||||||
"pushToken": "消息推送 Token",
|
"pushToken": "消息推送 Token",
|
||||||
"pveIgnoreCertTip": "不推荐开启,注意安全隐患!如果你使用的PVE默认证书,需要开启该选项",
|
"pveIgnoreCertTip": "不推荐开启,注意安全隐患!如果你使用的 PVE 默认证书,需要开启该选项",
|
||||||
"pveLoginFailed": "登录失败。无法使用服务器配置内的用户/密码,以Linux PAM方式登录。",
|
"pveLoginFailed": "登录失败。无法使用服务器配置内的用户/密码,以 Linux PAM 方式登录。",
|
||||||
"pveVersionLow": "当前该功能处于测试阶段,仅在PVE 8+上测试过,请谨慎使用",
|
"pveVersionLow": "当前该功能处于测试阶段,仅在 PVE 8+ 上测试过,请谨慎使用",
|
||||||
"pwd": "密码",
|
"pwd": "密码",
|
||||||
"read": "读",
|
"read": "读",
|
||||||
"reboot": "重启",
|
"reboot": "重启",
|
||||||
"rememberChoice": "记住选择",
|
|
||||||
"rememberPwdInMem": "在内存中记住密码",
|
"rememberPwdInMem": "在内存中记住密码",
|
||||||
"rememberPwdInMemTip": "用于容器、挂起等",
|
"rememberPwdInMemTip": "用于容器、挂起等",
|
||||||
"rememberWindowSize": "记住窗口大小",
|
"rememberWindowSize": "记住窗口大小",
|
||||||
"remotePath": "远端路径",
|
"remotePath": "远端路径",
|
||||||
"rename": "重命名",
|
|
||||||
"reportBugsOnGithubIssue": "请到 {url} 提交问题",
|
|
||||||
"restart": "重启",
|
"restart": "重启",
|
||||||
"restore": "恢复",
|
|
||||||
"restoreSuccess": "恢复成功,需要重启App来应用更改",
|
|
||||||
"result": "结果",
|
"result": "结果",
|
||||||
"rotateAngel": "旋转角度",
|
"rotateAngel": "旋转角度",
|
||||||
"route": "路由",
|
"route": "路由",
|
||||||
@@ -254,14 +151,8 @@
|
|||||||
"serverDetailOrder": "详情页部件顺序",
|
"serverDetailOrder": "详情页部件顺序",
|
||||||
"serverFuncBtns": "服务器功能按钮",
|
"serverFuncBtns": "服务器功能按钮",
|
||||||
"serverOrder": "服务器顺序",
|
"serverOrder": "服务器顺序",
|
||||||
"serverTabConnecting": "连接中...",
|
|
||||||
"serverTabEmpty": "现在没有服务器。\n点击右下方按钮来添加。",
|
|
||||||
"serverTabFailed": "失败",
|
|
||||||
"serverTabLoading": "加载中...",
|
|
||||||
"serverTabPlzSave": "请再次保存该私钥",
|
|
||||||
"serverTabUnkown": "未知状态",
|
|
||||||
"setting": "设置",
|
|
||||||
"sftpDlPrepare": "准备连接至服务器...",
|
"sftpDlPrepare": "准备连接至服务器...",
|
||||||
|
"sftpEditorTip": "如果为空, 使用App内置的文件编辑器. 如果有值, 这是用远程服务器的编辑器, 例如 `vim` (建议根据 `EDITOR` 自动获取).",
|
||||||
"sftpRmrDirSummary": "在 SFTP 中使用 `rm -r` 来删除文件夹",
|
"sftpRmrDirSummary": "在 SFTP 中使用 `rm -r` 来删除文件夹",
|
||||||
"sftpSSHConnected": "SFTP 已连接...",
|
"sftpSSHConnected": "SFTP 已连接...",
|
||||||
"sftpShowFoldersFirst": "文件夹显示在前",
|
"sftpShowFoldersFirst": "文件夹显示在前",
|
||||||
@@ -270,17 +161,19 @@
|
|||||||
"size": "大小",
|
"size": "大小",
|
||||||
"snippet": "代码片段",
|
"snippet": "代码片段",
|
||||||
"softWrap": "自动换行",
|
"softWrap": "自动换行",
|
||||||
|
"specifyDev": "指定设备",
|
||||||
|
"specifyDevTip": "例如网络流量统计默认是所有设备,你可以在这里指定特定的设备",
|
||||||
"speed": "速度",
|
"speed": "速度",
|
||||||
"spentTime": "耗时: {time}",
|
"spentTime": "耗时: {time}",
|
||||||
"sshTermHelp": "在终端可滚动时,横向拖动可以选中文字。点击键盘按钮可以开启/关闭键盘。文件图标会打开当前路径SFTP。粘贴板按钮会在有选中文字时复制内容,在未选中并且剪切板有内容时粘贴内容到终端。代码图标会粘贴代码片段到终端并执行。",
|
"sshTermHelp": "在终端可滚动时,横向拖动可以选中文字。点击键盘按钮可以开启/关闭键盘。文件图标会打开当前路径 SFTP。剪切板按钮会在有选中文字时复制内容,在未选中并且剪切板有内容时粘贴内容到终端。代码图标会粘贴代码片段到终端并执行。",
|
||||||
"sshTip": "该功能目前处于测试阶段。\n\n请在 {url} 反馈问题,或者加入我们开发。",
|
"sshTip": "该功能目前处于测试阶段。\n\n请在 {url} 反馈问题,或者加入我们开发。",
|
||||||
"sshVirtualKeyAutoOff": "虚拟按键自动切换",
|
"sshVirtualKeyAutoOff": "虚拟按键自动切换",
|
||||||
"start": "开始",
|
"start": "开始",
|
||||||
|
"stat": "统计",
|
||||||
"stats": "统计",
|
"stats": "统计",
|
||||||
"stop": "停止",
|
"stop": "停止",
|
||||||
"stopped": "已停止",
|
"stopped": "已停止",
|
||||||
"storage": "存储",
|
"storage": "存储",
|
||||||
"success": "成功",
|
|
||||||
"supportFmtArgs": "支持以下格式化参数:",
|
"supportFmtArgs": "支持以下格式化参数:",
|
||||||
"suspend": "挂起",
|
"suspend": "挂起",
|
||||||
"suspendTip": "suspend 功能需要 root 权限及 systemd 支持。",
|
"suspendTip": "suspend 功能需要 root 权限及 systemd 支持。",
|
||||||
@@ -290,41 +183,31 @@
|
|||||||
"system": "系统",
|
"system": "系统",
|
||||||
"tag": "标签",
|
"tag": "标签",
|
||||||
"temperature": "温度",
|
"temperature": "温度",
|
||||||
"termFontSizeTip": "此设置会影响终端大小(宽高)。可以在终端页面缩放来调整当前会话的字体大小",
|
"termFontSizeTip": "此设置会影响终端大小(宽和高)。可以在终端页面缩放来调整当前会话的字体大小",
|
||||||
"terminal": "终端",
|
"terminal": "终端",
|
||||||
"test": "测试",
|
"test": "测试",
|
||||||
"textScaler": "字体缩放",
|
"textScaler": "字体缩放",
|
||||||
"textScalerTip": "1.0 => 100%(原大小),仅作用于服务器页面部分字体,不建议修改。",
|
"textScalerTip": "1.0 => 100%(原大小),仅作用于服务器页面部分字体,不建议修改。",
|
||||||
"theme": "主题",
|
"theme": "主题",
|
||||||
"themeMode": "主题模式",
|
|
||||||
"time": "时间",
|
"time": "时间",
|
||||||
"times": "次",
|
"times": "次",
|
||||||
"total": "总共",
|
"total": "总共",
|
||||||
"traffic": "流量",
|
"traffic": "流量",
|
||||||
"trySudo": "尝试使用sudo",
|
"trySudo": "尝试使用 sudo",
|
||||||
"ttl": "缓存时间",
|
"ttl": "TTL",
|
||||||
"unknown": "未知",
|
"unknown": "未知",
|
||||||
"unknownError": "未知错误",
|
|
||||||
"unkownConvertMode": "未知转换模式",
|
"unkownConvertMode": "未知转换模式",
|
||||||
"update": "更新",
|
"update": "更新",
|
||||||
"updateAll": "更新全部",
|
"updateIntervalEqual0": "你设置为 0,服务器状态不会自动刷新。\n且不能计算 CPU 使用情况。",
|
||||||
"updateIntervalEqual0": "你设置为0,服务器状态不会自动刷新。\n且不能计算CPU使用情况。",
|
|
||||||
"updateServerStatusInterval": "服务器状态刷新间隔",
|
"updateServerStatusInterval": "服务器状态刷新间隔",
|
||||||
"updateTip": "新版本: v1.0.{newest}",
|
|
||||||
"updateTipTooLow": "当前版本过低,请升级至 v1.0.{newest}",
|
|
||||||
"upload": "上传",
|
"upload": "上传",
|
||||||
"upsideDown": "上下交换",
|
"upsideDown": "上下交换",
|
||||||
"uptime": "启动时长",
|
"uptime": "启动时长",
|
||||||
"urlOrJson": "链接或JSON",
|
"useCdn": "使用 CDN",
|
||||||
"useCdn": "使用CDN",
|
"useCdnTip": "非中国大陆用户推荐使用 CDN,是否使用?",
|
||||||
"useCdnTip": "非中国大陆用户推荐使用CDN,是否使用?",
|
|
||||||
"useNoPwd": "将会使用无密码",
|
"useNoPwd": "将会使用无密码",
|
||||||
"usePodmanByDefault": "默认使用 Podman",
|
"usePodmanByDefault": "默认使用 Podman",
|
||||||
"used": "已用",
|
"used": "已用",
|
||||||
"user": "用户",
|
|
||||||
"versionHaveUpdate": "找到新版本:v1.0.{build}, 点击更新",
|
|
||||||
"versionUnknownUpdate": "当前:v1.0.{build},点击检查更新",
|
|
||||||
"versionUpdated": "当前:v1.0.{build}, 已是最新版本",
|
|
||||||
"view": "视图",
|
"view": "视图",
|
||||||
"viewErr": "查看错误",
|
"viewErr": "查看错误",
|
||||||
"virtKeyHelpClipboard": "如果终端有选中字符,则复制选中字符至剪切板,否则粘贴剪切板内容至终端。",
|
"virtKeyHelpClipboard": "如果终端有选中字符,则复制选中字符至剪切板,否则粘贴剪切板内容至终端。",
|
||||||
@@ -333,10 +216,10 @@
|
|||||||
"waitConnection": "请等待连接建立",
|
"waitConnection": "请等待连接建立",
|
||||||
"wakeLock": "保持唤醒",
|
"wakeLock": "保持唤醒",
|
||||||
"watchNotPaired": "没有已配对的 Apple Watch",
|
"watchNotPaired": "没有已配对的 Apple Watch",
|
||||||
"webdavSettingEmpty": "Webdav 设置项为空",
|
"webdavSettingEmpty": "WebDav 设置项为空",
|
||||||
"whenOpenApp": "当打开 App 时",
|
"whenOpenApp": "当打开 App 时",
|
||||||
"willTakEeffectImmediately": "更改将会立即生效",
|
"wolTip": "在配置 WOL 后,每次连接服务器都会先发送一次 WOL 请求",
|
||||||
"wolTip": "在配置 WOL 后,每次连接服务器都会先发送一次 WOl 请求",
|
|
||||||
"write": "写",
|
"write": "写",
|
||||||
"writeScriptFailTip": "写入脚本失败,可能是没有权限/目录不存在等"
|
"writeScriptFailTip": "写入脚本失败,可能是没有权限/目录不存在等",
|
||||||
|
"writeScriptTip": "在连接服务器后,会向 ~/.config/server_box 写入脚本来监测系统状态,你可以审查脚本内容。"
|
||||||
}
|
}
|
||||||
@@ -1,245 +1,142 @@
|
|||||||
{
|
{
|
||||||
"@@locale": "zh_TW",
|
"@@locale": "zh_TW",
|
||||||
"about": "關於",
|
|
||||||
"aboutThanks": "感謝以下參與的各位。",
|
"aboutThanks": "感謝以下參與的各位。",
|
||||||
"acceptBeta": "接受測試版更新推送",
|
"acceptBeta": "接受測試版更新推送",
|
||||||
"add": "新增",
|
"addSystemPrivateKeyTip": "當前沒有任何私鑰,是否添加系統自帶的 (~/.ssh/id_rsa)?",
|
||||||
"addAServer": "新增服務器",
|
|
||||||
"addPrivateKey": "新增一個私鑰",
|
|
||||||
"addSystemPrivateKeyTip": "當前沒有任何私鑰,是否添加系統自帶的(~/.ssh/id_rsa)?",
|
|
||||||
"added2List": "已添加至任務列表",
|
"added2List": "已添加至任務列表",
|
||||||
"addr": "地址",
|
"addr": "位址",
|
||||||
"all": "所有",
|
|
||||||
"alreadyLastDir": "已經是最上層目錄了",
|
"alreadyLastDir": "已經是最上層目錄了",
|
||||||
"alterUrl": "備選鏈接",
|
|
||||||
"askContinue": "{msg},繼續嗎?",
|
|
||||||
"attention": "注意",
|
|
||||||
"authFailTip": "認證失敗,請檢查密碼/密鑰/主機/用戶等是否錯誤。",
|
"authFailTip": "認證失敗,請檢查密碼/密鑰/主機/用戶等是否錯誤。",
|
||||||
"authRequired": "需要認證",
|
"autoBackupConflict": "只能同時開啓一個自動備份",
|
||||||
"auto": "自動",
|
|
||||||
"autoBackupConflict": "只能同時開啓壹個自動備份",
|
|
||||||
"autoCheckUpdate": "自動檢查更新",
|
|
||||||
"autoConnect": "自動連接",
|
"autoConnect": "自動連接",
|
||||||
"autoRun": "自動運行",
|
"autoRun": "自動運行",
|
||||||
"autoUpdateHomeWidget": "自動更新桌面小部件",
|
"autoUpdateHomeWidget": "自動更新桌面小部件",
|
||||||
"backup": "備份",
|
"backupTip": "匯出的資料僅進行了簡單加密,請妥善保管。",
|
||||||
"backupTip": "導出的數據僅進行了簡單加密,請妥善保管。",
|
|
||||||
"backupVersionNotMatch": "備份版本不匹配,無法還原",
|
"backupVersionNotMatch": "備份版本不匹配,無法還原",
|
||||||
"battery": "電池",
|
"battery": "電池",
|
||||||
"beforeConnect": "ServerBox將在連接後,在`~/.config/server_box`寫入腳本並執行,更多的技術細節請訪問[Github]({url})。",
|
"bgRun": "後台運行",
|
||||||
"bgRun": "背景運行",
|
"bgRunTip": "此開關只代表程式會嘗試在後台運行,具體能否在後臺運行取決於是否開啟了權限。 原生 Android 請關閉本 App 的“電池優化”,MIUI / HyperOS 請修改省電策略為“無限制”。",
|
||||||
"bgRunTip": "此開關只代表程式會嘗試在背景執行,具體能否背景運行取決於是否開啟了權限。 原生 Android 請關閉本 App 的“電池優化”,MIUI 請修改省電策略為“無限制”。",
|
|
||||||
"bioAuth": "生物認證",
|
|
||||||
"browser": "瀏覽器",
|
|
||||||
"bulkImportServers": "批量導入伺服器",
|
|
||||||
"bulkImportServersTip": "您可以在此處找到[格式]({url})",
|
|
||||||
"canPullRefresh": "可以下拉更新",
|
|
||||||
"cancel": "取消",
|
|
||||||
"choose": "選擇",
|
|
||||||
"chooseFontFile": "選擇字體文件",
|
|
||||||
"choosePrivateKey": "選擇私鑰",
|
|
||||||
"clear": "清除",
|
|
||||||
"clipboard": "剪切板",
|
|
||||||
"close": "關閉",
|
|
||||||
"cmd": "命令",
|
"cmd": "命令",
|
||||||
"cnKeyboardComp": "中國Android兼容性",
|
"collapseUITip": "是否預設折疊 UI 中存在的長列表",
|
||||||
"cnKeyboardCompTip": "如果終端彈出安全鍵盤,您可以啟用它。",
|
|
||||||
"collapseUI": "折疊",
|
|
||||||
"collapseUITip": "是否預設折疊UI中存在的長列表",
|
|
||||||
"conn": "連接",
|
"conn": "連接",
|
||||||
"connected": "已連接",
|
|
||||||
"container": "容器",
|
"container": "容器",
|
||||||
"containerName": "容器名稱",
|
"containerTrySudoTip": "例如:App 內設置使用者為 aaa,但是 Docker 安裝在 root 使用者,這時就需要開啟此選項",
|
||||||
"containerStatus": "容器狀態",
|
|
||||||
"containerTrySudoTip": "例如:App内设置用户为aaa,但是Docker安装在root用户,这时就需要开启此选项",
|
|
||||||
"content": "內容",
|
|
||||||
"convert": "轉換",
|
"convert": "轉換",
|
||||||
"copy": "複製",
|
|
||||||
"copyPath": "複製路徑",
|
"copyPath": "複製路徑",
|
||||||
"cpuViewAsProgressTip": "以進度條樣式顯示每個CPU的使用率(舊版樣式)",
|
"cpuViewAsProgressTip": "以進度條樣式顯示每個CPU的使用率(舊版樣式)",
|
||||||
"createFile": "創建文件",
|
"cursorType": "游標類型",
|
||||||
"createFolder": "創建文件夾",
|
"customCmd": "自訂命令",
|
||||||
"cursorType": "光標類型",
|
|
||||||
"customCmd": "自定義命令",
|
|
||||||
"customCmdDocUrl": "https://github.com/lollipopkit/flutter_server_box/wiki/主页#自定义命令",
|
"customCmdDocUrl": "https://github.com/lollipopkit/flutter_server_box/wiki/主页#自定义命令",
|
||||||
"customCmdHint": "\"命令名稱\": \"命令\"",
|
"customCmdHint": "\"命令名稱\": \"命令\"",
|
||||||
"dark": "暗",
|
|
||||||
"day": "日",
|
|
||||||
"debug": "調試",
|
|
||||||
"decode": "解碼",
|
"decode": "解碼",
|
||||||
"decompress": "解壓縮",
|
"decompress": "解壓縮",
|
||||||
"delete": "刪除",
|
"deleteServers": "批量刪除伺服器",
|
||||||
"deleteScripts": "同時刪除服務器腳本",
|
"dirEmpty": "請確保資料夾為空",
|
||||||
"deleteServers": "批量刪除服務器",
|
|
||||||
"deviceName": "設備名",
|
|
||||||
"dirEmpty": "請確保文件夾為空",
|
|
||||||
"disabled": "已禁用",
|
|
||||||
"disconnected": "連接斷開",
|
"disconnected": "連接斷開",
|
||||||
"disk": "硬盤",
|
"disk": "磁碟",
|
||||||
"diskIgnorePath": "忽略的磁盤路徑",
|
"diskIgnorePath": "忽略的磁碟路徑",
|
||||||
"displayCpuIndex": "顯示CPU索引",
|
"displayCpuIndex": "顯示 CPU 索引",
|
||||||
"displayName": "顯示名稱",
|
|
||||||
"dl2Local": "下載 {fileName} 到本地?",
|
"dl2Local": "下載 {fileName} 到本地?",
|
||||||
"doc": "文檔",
|
|
||||||
"dockerEditHost": "編輯 DOCKER_HOST",
|
|
||||||
"dockerEmptyRunningItems": "沒有正在運行的容器。\n這可能是因為:\n- Docker 安裝使用者與 App 內配置的使用者名稱不同\n- 環境變量 DOCKER_HOST 沒有被正確讀取。你可以通過在終端內運行 `echo $DOCKER_HOST` 來獲取。",
|
"dockerEmptyRunningItems": "沒有正在運行的容器。\n這可能是因為:\n- Docker 安裝使用者與 App 內配置的使用者名稱不同\n- 環境變量 DOCKER_HOST 沒有被正確讀取。你可以通過在終端內運行 `echo $DOCKER_HOST` 來獲取。",
|
||||||
"dockerImagesFmt": "共 {count} 個鏡像",
|
"dockerImagesFmt": "共 {count} 個鏡像",
|
||||||
"dockerNotInstalled": "Docker 未安裝",
|
"dockerNotInstalled": "Docker 未安裝",
|
||||||
"dockerStatusRunningAndStoppedFmt": "{runningCount}個正在運行, {stoppedCount}個已停止",
|
"dockerStatusRunningAndStoppedFmt": "{runningCount} 個正在運行, {stoppedCount} 個已停止",
|
||||||
"dockerStatusRunningFmt": "{count}個容器正在運行",
|
"dockerStatusRunningFmt": "{count} 個容器正在運行",
|
||||||
"doubleColumnMode": "雙列模式",
|
"doubleColumnMode": "雙列模式",
|
||||||
"doubleColumnTip": "此選項僅開啟功能,實際是否能開啟還取決於設備的寬度",
|
"doubleColumnTip": "此選項僅開啟功能,實際是否能開啟還取決於設備的寬度",
|
||||||
"download": "下載",
|
|
||||||
"edit": "編輯",
|
|
||||||
"editVirtKeys": "編輯虛擬按鍵",
|
"editVirtKeys": "編輯虛擬按鍵",
|
||||||
"editor": "編輯器",
|
"editor": "編輯器",
|
||||||
"editorHighlightTip": "目前的代碼高亮性能較為糟糕,可以選擇關閉以改善。",
|
"editorHighlightTip": "目前的代碼高亮性能較為糟糕,可以選擇關閉以改善。",
|
||||||
"encode": "編碼",
|
"encode": "編碼",
|
||||||
"error": "錯誤",
|
"envVars": "環境變量",
|
||||||
"exampleName": "名稱範例",
|
|
||||||
"experimentalFeature": "實驗性功能",
|
"experimentalFeature": "實驗性功能",
|
||||||
"export": "導出",
|
|
||||||
"extraArgs": "額外參數",
|
"extraArgs": "額外參數",
|
||||||
"failed": "失敗",
|
"fallbackSshDest": "備選 SSH 目標",
|
||||||
"fdroidReleaseTip": "如果你是從 Fdroid 下載的本應用,推薦關閉此選項",
|
"fdroidReleaseTip": "如果你是從 F-Droid 下載的本應用,推薦關閉此選項",
|
||||||
"feedback": "反饋",
|
|
||||||
"feedbackOnGithub": "如果你有任何問題,請在GitHub反饋",
|
|
||||||
"fieldMustNotEmpty": "這些輸入框不能為空。",
|
|
||||||
"fileNotExist": "{file} 不存在",
|
|
||||||
"fileTooLarge": "文件 '{file}' 過大 '{size}',超過了 {sizeMax}",
|
"fileTooLarge": "文件 '{file}' 過大 '{size}',超過了 {sizeMax}",
|
||||||
"files": "文件",
|
|
||||||
"finished": "已完成",
|
|
||||||
"followSystem": "跟隨系統",
|
"followSystem": "跟隨系統",
|
||||||
"font": "字體",
|
"font": "字型",
|
||||||
"fontSize": "字體大小",
|
"fontSize": "字型大小",
|
||||||
"forExample": "例如",
|
|
||||||
"force": "強制",
|
"force": "強制",
|
||||||
"foundNUpdate": "找到 {count} 個更新",
|
"fullScreen": "全螢幕模式",
|
||||||
"fullScreen": "全屏模式",
|
"fullScreenJitter": "全螢幕模式抖動",
|
||||||
"fullScreenJitter": "全屏模式抖動",
|
|
||||||
"fullScreenJitterHelp": "防止燒屏",
|
"fullScreenJitterHelp": "防止燒屏",
|
||||||
"fullScreenTip": "當設備旋轉為橫屏時,是否開啟全屏模式?此選項僅適用於伺服器選項卡。",
|
"fullScreenTip": "當設備旋轉為橫屏時,是否開啟全熒幕模式?此選項僅適用於伺服器選項卡。",
|
||||||
"getPushTokenFailed": "未能獲取到推送token",
|
|
||||||
"gettingToken": "正在獲取Token...",
|
|
||||||
"goBackQ": "返回?",
|
"goBackQ": "返回?",
|
||||||
"goto": "前往",
|
"goto": "前往",
|
||||||
"hideTitleBar": "隱藏標題欄",
|
"hideTitleBar": "隱藏標題欄",
|
||||||
"hideTitleBarTip": "開啟後請按住右上角三個按鈕來拖動",
|
|
||||||
"highlight": "代碼高亮",
|
"highlight": "代碼高亮",
|
||||||
"homeWidgetUrlConfig": "桌面部件鏈接配置",
|
"homeWidgetUrlConfig": "桌面部件鏈接配置",
|
||||||
"host": "主機",
|
"host": "主機",
|
||||||
"hour": "小時",
|
|
||||||
"httpFailedWithCode": "請求失敗, 狀態碼: {code}",
|
"httpFailedWithCode": "請求失敗, 狀態碼: {code}",
|
||||||
"icloudSynced": "iCloud已同步,某些設置可能需要重啟才能生效。",
|
|
||||||
"ignoreCert": "忽略證書",
|
"ignoreCert": "忽略證書",
|
||||||
"image": "鏡像",
|
"image": "鏡像",
|
||||||
"imagesList": "鏡像列表",
|
"imagesList": "鏡像列表",
|
||||||
"import": "導入",
|
|
||||||
"inAppUpdate": "在App內更新?否則使用瀏覽器下載。",
|
|
||||||
"init": "初始化",
|
"init": "初始化",
|
||||||
"inner": "內置",
|
"inner": "內建",
|
||||||
"inputDomainHere": "在這裡輸入域名",
|
|
||||||
"install": "安裝",
|
"install": "安裝",
|
||||||
"installDockerWithUrl": "請先 https://docs.docker.com/engine/install docker",
|
"installDockerWithUrl": "請先 https://docs.docker.com/engine/install docker",
|
||||||
"invalid": "無效",
|
"invalid": "無效",
|
||||||
"invalidJson": "無效的 JSON",
|
"jumpServer": "跳板伺服器",
|
||||||
"invalidVersion": "不支持的版本",
|
|
||||||
"invalidVersionHelp": "請確保正確安裝了docker,或者使用的非自編譯版本。如果沒有以上問題,請在 {url} 提交問題。",
|
|
||||||
"isBusy": "當前正忙",
|
|
||||||
"jumpServer": "跳板服務器",
|
|
||||||
"keepForeground": "請保持應用處於前台!",
|
"keepForeground": "請保持應用處於前台!",
|
||||||
"keepStatusWhenErr": "保留上次的伺服器狀態",
|
"keepStatusWhenErr": "保留上次的伺服器狀態",
|
||||||
"keepStatusWhenErrTip": "仅在执行脚本时出错时",
|
"keepStatusWhenErrTip": "僅在執行腳本出錯時",
|
||||||
"keyAuth": "密鑰認證",
|
"keyAuth": "密鑰認證",
|
||||||
"language": "語言",
|
"letterCache": "输入法字符緩存",
|
||||||
"languageName": "繁體中文",
|
"letterCacheTip": "建議關閉,但關閉後將無法輸入 CJK 等文字。",
|
||||||
"lastTry": "最後嘗試",
|
"license": "證書",
|
||||||
"launchPage": "啓動頁",
|
|
||||||
"license": "開源證書",
|
|
||||||
"light": "亮",
|
|
||||||
"loadingFiles": "正在加載目錄。。。",
|
|
||||||
"location": "位置",
|
"location": "位置",
|
||||||
"log": "日誌",
|
|
||||||
"loss": "丟包率",
|
"loss": "丟包率",
|
||||||
"madeWithLove": "用❤️製作 by {myGithub}",
|
"madeWithLove": "用❤️製作 by {myGithub}",
|
||||||
"manual": "手動",
|
"manual": "手動",
|
||||||
"max": "最大",
|
"max": "最大",
|
||||||
"maxRetryCount": "服務器嘗試重連次數",
|
"maxRetryCount": "伺服器嘗試重連次數",
|
||||||
"maxRetryCountEqual0": "會無限重試",
|
"maxRetryCountEqual0": "會無限重試",
|
||||||
"min": "最小",
|
"min": "最小",
|
||||||
"minute": "分鐘",
|
|
||||||
"mission": "任務",
|
"mission": "任務",
|
||||||
"more": "更多",
|
"more": "更多",
|
||||||
"moveOutServerFuncBtnsHelp": "開啟:可以在服務器 Tab 頁的每個卡片下方顯示。關閉:在服務器詳情頁頂部顯示。",
|
"moveOutServerFuncBtnsHelp": "開啟:可以在伺服器 Tab 頁的每個卡片下方顯示。關閉:在伺服器詳情頁頂部顯示。",
|
||||||
"ms": "毫秒",
|
"ms": "毫秒",
|
||||||
"name": "名稱",
|
|
||||||
"needHomeDir": "如果你是群暉用戶,[看這裡](https://kb.synology.com/DSM/tutorial/user_enable_home_service)。其他系統用戶,需搜索如何創建家目錄(home directory)。",
|
"needHomeDir": "如果你是群暉用戶,[看這裡](https://kb.synology.com/DSM/tutorial/user_enable_home_service)。其他系統用戶,需搜索如何創建家目錄(home directory)。",
|
||||||
"needRestart": "需要重啓 App",
|
"needRestart": "需要重啓 App",
|
||||||
"net": "網絡",
|
"net": "網路",
|
||||||
"netViewType": "網絡視圖類型",
|
"netViewType": "網路視圖類型",
|
||||||
"newContainer": "新建容器",
|
"newContainer": "新建容器",
|
||||||
"noClient": "沒有SSH連接",
|
|
||||||
"noInterface": "沒有可用的接口",
|
|
||||||
"noLineChart": "不使用折線圖",
|
"noLineChart": "不使用折線圖",
|
||||||
"noNotiPerm": "無通知權限,可能在下載應用程式更新時沒有進度提示。",
|
"noLineChartForCpu": "CPU 不使用折線圖",
|
||||||
"noOptions": "無可選項",
|
|
||||||
"noPrivateKeyTip": "私鑰不存在,可能已被刪除/配置錯誤。",
|
"noPrivateKeyTip": "私鑰不存在,可能已被刪除/配置錯誤。",
|
||||||
"noPromptAgain": "不再提示",
|
"noPromptAgain": "不再提示",
|
||||||
"noResult": "無結果",
|
|
||||||
"noSavedPrivateKey": "沒有已保存的私鑰。",
|
|
||||||
"noSavedSnippet": "沒有已保存的程式片段。",
|
|
||||||
"noServerAvailable": "沒有可用的服務器。",
|
|
||||||
"noTask": "沒有任務",
|
|
||||||
"noUpdateAvailable": "沒有可用更新",
|
|
||||||
"node": "節點",
|
"node": "節點",
|
||||||
"notAvailable": "不可用",
|
"notAvailable": "不可用",
|
||||||
"notSelected": "未選擇",
|
"onServerDetailPage": "在伺服器詳情頁",
|
||||||
"note": "備註",
|
|
||||||
"nullToken": "無Token",
|
|
||||||
"ok": "好",
|
|
||||||
"onServerDetailPage": "在服務器詳情頁",
|
|
||||||
"onlyOneLine": "僅顯示為一行(可滾動)",
|
"onlyOneLine": "僅顯示為一行(可滾動)",
|
||||||
"onlyWhenCoreBiggerThan8": "僅當核心數>8時生效",
|
"onlyWhenCoreBiggerThan8": "僅當核心數大於 8 時生效",
|
||||||
"open": "打開",
|
|
||||||
"openLastPath": "打開上次的路徑",
|
"openLastPath": "打開上次的路徑",
|
||||||
"openLastPathTip": "不同的服務器會有不同的記錄,且記錄的是退出時的路徑",
|
"openLastPathTip": "不同的伺服器會有不同的記錄,且記錄的是退出時的路徑",
|
||||||
"parseContainerStats": "解析容器佔用狀態",
|
"parseContainerStatsTip": "Docker 解析佔用狀態較為緩慢",
|
||||||
"parseContainerStatsTip": "Docker解析佔用狀態較為緩慢",
|
|
||||||
"paste": "貼上",
|
|
||||||
"path": "路徑",
|
|
||||||
"percentOfSize": "{size} 的 {percent}%",
|
"percentOfSize": "{size} 的 {percent}%",
|
||||||
"pickFile": "選擇文件",
|
"permission": "權限",
|
||||||
"pingAvg": "平均:",
|
"pingAvg": "平均:",
|
||||||
"pingInputIP": "請輸入目標IP或域名",
|
"pingInputIP": "請輸入目標 IP 或域名",
|
||||||
"pingNoServer": "沒有服務器可用於Ping\n請在服務器tab新增服務器後再試",
|
"pingNoServer": "沒有伺服器可用於 Ping\n請在伺服器 Tab 新增伺服器後再試",
|
||||||
"pkg": "包管理",
|
"pkg": "包管理",
|
||||||
"pkgUpgradeTip": "請在更新前備份系統。",
|
|
||||||
"platformNotSupportUpdate": "當前平台不支持更新,請編譯最新源碼後手動安裝",
|
|
||||||
"plugInType": "插入類型",
|
"plugInType": "插入類型",
|
||||||
"plzEnterHost": "請輸入主機",
|
"port": "埠",
|
||||||
"plzSelectKey": "請選擇私鑰",
|
|
||||||
"port": "端口",
|
|
||||||
"preview": "預覽",
|
"preview": "預覽",
|
||||||
"primaryColorSeed": "主要色調種子",
|
|
||||||
"privateKey": "私鑰",
|
"privateKey": "私鑰",
|
||||||
"process": "進程",
|
"process": "行程",
|
||||||
"pushToken": "消息推送 Token",
|
"pushToken": "消息推送 Token",
|
||||||
"pveIgnoreCertTip": "不建議啟用,請注意安全風險!如果您使用的是 PVE 的默認證書,則需要啟用此選項。",
|
"pveIgnoreCertTip": "不建議啟用,請注意安全風險!如果您使用的是 PVE 的默認證書,則需要啟用此選項。",
|
||||||
"pveLoginFailed": "登錄失敗。無法使用伺服器配置中的使用者名稱/密碼以Linux PAM方式登錄。",
|
"pveLoginFailed": "登錄失敗。無法使用伺服器配置中的使用者名稱/密碼以 Linux PAM 方式登錄。",
|
||||||
"pveVersionLow": "此功能目前處於測試階段,僅在PVE 8+上進行過測試。請謹慎使用。",
|
"pveVersionLow": "此功能目前處於測試階段,僅在 PVE 8+ 上進行過測試。請謹慎使用。",
|
||||||
"pwd": "密碼",
|
"pwd": "密碼",
|
||||||
"read": "读",
|
"read": "读",
|
||||||
"reboot": "重启",
|
"reboot": "重启",
|
||||||
"rememberChoice": "記住選擇",
|
|
||||||
"rememberPwdInMem": "在記憶體中記住密碼",
|
"rememberPwdInMem": "在記憶體中記住密碼",
|
||||||
"rememberPwdInMemTip": "用於容器、暫停等",
|
"rememberPwdInMemTip": "用於容器、暫停等",
|
||||||
"rememberWindowSize": "記住窗口大小",
|
"rememberWindowSize": "記住窗口大小",
|
||||||
"remotePath": "遠端路徑",
|
"remotePath": "遠端路徑",
|
||||||
"rename": "重命名",
|
|
||||||
"reportBugsOnGithubIssue": "請到 {url} 提交問題",
|
|
||||||
"restart": "重啓",
|
"restart": "重啓",
|
||||||
"restore": "恢復",
|
|
||||||
"restoreSuccess": "恢復成功,需要重啓App來應用更改",
|
|
||||||
"result": "結果",
|
"result": "結果",
|
||||||
"rotateAngel": "旋轉角度",
|
"rotateAngel": "旋轉角度",
|
||||||
"route": "路由",
|
"route": "路由",
|
||||||
@@ -248,39 +145,35 @@
|
|||||||
"save": "保存",
|
"save": "保存",
|
||||||
"saved": "已保存",
|
"saved": "已保存",
|
||||||
"second": "秒",
|
"second": "秒",
|
||||||
"sensors": "传感器",
|
"sensors": "感測器",
|
||||||
"sequence": "順序",
|
"sequence": "順序",
|
||||||
"server": "服務器",
|
"server": "伺服器",
|
||||||
"serverDetailOrder": "詳情頁部件順序",
|
"serverDetailOrder": "詳情頁部件順序",
|
||||||
"serverFuncBtns": "服務器功能按鈕",
|
"serverFuncBtns": "伺服器功能按鈕",
|
||||||
"serverOrder": "服務器順序",
|
"serverOrder": "伺服器順序",
|
||||||
"serverTabConnecting": "連接中...",
|
"sftpDlPrepare": "準備連接至伺服器...",
|
||||||
"serverTabEmpty": "現在沒有服務器。\n點擊右下方按鈕來新增。",
|
"sftpEditorTip": "如果為空, 使用App內置的文件編輯器。如果有值, 則使用遠程伺服器的編輯器, 例如 `vim`(建議根據 `EDITOR` 自動獲取)。",
|
||||||
"serverTabFailed": "失敗",
|
|
||||||
"serverTabLoading": "加載中...",
|
|
||||||
"serverTabPlzSave": "請再次保存該私鑰",
|
|
||||||
"serverTabUnkown": "未知狀態",
|
|
||||||
"setting": "設置",
|
|
||||||
"sftpDlPrepare": "準備連接至服務器...",
|
|
||||||
"sftpRmrDirSummary": "在 SFTP 中使用 `rm -r` 來刪除文件夾",
|
"sftpRmrDirSummary": "在 SFTP 中使用 `rm -r` 來刪除文件夾",
|
||||||
"sftpSSHConnected": "SFTP 已連接...",
|
"sftpSSHConnected": "SFTP 已連接...",
|
||||||
"sftpShowFoldersFirst": "文件夾顯示在前",
|
"sftpShowFoldersFirst": "資料夾顯示在前",
|
||||||
"showDistLogo": "顯示發行版 Logo",
|
"showDistLogo": "顯示發行版 Logo",
|
||||||
"shutdown": "关机",
|
"shutdown": "關機",
|
||||||
"size": "大小",
|
"size": "大小",
|
||||||
"snippet": "程式片段",
|
"snippet": "程式片段",
|
||||||
"softWrap": "軟換行",
|
"softWrap": "軟換行",
|
||||||
|
"specifyDev": "指定裝置",
|
||||||
|
"specifyDevTip": "例如網路流量統計預設是所有裝置,你可以在這裡指定特定的裝置。",
|
||||||
"speed": "速度",
|
"speed": "速度",
|
||||||
"spentTime": "耗時: {time}",
|
"spentTime": "耗時: {time}",
|
||||||
"sshTermHelp": "在終端可滾動時,橫向拖動可以選中文字。點擊鍵盤按鈕可以開啟/關閉鍵盤。文件圖標會打開當前路徑SFTP。剪貼板按鈕會在有選中文字時複製內容,在未選中並且剪貼板有內容時貼上內容到終端。代碼圖標會貼上代碼片段到終端並執行。",
|
"sshTermHelp": "在終端可滾動時,橫向拖動可以選中文字。點擊鍵盤按鈕可以開啟/關閉鍵盤。文件圖標會打開當前路徑 SFTP。剪貼簿按鈕會在有選中文字時複製內容,在未選中並且剪貼簿有內容時貼上內容到終端。代碼圖標會貼上代碼片段到終端並執行。",
|
||||||
"sshTip": "該功能目前處於測試階段。\n\n請在 {url} 反饋問題,或者加入我們開發。",
|
"sshTip": "該功能目前處於測試階段。\n\n請在 {url} 反饋問題,或者加入我們開發。",
|
||||||
"sshVirtualKeyAutoOff": "虛擬按鍵自動切換",
|
"sshVirtualKeyAutoOff": "虛擬按鍵自動切換",
|
||||||
"start": "開始",
|
"start": "開始",
|
||||||
|
"stat": "統計",
|
||||||
"stats": "統計",
|
"stats": "統計",
|
||||||
"stop": "停止",
|
"stop": "停止",
|
||||||
"stopped": "已停止",
|
"stopped": "已停止",
|
||||||
"storage": "存儲",
|
"storage": "存儲",
|
||||||
"success": "成功",
|
|
||||||
"supportFmtArgs": "支援以下格式化參數:",
|
"supportFmtArgs": "支援以下格式化參數:",
|
||||||
"suspend": "挂起",
|
"suspend": "挂起",
|
||||||
"suspendTip": "suspend 功能需要 root 權限及 systemd 支持。",
|
"suspendTip": "suspend 功能需要 root 權限及 systemd 支持。",
|
||||||
@@ -290,53 +183,43 @@
|
|||||||
"system": "系統",
|
"system": "系統",
|
||||||
"tag": "标签",
|
"tag": "标签",
|
||||||
"temperature": "溫度",
|
"temperature": "溫度",
|
||||||
"termFontSizeTip": "此設置將影響終端大小(寬度和高度)。您可以在終端頁面縮放,來調整當前會話的字體大小。",
|
"termFontSizeTip": "此設置將影響終端大小(寬度和高度)。您可以在終端頁面縮放,來調整當前會話的字型大小。",
|
||||||
"terminal": "终端機",
|
"terminal": "终端機",
|
||||||
"test": "測試",
|
"test": "測試",
|
||||||
"textScaler": "字體縮放",
|
"textScaler": "字型縮放",
|
||||||
"textScalerTip": "1.0 => 100%(原大小),僅作用於伺服器頁面部分字體,不建議修改。",
|
"textScalerTip": "1.0 => 100%(原大小),僅作用於伺服器頁面部分字型,不建議修改。",
|
||||||
"theme": "主題",
|
"theme": "主題",
|
||||||
"themeMode": "主題模式",
|
|
||||||
"time": "時間",
|
"time": "時間",
|
||||||
"times": "次",
|
"times": "次",
|
||||||
"total": "總共",
|
"total": "總共",
|
||||||
"traffic": "流量",
|
"traffic": "流量",
|
||||||
"trySudo": "嘗試使用sudo",
|
"trySudo": "嘗試使用 sudo",
|
||||||
"ttl": "緩存時間",
|
"ttl": "TTL",
|
||||||
"unknown": "未知",
|
"unknown": "未知",
|
||||||
"unknownError": "未知錯誤",
|
|
||||||
"unkownConvertMode": "未知轉換模式",
|
"unkownConvertMode": "未知轉換模式",
|
||||||
"update": "更新",
|
"update": "更新",
|
||||||
"updateAll": "更新全部",
|
"updateIntervalEqual0": "你設置為 0,伺服器狀態不會自動更新。\n且不能計算CPU使用情況。",
|
||||||
"updateIntervalEqual0": "你設置為0,服務器狀態不會自動更新。\n且不能計算CPU使用情況。",
|
"updateServerStatusInterval": "伺服器狀態更新間隔",
|
||||||
"updateServerStatusInterval": "服務器狀態更新間隔",
|
|
||||||
"updateTip": "新版本: v1.0.{newest}",
|
|
||||||
"updateTipTooLow": "當前版本過低,請升級至 v1.0.{newest}",
|
|
||||||
"upload": "上傳",
|
"upload": "上傳",
|
||||||
"upsideDown": "上下交換",
|
"upsideDown": "上下交換",
|
||||||
"uptime": "啟動時長",
|
"uptime": "啟動時長",
|
||||||
"urlOrJson": "鏈接或JSON",
|
"useCdn": "使用 CDN",
|
||||||
"useCdn": "使用CDN",
|
"useCdnTip": "非中國大陸用戶建議使用 CDN,是否使用?",
|
||||||
"useCdnTip": "非中國大陆用戶建議使用CDN,是否使用?",
|
|
||||||
"useNoPwd": "将使用無密碼",
|
"useNoPwd": "将使用無密碼",
|
||||||
"usePodmanByDefault": "默認使用 Podman",
|
"usePodmanByDefault": "默認使用 Podman",
|
||||||
"used": "已用",
|
"used": "已用",
|
||||||
"user": "用戶",
|
|
||||||
"versionHaveUpdate": "找到新版本:v1.0.{build}, 點擊更新",
|
|
||||||
"versionUnknownUpdate": "當前:v1.0.{build},點擊檢查更新",
|
|
||||||
"versionUpdated": "當前:v1.0.{build}, 已是最新版本",
|
|
||||||
"view": "視圖",
|
"view": "視圖",
|
||||||
"viewErr": "查看錯誤",
|
"viewErr": "查看錯誤",
|
||||||
"virtKeyHelpClipboard": "如果終端有選中字符,則復製選中字符至剪切板,否則粘貼剪切板內容至終端。",
|
"virtKeyHelpClipboard": "如果終端有選中字元,則復製選中字元至剪貼簿,否則粘貼剪貼簿內容至終端。",
|
||||||
"virtKeyHelpIME": "打開/關閉鍵盤",
|
"virtKeyHelpIME": "打開/關閉鍵盤",
|
||||||
"virtKeyHelpSFTP": "在 SFTP 中打開當前路徑。",
|
"virtKeyHelpSFTP": "在 SFTP 中打開當前路徑。",
|
||||||
"waitConnection": "請等待連接建立",
|
"waitConnection": "請等待連接建立",
|
||||||
"wakeLock": "保持喚醒",
|
"wakeLock": "保持喚醒",
|
||||||
"watchNotPaired": "沒有已配對的 Apple Watch",
|
"watchNotPaired": "沒有已配對的 Apple Watch",
|
||||||
"webdavSettingEmpty": "Webdav 設置項爲空",
|
"webdavSettingEmpty": "WebDav 設置項爲空",
|
||||||
"whenOpenApp": "當打開 App 時",
|
"whenOpenApp": "當打開 App 時",
|
||||||
"willTakEeffectImmediately": "更改將會立即生效",
|
"wolTip": "在配置 WOL(網絡喚醒)後,每次連接伺服器都會先發送一次 WOL 請求。",
|
||||||
"wolTip": "在配置WOL(網絡喚醒)後,每次連接伺服器都會先發送一次WOL請求。",
|
|
||||||
"write": "写",
|
"write": "写",
|
||||||
"writeScriptFailTip": "寫入腳本失敗,可能是沒有權限/目錄不存在等。"
|
"writeScriptFailTip": "寫入腳本失敗,可能是沒有權限/目錄不存在等。",
|
||||||
|
"writeScriptTip": "連接到伺服器後,將會在 ~/.config/server_box 中寫入一個腳本來監測系統狀態。你可以審查腳本內容。"
|
||||||
}
|
}
|
||||||
@@ -8,8 +8,6 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_displaymode/flutter_displaymode.dart';
|
import 'package:flutter_displaymode/flutter_displaymode.dart';
|
||||||
import 'package:hive_flutter/hive_flutter.dart';
|
import 'package:hive_flutter/hive_flutter.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
|
||||||
import 'package:server_box/app.dart';
|
import 'package:server_box/app.dart';
|
||||||
import 'package:server_box/core/utils/sync/icloud.dart';
|
import 'package:server_box/core/utils/sync/icloud.dart';
|
||||||
import 'package:server_box/core/utils/sync/webdav.dart';
|
import 'package:server_box/core/utils/sync/webdav.dart';
|
||||||
@@ -22,26 +20,18 @@ import 'package:server_box/data/model/server/server_private_info.dart';
|
|||||||
import 'package:server_box/data/model/server/snippet.dart';
|
import 'package:server_box/data/model/server/snippet.dart';
|
||||||
import 'package:server_box/data/model/server/wol_cfg.dart';
|
import 'package:server_box/data/model/server/wol_cfg.dart';
|
||||||
import 'package:server_box/data/model/ssh/virtual_key.dart';
|
import 'package:server_box/data/model/ssh/virtual_key.dart';
|
||||||
|
import 'package:server_box/data/provider/private_key.dart';
|
||||||
|
import 'package:server_box/data/provider/server.dart';
|
||||||
|
import 'package:server_box/data/provider/sftp.dart';
|
||||||
|
import 'package:server_box/data/provider/snippet.dart';
|
||||||
import 'package:server_box/data/res/build_data.dart';
|
import 'package:server_box/data/res/build_data.dart';
|
||||||
import 'package:server_box/data/res/misc.dart';
|
import 'package:server_box/data/res/misc.dart';
|
||||||
import 'package:server_box/data/res/provider.dart';
|
|
||||||
import 'package:server_box/data/res/store.dart';
|
import 'package:server_box/data/res/store.dart';
|
||||||
|
|
||||||
Future<void> main() async {
|
Future<void> main() async {
|
||||||
_runInZone(() async {
|
_runInZone(() async {
|
||||||
await _initApp();
|
await _initApp();
|
||||||
runApp(
|
runApp(const MyApp());
|
||||||
MultiProvider(
|
|
||||||
providers: [
|
|
||||||
ChangeNotifierProvider(create: (_) => Pros.app),
|
|
||||||
ChangeNotifierProvider(create: (_) => Pros.server),
|
|
||||||
ChangeNotifierProvider(create: (_) => Pros.snippet),
|
|
||||||
ChangeNotifierProvider(create: (_) => Pros.key),
|
|
||||||
ChangeNotifierProvider(create: (_) => Pros.sftp),
|
|
||||||
],
|
|
||||||
child: const MyApp(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,9 +44,7 @@ void _runInZone(void Function() body) {
|
|||||||
|
|
||||||
runZonedGuarded(
|
runZonedGuarded(
|
||||||
body,
|
body,
|
||||||
(obj, trace) {
|
(e, s) => print('[ZONE] $e\n$s'),
|
||||||
Loggers.root.warning(obj, null, trace);
|
|
||||||
},
|
|
||||||
zoneSpecification: zoneSpec,
|
zoneSpecification: zoneSpec,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -70,11 +58,12 @@ Future<void> _initApp() async {
|
|||||||
|
|
||||||
final windowSize = Stores.setting.windowSize;
|
final windowSize = Stores.setting.windowSize;
|
||||||
final hideTitleBar = Stores.setting.hideTitleBar.fetch();
|
final hideTitleBar = Stores.setting.hideTitleBar.fetch();
|
||||||
SystemUIs.initDesktopWindow(
|
await SystemUIs.initDesktopWindow(
|
||||||
hideTitleBar: hideTitleBar,
|
hideTitleBar: hideTitleBar,
|
||||||
size: windowSize.fetch().toSize(),
|
size: windowSize.fetch().toSize(),
|
||||||
listener: WindowSizeListener(windowSize),
|
listener: WindowSizeListener(windowSize),
|
||||||
);
|
);
|
||||||
|
|
||||||
FontUtils.loadFrom(Stores.setting.fontPath.fetch());
|
FontUtils.loadFrom(Stores.setting.fontPath.fetch());
|
||||||
|
|
||||||
_doPlatformRelated();
|
_doPlatformRelated();
|
||||||
@@ -82,28 +71,26 @@ Future<void> _initApp() async {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _initData() async {
|
Future<void> _initData() async {
|
||||||
// await SecureStore.init();
|
|
||||||
await Hive.initFlutter();
|
await Hive.initFlutter();
|
||||||
|
|
||||||
// Ordered by typeId
|
// Ordered by typeId
|
||||||
Hive.registerAdapter(PrivateKeyInfoAdapter()); // 1
|
Hive.registerAdapter(PrivateKeyInfoAdapter()); // 1
|
||||||
Hive.registerAdapter(SnippetAdapter()); // 2
|
Hive.registerAdapter(SnippetAdapter()); // 2
|
||||||
Hive.registerAdapter(ServerPrivateInfoAdapter()); // 3
|
Hive.registerAdapter(SpiAdapter()); // 3
|
||||||
Hive.registerAdapter(VirtKeyAdapter()); // 4
|
Hive.registerAdapter(VirtKeyAdapter()); // 4
|
||||||
Hive.registerAdapter(NetViewTypeAdapter()); // 5
|
Hive.registerAdapter(NetViewTypeAdapter()); // 5
|
||||||
Hive.registerAdapter(ServerFuncBtnAdapter()); // 6
|
Hive.registerAdapter(ServerFuncBtnAdapter()); // 6
|
||||||
Hive.registerAdapter(ServerCustomAdapter()); // 7
|
Hive.registerAdapter(ServerCustomAdapter()); // 7
|
||||||
Hive.registerAdapter(WakeOnLanCfgAdapter()); // 8
|
Hive.registerAdapter(WakeOnLanCfgAdapter()); // 8
|
||||||
|
|
||||||
await Stores.setting.init();
|
await PrefStore.init(); // Call this before accessing any store
|
||||||
await Stores.server.init();
|
await Stores.init();
|
||||||
await Stores.key.init();
|
|
||||||
await Stores.snippet.init();
|
|
||||||
await Stores.container.init();
|
|
||||||
await Stores.history.init();
|
|
||||||
|
|
||||||
Pros.snippet.load();
|
// DO NOT change the order of these providers.
|
||||||
Pros.key.load();
|
PrivateKeyProvider.instance.load();
|
||||||
await Pros.app.init();
|
SnippetProvider.instance.load();
|
||||||
|
ServerProvider.instance.load();
|
||||||
|
SftpProvider.instance.load();
|
||||||
|
|
||||||
if (Stores.setting.betaTest.fetch()) AppUpdate.chan = AppUpdateChan.beta;
|
if (Stores.setting.betaTest.fetch()) AppUpdate.chan = AppUpdateChan.beta;
|
||||||
}
|
}
|
||||||
@@ -111,7 +98,7 @@ Future<void> _initData() async {
|
|||||||
void _setupDebug() {
|
void _setupDebug() {
|
||||||
Logger.root.level = Level.ALL;
|
Logger.root.level = Level.ALL;
|
||||||
Logger.root.onRecord.listen((record) {
|
Logger.root.onRecord.listen((record) {
|
||||||
Pros.debug.addLog(record);
|
DebugProvider.addLog(record);
|
||||||
print(record);
|
print(record);
|
||||||
if (record.error != null) print(record.error);
|
if (record.error != null) print(record.error);
|
||||||
if (record.stackTrace != null) print(record.stackTrace);
|
if (record.stackTrace != null) print(record.stackTrace);
|
||||||
@@ -120,8 +107,6 @@ void _setupDebug() {
|
|||||||
|
|
||||||
void _doPlatformRelated() async {
|
void _doPlatformRelated() async {
|
||||||
if (isAndroid) {
|
if (isAndroid) {
|
||||||
// SharedPreferences is only used on Android for saving home widgets settings.
|
|
||||||
SharedPreferences.setPrefix('');
|
|
||||||
// try switch to highest refresh rate
|
// try switch to highest refresh rate
|
||||||
FlutterDisplayMode.setHighRefreshRate();
|
FlutterDisplayMode.setHighRefreshRate();
|
||||||
}
|
}
|
||||||
@@ -144,6 +129,7 @@ Future<void> _doVersionRelated() async {
|
|||||||
// How to upgrade the data is inside each own func.
|
// How to upgrade the data is inside each own func.
|
||||||
if (curVer < newVer) {
|
if (curVer < newVer) {
|
||||||
ServerDetailCards.autoAddNewCards(newVer);
|
ServerDetailCards.autoAddNewCards(newVer);
|
||||||
|
ServerFuncBtn.autoAddNewFuncs(newVer);
|
||||||
Stores.setting.lastVer.put(newVer);
|
Stores.setting.lastVer.put(newVer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,42 +4,48 @@ 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:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_markdown/flutter_markdown.dart';
|
|
||||||
import 'package:server_box/core/extension/context/locale.dart';
|
import 'package:server_box/core/extension/context/locale.dart';
|
||||||
import 'package:server_box/core/utils/sync/icloud.dart';
|
import 'package:server_box/core/utils/sync/icloud.dart';
|
||||||
import 'package:server_box/core/utils/sync/webdav.dart';
|
import 'package:server_box/core/utils/sync/webdav.dart';
|
||||||
import 'package:server_box/data/model/app/backup.dart';
|
import 'package:server_box/data/model/app/backup.dart';
|
||||||
import 'package:server_box/data/model/server/server_private_info.dart';
|
import 'package:server_box/data/model/server/server_private_info.dart';
|
||||||
|
import 'package:server_box/data/model/server/snippet.dart';
|
||||||
|
import 'package:server_box/data/provider/snippet.dart';
|
||||||
import 'package:server_box/data/res/misc.dart';
|
import 'package:server_box/data/res/misc.dart';
|
||||||
import 'package:server_box/data/res/store.dart';
|
import 'package:server_box/data/res/store.dart';
|
||||||
import 'package:server_box/data/res/url.dart';
|
import 'package:icons_plus/icons_plus.dart';
|
||||||
|
|
||||||
|
final icloudLoading = false.vn;
|
||||||
|
final webdavLoading = false.vn;
|
||||||
|
|
||||||
class BackupPage extends StatelessWidget {
|
class BackupPage extends StatelessWidget {
|
||||||
BackupPage({super.key});
|
const BackupPage({super.key});
|
||||||
|
|
||||||
final icloudLoading = ValueNotifier(false);
|
|
||||||
final webdavLoading = ValueNotifier(false);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: CustomAppBar(
|
appBar: CustomAppBar(title: Text(libL10n.backup)),
|
||||||
title: Text(l10n.backup, style: UIs.text18),
|
|
||||||
),
|
|
||||||
body: _buildBody(context),
|
body: _buildBody(context),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildBody(BuildContext context) {
|
Widget _buildBody(BuildContext context) {
|
||||||
return ListView(
|
return MultiList(
|
||||||
padding: const EdgeInsets.all(17),
|
widthDivider: 2,
|
||||||
children: [
|
children: [
|
||||||
_buildTip(),
|
[
|
||||||
if (isMacOS || isIOS) _buildIcloud(context),
|
CenterGreyTitle(libL10n.sync),
|
||||||
_buildWebdav(context),
|
_buildTip(),
|
||||||
_buildFile(context),
|
if (isMacOS || isIOS) _buildIcloud(context),
|
||||||
_buildClipboard(context),
|
_buildWebdav(context),
|
||||||
_buildBulkImportServers(context),
|
_buildFile(context),
|
||||||
|
_buildClipboard(context),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
CenterGreyTitle(libL10n.import),
|
||||||
|
_buildBulkImportServers(context),
|
||||||
|
_buildImportSnippet(context),
|
||||||
|
],
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -48,7 +54,7 @@ class BackupPage extends StatelessWidget {
|
|||||||
return CardX(
|
return CardX(
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: const Icon(Icons.warning),
|
leading: const Icon(Icons.warning),
|
||||||
title: Text(l10n.attention),
|
title: Text(libL10n.attention),
|
||||||
subtitle: Text(l10n.backupTip, style: UIs.textGrey),
|
subtitle: Text(l10n.backupTip, style: UIs.textGrey),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -58,11 +64,11 @@ class BackupPage extends StatelessWidget {
|
|||||||
return CardX(
|
return CardX(
|
||||||
child: ExpandTile(
|
child: ExpandTile(
|
||||||
leading: const Icon(Icons.file_open),
|
leading: const Icon(Icons.file_open),
|
||||||
title: Text(l10n.files),
|
title: Text(libL10n.file),
|
||||||
initiallyExpanded: true,
|
initiallyExpanded: false,
|
||||||
children: [
|
children: [
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(l10n.backup),
|
title: Text(libL10n.backup),
|
||||||
trailing: const Icon(Icons.save),
|
trailing: const Icon(Icons.save),
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
final path = await Backup.backup();
|
final path = await Backup.backup();
|
||||||
@@ -71,7 +77,7 @@ class BackupPage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
trailing: const Icon(Icons.restore),
|
trailing: const Icon(Icons.restore),
|
||||||
title: Text(l10n.restore),
|
title: Text(libL10n.restore),
|
||||||
onTap: () async => _onTapFileRestore(context),
|
onTap: () async => _onTapFileRestore(context),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -110,15 +116,15 @@ class BackupPage extends StatelessWidget {
|
|||||||
child: ExpandTile(
|
child: ExpandTile(
|
||||||
leading: const Icon(Icons.storage),
|
leading: const Icon(Icons.storage),
|
||||||
title: const Text('WebDAV'),
|
title: const Text('WebDAV'),
|
||||||
initiallyExpanded: true,
|
initiallyExpanded: false,
|
||||||
children: [
|
children: [
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(l10n.setting),
|
title: Text(libL10n.setting),
|
||||||
trailing: const Icon(Icons.settings),
|
trailing: const Icon(Icons.settings),
|
||||||
onTap: () async => _onTapWebdavSetting(context),
|
onTap: () async => _onTapWebdavSetting(context),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(l10n.auto),
|
title: Text(libL10n.auto),
|
||||||
trailing: StoreSwitch(
|
trailing: StoreSwitch(
|
||||||
prop: Stores.setting.webdavSync,
|
prop: Stores.setting.webdavSync,
|
||||||
validator: (p0) {
|
validator: (p0) {
|
||||||
@@ -150,20 +156,19 @@ class BackupPage extends StatelessWidget {
|
|||||||
trailing: ListenableBuilder(
|
trailing: ListenableBuilder(
|
||||||
listenable: webdavLoading,
|
listenable: webdavLoading,
|
||||||
builder: (_, __) {
|
builder: (_, __) {
|
||||||
if (webdavLoading.value) {
|
if (webdavLoading.value) return SizedLoading.centerSmall;
|
||||||
return UIs.centerSizedLoadingSmall;
|
|
||||||
}
|
|
||||||
return Row(
|
return Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () async => _onTapWebdavDl(context),
|
onPressed: () async => _onTapWebdavDl(context),
|
||||||
child: Text(l10n.restore),
|
child: Text(libL10n.restore),
|
||||||
),
|
),
|
||||||
UIs.width7,
|
UIs.width7,
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () async => _onTapWebdavUp(context),
|
onPressed: () async => _onTapWebdavUp(context),
|
||||||
child: Text(l10n.backup),
|
child: Text(libL10n.backup),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -179,20 +184,20 @@ class BackupPage extends StatelessWidget {
|
|||||||
return CardX(
|
return CardX(
|
||||||
child: ExpandTile(
|
child: ExpandTile(
|
||||||
leading: const Icon(Icons.content_paste),
|
leading: const Icon(Icons.content_paste),
|
||||||
title: Text(l10n.clipboard),
|
title: Text(libL10n.clipboard),
|
||||||
children: [
|
children: [
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(l10n.backup),
|
title: Text(libL10n.backup),
|
||||||
trailing: const Icon(Icons.save),
|
trailing: const Icon(Icons.save),
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
final path = await Backup.backup();
|
final path = await Backup.backup();
|
||||||
Pfs.copy(await File(path).readAsString());
|
Pfs.copy(await File(path).readAsString());
|
||||||
context.showSnackBar(l10n.success);
|
context.showSnackBar(libL10n.success);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
trailing: const Icon(Icons.restore),
|
trailing: const Icon(Icons.restore),
|
||||||
title: Text(l10n.restore),
|
title: Text(libL10n.restore),
|
||||||
onTap: () async => _onTapClipboardRestore(context),
|
onTap: () async => _onTapClipboardRestore(context),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -203,55 +208,104 @@ class BackupPage extends StatelessWidget {
|
|||||||
Widget _buildBulkImportServers(BuildContext context) {
|
Widget _buildBulkImportServers(BuildContext context) {
|
||||||
return CardX(
|
return CardX(
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
title: Text(l10n.bulkImportServers),
|
title: Text(l10n.server),
|
||||||
subtitle: SimpleMarkdown(
|
leading: const Icon(BoxIcons.bx_server),
|
||||||
data: l10n.bulkImportServersTip(Urls.appWiki),
|
|
||||||
styleSheet: MarkdownStyleSheet(
|
|
||||||
p: UIs.textGrey,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
leading: const Icon(Icons.import_export),
|
|
||||||
onTap: () => _onBulkImportServers(context),
|
onTap: () => _onBulkImportServers(context),
|
||||||
trailing: const Icon(Icons.keyboard_arrow_right),
|
trailing: const Icon(Icons.keyboard_arrow_right),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildImportSnippet(BuildContext context) {
|
||||||
|
return ListTile(
|
||||||
|
title: Text(l10n.snippet),
|
||||||
|
leading: const Icon(MingCute.code_line),
|
||||||
|
trailing: const Icon(Icons.keyboard_arrow_right),
|
||||||
|
onTap: () async {
|
||||||
|
final data = await context.showImportDialog(
|
||||||
|
title: l10n.snippet,
|
||||||
|
modelDef: Snippet.example.toJson(),
|
||||||
|
);
|
||||||
|
if (data == null) return;
|
||||||
|
final str = String.fromCharCodes(data);
|
||||||
|
final (list, _) = await context.showLoadingDialog(
|
||||||
|
fn: () => Computer.shared.start((s) {
|
||||||
|
return json.decode(s) as List;
|
||||||
|
}, str),
|
||||||
|
);
|
||||||
|
if (list == null || list.isEmpty) return;
|
||||||
|
final snippets = <Snippet>[];
|
||||||
|
final errs = <String>[];
|
||||||
|
for (final item in list) {
|
||||||
|
try {
|
||||||
|
final snippet = Snippet.fromJson(item);
|
||||||
|
snippets.add(snippet);
|
||||||
|
} catch (e) {
|
||||||
|
errs.add(e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (snippets.isEmpty) {
|
||||||
|
context.showSnackBar(libL10n.empty);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (errs.isNotEmpty) {
|
||||||
|
context.showRoundDialog(
|
||||||
|
title: libL10n.error,
|
||||||
|
child: SingleChildScrollView(child: Text(errs.join('\n'))),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final snippetNames = snippets.map((e) => e.name).join(', ');
|
||||||
|
context.showRoundDialog(
|
||||||
|
title: libL10n.attention,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: Text(
|
||||||
|
libL10n.askContinue('${libL10n.import} [$snippetNames]'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
actions: Btn.ok(
|
||||||
|
onTap: () {
|
||||||
|
for (final snippet in snippets) {
|
||||||
|
SnippetProvider.add(snippet);
|
||||||
|
}
|
||||||
|
context.pop();
|
||||||
|
context.pop();
|
||||||
|
},
|
||||||
|
).toList,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
).cardx;
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _onTapFileRestore(BuildContext context) async {
|
Future<void> _onTapFileRestore(BuildContext context) async {
|
||||||
final text = await Pfs.pickFileString();
|
final text = await Pfs.pickFileString();
|
||||||
if (text == null) return;
|
if (text == null) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final backup = await context.showLoadingDialog(
|
final (backup, err) = await context.showLoadingDialog(
|
||||||
fn: () => Computer.shared.start(Backup.fromJsonString, text.trim()),
|
fn: () => Computer.shared.start(Backup.fromJsonString, text.trim()),
|
||||||
);
|
);
|
||||||
|
if (err != null || backup == null) return;
|
||||||
if (backupFormatVersion != backup.version) {
|
if (backupFormatVersion != backup.version) {
|
||||||
context.showSnackBar(l10n.backupVersionNotMatch);
|
context.showSnackBar(l10n.backupVersionNotMatch);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await context.showRoundDialog(
|
await context.showRoundDialog(
|
||||||
title: l10n.restore,
|
title: libL10n.restore,
|
||||||
child: Text(l10n.askContinue(
|
child: Text(libL10n.askContinue(
|
||||||
'${l10n.restore} ${l10n.backup}(${backup.date})',
|
'${libL10n.restore} ${libL10n.backup}(${backup.date})',
|
||||||
)),
|
)),
|
||||||
actions: [
|
actions: Btn.ok(
|
||||||
TextButton(
|
onTap: () async {
|
||||||
onPressed: () => context.pop(),
|
await backup.restore(force: true);
|
||||||
child: Text(l10n.cancel),
|
context.pop();
|
||||||
),
|
},
|
||||||
TextButton(
|
).toList,
|
||||||
onPressed: () async {
|
|
||||||
await backup.restore(force: true);
|
|
||||||
context.pop();
|
|
||||||
},
|
|
||||||
child: Text(l10n.ok),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Loggers.app.warning('Import backup failed', e, s);
|
Loggers.app.warning('Import backup failed', e, s);
|
||||||
context.showErrDialog(e: e, s: s, operation: l10n.restore);
|
context.showErrDialog(e, s, libL10n.restore);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,7 +316,7 @@ class BackupPage extends StatelessWidget {
|
|||||||
if (files.isEmpty) return context.showSnackBar(l10n.dirEmpty);
|
if (files.isEmpty) return context.showSnackBar(l10n.dirEmpty);
|
||||||
|
|
||||||
final fileName = await context.showPickSingleDialog(
|
final fileName = await context.showPickSingleDialog(
|
||||||
title: l10n.restore,
|
title: libL10n.restore,
|
||||||
items: files,
|
items: files,
|
||||||
);
|
);
|
||||||
if (fileName == null) return;
|
if (fileName == null) return;
|
||||||
@@ -275,7 +329,7 @@ class BackupPage extends StatelessWidget {
|
|||||||
final dlBak = await Computer.shared.start(Backup.fromJsonString, dlFile);
|
final dlBak = await Computer.shared.start(Backup.fromJsonString, dlFile);
|
||||||
await dlBak.restore(force: true);
|
await dlBak.restore(force: true);
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
context.showErrDialog(e: e, s: s, operation: l10n.restore);
|
context.showErrDialog(e, s, libL10n.restore);
|
||||||
Loggers.app.warning('Download webdav backup failed', e, s);
|
Loggers.app.warning('Download webdav backup failed', e, s);
|
||||||
} finally {
|
} finally {
|
||||||
webdavLoading.value = false;
|
webdavLoading.value = false;
|
||||||
@@ -284,7 +338,7 @@ class BackupPage extends StatelessWidget {
|
|||||||
|
|
||||||
Future<void> _onTapWebdavUp(BuildContext context) async {
|
Future<void> _onTapWebdavUp(BuildContext context) async {
|
||||||
webdavLoading.value = true;
|
webdavLoading.value = true;
|
||||||
final date = DateTime.now().ymdhms(ymdSep: "-", hmsSep: "-", sep: "-");
|
final date = DateTime.now().ymdhms(ymdSep: '-', hmsSep: '-', sep: '-');
|
||||||
final bakName = '$date-${Miscs.bakFileName}';
|
final bakName = '$date-${Miscs.bakFileName}';
|
||||||
try {
|
try {
|
||||||
await Backup.backup(bakName);
|
await Backup.backup(bakName);
|
||||||
@@ -294,7 +348,7 @@ class BackupPage extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
Loggers.app.info('Upload webdav backup success');
|
Loggers.app.info('Upload webdav backup success');
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
context.showErrDialog(e: e, s: s, operation: l10n.upload);
|
context.showErrDialog(e, s, l10n.upload);
|
||||||
Loggers.app.warning('Upload webdav backup failed', e, s);
|
Loggers.app.warning('Upload webdav backup failed', e, s);
|
||||||
} finally {
|
} finally {
|
||||||
webdavLoading.value = false;
|
webdavLoading.value = false;
|
||||||
@@ -305,6 +359,8 @@ class BackupPage extends StatelessWidget {
|
|||||||
final url = TextEditingController(text: Stores.setting.webdavUrl.fetch());
|
final url = TextEditingController(text: Stores.setting.webdavUrl.fetch());
|
||||||
final user = TextEditingController(text: Stores.setting.webdavUser.fetch());
|
final user = TextEditingController(text: Stores.setting.webdavUser.fetch());
|
||||||
final pwd = TextEditingController(text: Stores.setting.webdavPwd.fetch());
|
final pwd = TextEditingController(text: Stores.setting.webdavPwd.fetch());
|
||||||
|
final nodeUser = FocusNode();
|
||||||
|
final nodePwd = FocusNode();
|
||||||
final result = await context.showRoundDialog<bool>(
|
final result = await context.showRoundDialog<bool>(
|
||||||
title: 'WebDAV',
|
title: 'WebDAV',
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -314,25 +370,26 @@ class BackupPage extends StatelessWidget {
|
|||||||
label: 'URL',
|
label: 'URL',
|
||||||
hint: 'https://example.com/webdav/',
|
hint: 'https://example.com/webdav/',
|
||||||
controller: url,
|
controller: url,
|
||||||
|
suggestion: false,
|
||||||
|
onSubmitted: (p0) => FocusScope.of(context).requestFocus(nodeUser),
|
||||||
),
|
),
|
||||||
Input(
|
Input(
|
||||||
label: l10n.user,
|
label: libL10n.user,
|
||||||
controller: user,
|
controller: user,
|
||||||
|
node: nodeUser,
|
||||||
|
suggestion: false,
|
||||||
|
onSubmitted: (p0) => FocusScope.of(context).requestFocus(nodePwd),
|
||||||
),
|
),
|
||||||
Input(
|
Input(
|
||||||
label: l10n.pwd,
|
label: l10n.pwd,
|
||||||
controller: pwd,
|
controller: pwd,
|
||||||
|
node: nodePwd,
|
||||||
|
suggestion: false,
|
||||||
|
onSubmitted: (_) => context.pop(true),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
actions: [
|
actions: Btnx.oks,
|
||||||
TextButton(
|
|
||||||
onPressed: () {
|
|
||||||
context.pop(true);
|
|
||||||
},
|
|
||||||
child: Text(l10n.ok),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
if (result == true) {
|
if (result == true) {
|
||||||
final result = await Webdav.test(url.text, user.text, pwd.text);
|
final result = await Webdav.test(url.text, user.text, pwd.text);
|
||||||
@@ -340,7 +397,7 @@ class BackupPage extends StatelessWidget {
|
|||||||
context.showSnackBar(result);
|
context.showSnackBar(result);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
context.showSnackBar(l10n.success);
|
context.showSnackBar(libL10n.success);
|
||||||
Webdav.changeClient(url.text, user.text, pwd.text);
|
Webdav.changeClient(url.text, user.text, pwd.text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -348,14 +405,15 @@ class BackupPage extends StatelessWidget {
|
|||||||
void _onTapClipboardRestore(BuildContext context) async {
|
void _onTapClipboardRestore(BuildContext context) async {
|
||||||
final text = await Pfs.paste();
|
final text = await Pfs.paste();
|
||||||
if (text == null || text.isEmpty) {
|
if (text == null || text.isEmpty) {
|
||||||
context.showSnackBar(l10n.fieldMustNotEmpty);
|
context.showSnackBar(libL10n.empty);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final backup = await context.showLoadingDialog(
|
final (backup, err) = await context.showLoadingDialog(
|
||||||
fn: () => Computer.shared.start(Backup.fromJsonString, text.trim()),
|
fn: () => Computer.shared.start(Backup.fromJsonString, text.trim()),
|
||||||
);
|
);
|
||||||
|
if (err != null || backup == null) return;
|
||||||
|
|
||||||
if (backupFormatVersion != backup.version) {
|
if (backupFormatVersion != backup.version) {
|
||||||
context.showSnackBar(l10n.backupVersionNotMatch);
|
context.showSnackBar(l10n.backupVersionNotMatch);
|
||||||
@@ -363,63 +421,58 @@ class BackupPage extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await context.showRoundDialog(
|
await context.showRoundDialog(
|
||||||
title: l10n.restore,
|
title: libL10n.restore,
|
||||||
child: Text(l10n.askContinue(
|
child: Text(libL10n.askContinue(
|
||||||
'${l10n.restore} ${l10n.backup}(${backup.date})',
|
'${libL10n.restore} ${libL10n.backup}(${backup.date})',
|
||||||
)),
|
)),
|
||||||
actions: [
|
actions: Btn.ok(
|
||||||
TextButton(
|
onTap: () async {
|
||||||
onPressed: () => context.pop(),
|
await backup.restore(force: true);
|
||||||
child: Text(l10n.cancel),
|
context.pop();
|
||||||
),
|
},
|
||||||
TextButton(
|
).toList,
|
||||||
onPressed: () async {
|
|
||||||
await backup.restore(force: true);
|
|
||||||
context.pop();
|
|
||||||
},
|
|
||||||
child: Text(l10n.ok),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Loggers.app.warning('Import backup failed', e, s);
|
Loggers.app.warning('Import backup failed', e, s);
|
||||||
context.showErrDialog(e: e, s: s, operation: l10n.restore);
|
context.showErrDialog(e, s, libL10n.restore);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onBulkImportServers(BuildContext context) async {
|
void _onBulkImportServers(BuildContext context) async {
|
||||||
final text = await Pfs.pickFileString();
|
final data = await context.showImportDialog(
|
||||||
if (text == null) return;
|
title: l10n.server,
|
||||||
|
modelDef: Spix.example.toJson(),
|
||||||
|
);
|
||||||
|
if (data == null) return;
|
||||||
|
final text = String.fromCharCodes(data);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final spis = await context.showLoadingDialog(
|
final (spis, err) = await context.showLoadingDialog(
|
||||||
fn: () => Computer.shared.start((val) {
|
fn: () => Computer.shared.start((val) {
|
||||||
final list = json.decode(val) as List;
|
final list = json.decode(val) as List;
|
||||||
return list.map((e) => ServerPrivateInfo.fromJson(e)).toList();
|
return list.map((e) => Spi.fromJson(e)).toList();
|
||||||
}, text.trim()),
|
}, text.trim()),
|
||||||
);
|
);
|
||||||
|
if (err != null || spis == null) return;
|
||||||
final sure = await context.showRoundDialog<bool>(
|
final sure = await context.showRoundDialog<bool>(
|
||||||
title: l10n.import,
|
title: libL10n.import,
|
||||||
child: Text(l10n.askContinue('${spis.length} ${l10n.server}')),
|
child: Text(libL10n.askContinue('${spis.length} ${l10n.server}')),
|
||||||
actions: [
|
actions: Btnx.oks,
|
||||||
TextButton(
|
|
||||||
onPressed: () => context.pop(true),
|
|
||||||
child: Text(l10n.ok),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
if (sure == true) {
|
if (sure == true) {
|
||||||
await context.showLoadingDialog(
|
final (suc, err) = await context.showLoadingDialog(
|
||||||
fn: () async {
|
fn: () async {
|
||||||
for (var spi in spis) {
|
for (var spi in spis) {
|
||||||
Stores.server.put(spi);
|
Stores.server.put(spi);
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
context.showSnackBar(l10n.success);
|
if (err != null || suc != true) return;
|
||||||
|
context.showSnackBar(libL10n.success);
|
||||||
}
|
}
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
context.showErrDialog(e: e, s: s, operation: l10n.import);
|
context.showErrDialog(e, s, libL10n.import);
|
||||||
Loggers.app.warning('Import servers failed', e, s);
|
Loggers.app.warning('Import servers failed', e, s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,13 +11,13 @@ import 'package:server_box/data/model/container/image.dart';
|
|||||||
import 'package:server_box/data/model/container/type.dart';
|
import 'package:server_box/data/model/container/type.dart';
|
||||||
import 'package:server_box/data/res/store.dart';
|
import 'package:server_box/data/res/store.dart';
|
||||||
|
|
||||||
import '../../data/model/container/ps.dart';
|
import 'package:server_box/data/model/container/ps.dart';
|
||||||
import '../../data/model/server/server_private_info.dart';
|
import 'package:server_box/data/model/server/server_private_info.dart';
|
||||||
import '../../data/provider/container.dart';
|
import 'package:server_box/data/provider/container.dart';
|
||||||
import '../widget/two_line_text.dart';
|
import 'package:server_box/view/widget/two_line_text.dart';
|
||||||
|
|
||||||
class ContainerPage extends StatefulWidget {
|
class ContainerPage extends StatefulWidget {
|
||||||
final ServerPrivateInfo spi;
|
final Spi spi;
|
||||||
const ContainerPage({required this.spi, super.key});
|
const ContainerPage({required this.spi, super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -27,7 +27,7 @@ class ContainerPage extends StatefulWidget {
|
|||||||
class _ContainerPageState extends State<ContainerPage> {
|
class _ContainerPageState extends State<ContainerPage> {
|
||||||
final _textController = TextEditingController();
|
final _textController = TextEditingController();
|
||||||
late final _container = ContainerProvider(
|
late final _container = ContainerProvider(
|
||||||
client: widget.spi.server?.client,
|
client: widget.spi.server?.value.client,
|
||||||
userName: widget.spi.user,
|
userName: widget.spi.user,
|
||||||
hostId: widget.spi.id,
|
hostId: widget.spi.id,
|
||||||
context: context,
|
context: context,
|
||||||
@@ -98,7 +98,7 @@ class _ContainerPageState extends State<ContainerPage> {
|
|||||||
UIs.height13,
|
UIs.height13,
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 23),
|
padding: const EdgeInsets.symmetric(horizontal: 23),
|
||||||
child: Text(_container.error?.toString() ?? l10n.unknownError),
|
child: Text(_container.error.toString()),
|
||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
_buildEditHost(),
|
_buildEditHost(),
|
||||||
@@ -285,16 +285,13 @@ class _ContainerPageState extends State<ContainerPage> {
|
|||||||
if (emptyPs && emptyImgs) {
|
if (emptyPs && emptyImgs) {
|
||||||
children.add(Padding(
|
children.add(Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(17, 17, 17, 0),
|
padding: const EdgeInsets.fromLTRB(17, 17, 17, 0),
|
||||||
child: Text(
|
child: SimpleMarkdown(data: l10n.dockerEmptyRunningItems),
|
||||||
l10n.dockerEmptyRunningItems,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
children.add(
|
children.add(
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: _showEditHostDialog,
|
onPressed: _showEditHostDialog,
|
||||||
child: Text(l10n.dockerEditHost),
|
child: Text('${libL10n.edit} DOCKER_HOST'),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
return CardX(
|
return CardX(
|
||||||
@@ -328,51 +325,44 @@ class _ContainerPageState extends State<ContainerPage> {
|
|||||||
final nameCtrl = TextEditingController();
|
final nameCtrl = TextEditingController();
|
||||||
final argsCtrl = TextEditingController();
|
final argsCtrl = TextEditingController();
|
||||||
await context.showRoundDialog(
|
await context.showRoundDialog(
|
||||||
title: l10n.newContainer,
|
title: l10n.newContainer,
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Input(
|
Input(
|
||||||
autoFocus: true,
|
autoFocus: true,
|
||||||
type: TextInputType.text,
|
type: TextInputType.text,
|
||||||
label: l10n.image,
|
label: l10n.image,
|
||||||
hint: 'xxx:1.1',
|
hint: 'xxx:1.1',
|
||||||
controller: imageCtrl,
|
controller: imageCtrl,
|
||||||
),
|
suggestion: false,
|
||||||
Input(
|
),
|
||||||
type: TextInputType.text,
|
Input(
|
||||||
controller: nameCtrl,
|
type: TextInputType.text,
|
||||||
label: l10n.containerName,
|
controller: nameCtrl,
|
||||||
hint: 'xxx',
|
label: libL10n.name,
|
||||||
),
|
hint: 'xxx',
|
||||||
Input(
|
suggestion: false,
|
||||||
type: TextInputType.text,
|
),
|
||||||
controller: argsCtrl,
|
Input(
|
||||||
label: l10n.extraArgs,
|
type: TextInputType.text,
|
||||||
hint: '-p 2222:22 -v ~/.xxx/:/xxx',
|
controller: argsCtrl,
|
||||||
),
|
label: l10n.extraArgs,
|
||||||
],
|
hint: '-p 2222:22 -v ~/.xxx/:/xxx',
|
||||||
),
|
suggestion: false,
|
||||||
actions: [
|
),
|
||||||
TextButton(
|
],
|
||||||
onPressed: () => context.pop(),
|
|
||||||
child: Text(l10n.cancel),
|
|
||||||
),
|
),
|
||||||
TextButton(
|
actions: Btn.ok(onTap: () async {
|
||||||
onPressed: () async {
|
context.pop();
|
||||||
context.pop();
|
await _showAddCmdPreview(
|
||||||
await _showAddCmdPreview(
|
_buildAddCmd(
|
||||||
_buildAddCmd(
|
imageCtrl.text.trim(),
|
||||||
imageCtrl.text.trim(),
|
nameCtrl.text.trim(),
|
||||||
nameCtrl.text.trim(),
|
argsCtrl.text.trim(),
|
||||||
argsCtrl.text.trim(),
|
),
|
||||||
),
|
);
|
||||||
);
|
}).toList);
|
||||||
},
|
|
||||||
child: Text(l10n.ok),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _showAddCmdPreview(String cmd) async {
|
Future<void> _showAddCmdPreview(String cmd) async {
|
||||||
@@ -382,17 +372,21 @@ class _ContainerPageState extends State<ContainerPage> {
|
|||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => context.pop(),
|
onPressed: () => context.pop(),
|
||||||
child: Text(l10n.cancel),
|
child: Text(libL10n.cancel),
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
context.pop();
|
context.pop();
|
||||||
|
|
||||||
final result = await context.showLoadingDialog(
|
final (result, err) = await context.showLoadingDialog(
|
||||||
fn: () => _container.run(cmd),
|
fn: () => _container.run(cmd),
|
||||||
);
|
);
|
||||||
if (result != null) {
|
if (err != null || result != null) {
|
||||||
context.showSnackBar(result.message ?? l10n.unknownError);
|
final e = result?.message ?? err?.toString();
|
||||||
|
context.showRoundDialog(
|
||||||
|
title: libL10n.error,
|
||||||
|
child: Text(e.toString()),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: Text(l10n.run),
|
child: Text(l10n.run),
|
||||||
@@ -419,19 +413,15 @@ class _ContainerPageState extends State<ContainerPage> {
|
|||||||
final host = Stores.container.fetch(id);
|
final host = Stores.container.fetch(id);
|
||||||
final ctrl = TextEditingController(text: host);
|
final ctrl = TextEditingController(text: host);
|
||||||
await context.showRoundDialog(
|
await context.showRoundDialog(
|
||||||
title: l10n.dockerEditHost,
|
title: libL10n.edit,
|
||||||
child: Input(
|
child: Input(
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
controller: ctrl,
|
controller: ctrl,
|
||||||
onSubmitted: _onSaveDockerHost,
|
onSubmitted: _onSaveDockerHost,
|
||||||
hint: 'unix:///run/user/1000/docker.sock',
|
hint: 'unix:///run/user/1000/docker.sock',
|
||||||
|
suggestion: false,
|
||||||
),
|
),
|
||||||
actions: [
|
actions: Btn.ok(onTap: () => _onSaveDockerHost(ctrl.text)).toList,
|
||||||
TextButton(
|
|
||||||
onPressed: () => _onSaveDockerHost(ctrl.text),
|
|
||||||
child: Text(l10n.ok),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -443,24 +433,20 @@ class _ContainerPageState extends State<ContainerPage> {
|
|||||||
|
|
||||||
void _showImageRmDialog(ContainerImg e) {
|
void _showImageRmDialog(ContainerImg e) {
|
||||||
context.showRoundDialog(
|
context.showRoundDialog(
|
||||||
title: l10n.attention,
|
title: libL10n.attention,
|
||||||
child: Text(l10n.askContinue('${l10n.delete} Image(${e.repository})')),
|
child: Text(
|
||||||
actions: [
|
libL10n.askContinue('${libL10n.delete} Image(${e.repository})'),
|
||||||
TextButton(
|
),
|
||||||
onPressed: () => context.pop(),
|
actions: Btn.ok(
|
||||||
child: Text(l10n.cancel),
|
onTap: () async {
|
||||||
),
|
context.pop();
|
||||||
TextButton(
|
final result = await _container.run('rmi ${e.id} -f');
|
||||||
onPressed: () async {
|
if (result != null) {
|
||||||
context.pop();
|
context.showSnackBar(result.message ?? 'null');
|
||||||
final result = await _container.run('rmi ${e.id} -f');
|
}
|
||||||
if (result != null) {
|
},
|
||||||
context.showSnackBar(result.message ?? l10n.unknownError);
|
red: true,
|
||||||
}
|
).toList,
|
||||||
},
|
|
||||||
child: Text(l10n.ok, style: UIs.textRed),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -474,12 +460,12 @@ class _ContainerPageState extends State<ContainerPage> {
|
|||||||
case ContainerMenu.rm:
|
case ContainerMenu.rm:
|
||||||
var force = false;
|
var force = false;
|
||||||
context.showRoundDialog(
|
context.showRoundDialog(
|
||||||
title: l10n.attention,
|
title: libL10n.attention,
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Text(l10n.askContinue(
|
Text(libL10n.askContinue(
|
||||||
'${l10n.delete} Container(${dItem.name})',
|
'${libL10n.delete} Container(${dItem.name})',
|
||||||
)),
|
)),
|
||||||
UIs.height13,
|
UIs.height13,
|
||||||
Row(
|
Row(
|
||||||
@@ -497,56 +483,55 @@ class _ContainerPageState extends State<ContainerPage> {
|
|||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
actions: [
|
actions: Btn.ok(onTap: () async {
|
||||||
TextButton(
|
context.pop();
|
||||||
onPressed: () async {
|
|
||||||
context.pop();
|
|
||||||
|
|
||||||
final result = await context.showLoadingDialog(
|
final (result, err) = await context.showLoadingDialog(
|
||||||
fn: () => _container.delete(id, force),
|
fn: () => _container.delete(id, force),
|
||||||
);
|
);
|
||||||
if (result != null) {
|
if (err != null || result != null) {
|
||||||
context.showRoundDialog(
|
final e = result?.message ?? err?.toString();
|
||||||
title: l10n.error,
|
context.showRoundDialog(
|
||||||
child: Text(result.message ?? l10n.unknownError),
|
title: libL10n.error,
|
||||||
);
|
child: Text(e ?? 'null'),
|
||||||
}
|
);
|
||||||
},
|
}
|
||||||
child: Text(l10n.ok),
|
}).toList,
|
||||||
)
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case ContainerMenu.start:
|
case ContainerMenu.start:
|
||||||
final result = await context.showLoadingDialog(
|
final (result, err) = await context.showLoadingDialog(
|
||||||
fn: () => _container.start(id),
|
fn: () => _container.start(id),
|
||||||
);
|
);
|
||||||
if (result != null) {
|
if (err != null || result != null) {
|
||||||
|
final e = result?.message ?? err?.toString();
|
||||||
context.showRoundDialog(
|
context.showRoundDialog(
|
||||||
title: l10n.error,
|
title: libL10n.error,
|
||||||
child: Text(result.message ?? l10n.unknownError),
|
child: Text(e ?? 'null'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ContainerMenu.stop:
|
case ContainerMenu.stop:
|
||||||
final result = await context.showLoadingDialog(
|
final (result, err) = await context.showLoadingDialog(
|
||||||
fn: () => _container.stop(id),
|
fn: () => _container.stop(id),
|
||||||
);
|
);
|
||||||
if (result != null) {
|
if (err != null || result != null) {
|
||||||
|
final e = result?.message ?? err?.toString();
|
||||||
context.showRoundDialog(
|
context.showRoundDialog(
|
||||||
title: l10n.error,
|
title: libL10n.error,
|
||||||
child: Text(result.message ?? l10n.unknownError),
|
child: Text(e ?? 'null'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ContainerMenu.restart:
|
case ContainerMenu.restart:
|
||||||
final result = await context.showLoadingDialog(
|
final (result, err) = await context.showLoadingDialog(
|
||||||
fn: () => _container.restart(id),
|
fn: () => _container.restart(id),
|
||||||
);
|
);
|
||||||
if (result != null) {
|
if (err != null || result != null) {
|
||||||
|
final e = result?.message ?? err?.toString();
|
||||||
context.showRoundDialog(
|
context.showRoundDialog(
|
||||||
title: l10n.error,
|
title: libL10n.error,
|
||||||
child: Text(result.message ?? l10n.unknownError),
|
child: Text(e ?? 'null'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import 'package:server_box/core/extension/context/locale.dart';
|
|||||||
import 'package:server_box/data/res/highlight.dart';
|
import 'package:server_box/data/res/highlight.dart';
|
||||||
import 'package:server_box/data/res/store.dart';
|
import 'package:server_box/data/res/store.dart';
|
||||||
|
|
||||||
import '../widget/two_line_text.dart';
|
import 'package:server_box/view/widget/two_line_text.dart';
|
||||||
|
|
||||||
class EditorPage extends StatefulWidget {
|
class EditorPage extends StatefulWidget {
|
||||||
/// If path is not null, then it's a file editor
|
/// If path is not null, then it's a file editor
|
||||||
@@ -123,7 +123,7 @@ class _EditorPageState extends State<EditorPage> {
|
|||||||
actions: [
|
actions: [
|
||||||
PopupMenuButton<String>(
|
PopupMenuButton<String>(
|
||||||
icon: const Icon(Icons.language),
|
icon: const Icon(Icons.language),
|
||||||
tooltip: l10n.language,
|
tooltip: libL10n.language,
|
||||||
onSelected: (value) {
|
onSelected: (value) {
|
||||||
_controller.language = Highlights.all[value];
|
_controller.language = Highlights.all[value];
|
||||||
_langCode = value;
|
_langCode = value;
|
||||||
@@ -145,10 +145,13 @@ class _EditorPageState extends State<EditorPage> {
|
|||||||
// If path is not null, then it's a file editor
|
// If path is not null, then it's a file editor
|
||||||
// save the text and return true to pop the page
|
// save the text and return true to pop the page
|
||||||
if (widget.path != null) {
|
if (widget.path != null) {
|
||||||
await context.showLoadingDialog(
|
final (res, _) = await context.showLoadingDialog(
|
||||||
fn: () => File(widget.path!).writeAsString(_controller.text),
|
fn: () => File(widget.path!).writeAsString(_controller.text),
|
||||||
);
|
);
|
||||||
|
if (res == null) {
|
||||||
|
context.showSnackBar(libL10n.fail);
|
||||||
|
return;
|
||||||
|
}
|
||||||
context.pop(true);
|
context.pop(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,17 +17,19 @@ final class _AppBar extends CustomAppBar {
|
|||||||
final placeholder = SizedBox(
|
final placeholder = SizedBox(
|
||||||
height: CustomAppBar.barHeight ?? 0 + MediaQuery.of(context).padding.top,
|
height: CustomAppBar.barHeight ?? 0 + MediaQuery.of(context).padding.top,
|
||||||
);
|
);
|
||||||
return ValBuilder(
|
return selectIndex.listenVal(
|
||||||
listenable: landscape,
|
(idx) {
|
||||||
builder: (ls) {
|
if (idx == AppTab.ssh.index) {
|
||||||
if (ls) return placeholder;
|
return placeholder;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isDesktop) return super.build(context);
|
||||||
|
|
||||||
return ValBuilder(
|
return ValBuilder(
|
||||||
listenable: selectIndex,
|
listenable: landscape,
|
||||||
builder: (idx) {
|
builder: (ls) {
|
||||||
if (idx == AppTab.ssh.index && !isWindows && !isLinux) {
|
if (ls) return placeholder;
|
||||||
return placeholder;
|
|
||||||
}
|
|
||||||
return super.build(context);
|
return super.build(context);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -8,13 +8,13 @@ import 'package:server_box/core/extension/build.dart';
|
|||||||
import 'package:server_box/core/extension/context/locale.dart';
|
import 'package:server_box/core/extension/context/locale.dart';
|
||||||
import 'package:server_box/core/route.dart';
|
import 'package:server_box/core/route.dart';
|
||||||
import 'package:server_box/data/model/app/tab.dart';
|
import 'package:server_box/data/model/app/tab.dart';
|
||||||
|
import 'package:server_box/data/provider/app.dart';
|
||||||
|
import 'package:server_box/data/provider/server.dart';
|
||||||
import 'package:server_box/data/res/build_data.dart';
|
import 'package:server_box/data/res/build_data.dart';
|
||||||
import 'package:server_box/data/res/github_id.dart';
|
import 'package:server_box/data/res/github_id.dart';
|
||||||
import 'package:server_box/data/res/misc.dart';
|
import 'package:server_box/data/res/misc.dart';
|
||||||
import 'package:server_box/data/res/provider.dart';
|
|
||||||
import 'package:server_box/data/res/store.dart';
|
import 'package:server_box/data/res/store.dart';
|
||||||
import 'package:server_box/data/res/url.dart';
|
import 'package:server_box/data/res/url.dart';
|
||||||
import 'package:server_box/view/page/ssh/page.dart';
|
|
||||||
import 'package:wakelock_plus/wakelock_plus.dart';
|
import 'package:wakelock_plus/wakelock_plus.dart';
|
||||||
|
|
||||||
part 'appbar.dart';
|
part 'appbar.dart';
|
||||||
@@ -44,7 +44,6 @@ class _HomePageState extends State<HomePage>
|
|||||||
super.initState();
|
super.initState();
|
||||||
SystemUIs.switchStatusBar(hide: false);
|
SystemUIs.switchStatusBar(hide: false);
|
||||||
WidgetsBinding.instance.addObserver(this);
|
WidgetsBinding.instance.addObserver(this);
|
||||||
_selectIndex.value = Stores.setting.launchPage.fetch();
|
|
||||||
// avoid index out of range
|
// avoid index out of range
|
||||||
if (_selectIndex.value >= AppTab.values.length || _selectIndex.value < 0) {
|
if (_selectIndex.value >= AppTab.values.length || _selectIndex.value < 0) {
|
||||||
_selectIndex.value = 0;
|
_selectIndex.value = 0;
|
||||||
@@ -66,7 +65,7 @@ class _HomePageState extends State<HomePage>
|
|||||||
void dispose() {
|
void dispose() {
|
||||||
super.dispose();
|
super.dispose();
|
||||||
WidgetsBinding.instance.removeObserver(this);
|
WidgetsBinding.instance.removeObserver(this);
|
||||||
Pros.server.closeServer();
|
ServerProvider.closeServer();
|
||||||
_pageController.dispose();
|
_pageController.dispose();
|
||||||
WakelockPlus.disable();
|
WakelockPlus.disable();
|
||||||
}
|
}
|
||||||
@@ -78,13 +77,9 @@ class _HomePageState extends State<HomePage>
|
|||||||
|
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case AppLifecycleState.resumed:
|
case AppLifecycleState.resumed:
|
||||||
if (_shouldAuth) {
|
if (_shouldAuth) _goAuth();
|
||||||
if (Stores.setting.useBioAuth.fetch()) {
|
if (!ServerProvider.isAutoRefreshOn) {
|
||||||
BioAuth.go().then((_) => _shouldAuth = false);
|
ServerProvider.startAutoRefresh();
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!Pros.server.isAutoRefreshOn) {
|
|
||||||
Pros.server.startAutoRefresh();
|
|
||||||
}
|
}
|
||||||
HomeWidgetMC.update();
|
HomeWidgetMC.update();
|
||||||
break;
|
break;
|
||||||
@@ -98,7 +93,7 @@ class _HomePageState extends State<HomePage>
|
|||||||
// }
|
// }
|
||||||
} else {
|
} else {
|
||||||
//Pros.server.setDisconnected();
|
//Pros.server.setDisconnected();
|
||||||
Pros.server.stopAutoRefresh();
|
ServerProvider.stopAutoRefresh();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -109,7 +104,7 @@ class _HomePageState extends State<HomePage>
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
super.build(context);
|
super.build(context);
|
||||||
Pros.app.ctx = context;
|
AppProvider.ctx = context;
|
||||||
|
|
||||||
final appBar = _AppBar(
|
final appBar = _AppBar(
|
||||||
selectIndex: _selectIndex,
|
selectIndex: _selectIndex,
|
||||||
@@ -125,15 +120,18 @@ class _HomePageState extends State<HomePage>
|
|||||||
icon: const Icon(Icons.refresh),
|
icon: const Icon(Icons.refresh),
|
||||||
tooltip: 'Refresh',
|
tooltip: 'Refresh',
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await Pros.server.refresh();
|
await ServerProvider.refresh();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.developer_mode, size: 21),
|
icon: const Icon(Icons.developer_mode, size: 21),
|
||||||
tooltip: l10n.debug,
|
tooltip: 'Debug',
|
||||||
onPressed: () => AppRoutes.debug().go(context),
|
onPressed: () => DebugPage.route.go(
|
||||||
|
context,
|
||||||
|
args: const DebugPageArgs(title: 'Debug(${BuildData.build})'),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -146,7 +144,7 @@ class _HomePageState extends State<HomePage>
|
|||||||
physics: const NeverScrollableScrollPhysics(),
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
itemBuilder: (_, index) => AppTab.values[index].page,
|
itemBuilder: (_, index) => AppTab.values[index].page,
|
||||||
onPageChanged: (value) {
|
onPageChanged: (value) {
|
||||||
SSHPage.focusNode.unfocus();
|
FocusScope.of(context).unfocus();
|
||||||
if (!_switchingPage) {
|
if (!_switchingPage) {
|
||||||
_selectIndex.value = value;
|
_selectIndex.value = value;
|
||||||
}
|
}
|
||||||
@@ -238,7 +236,7 @@ class _HomePageState extends State<HomePage>
|
|||||||
children: [
|
children: [
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.settings),
|
leading: const Icon(Icons.settings),
|
||||||
title: Text(l10n.setting),
|
title: Text(libL10n.setting),
|
||||||
onTap: () => AppRoutes.settings().go(context),
|
onTap: () => AppRoutes.settings().go(context),
|
||||||
onLongPress: _onLongPressSetting,
|
onLongPress: _onLongPressSetting,
|
||||||
),
|
),
|
||||||
@@ -249,17 +247,17 @@ class _HomePageState extends State<HomePage>
|
|||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(BoxIcons.bxs_file_blank),
|
leading: const Icon(BoxIcons.bxs_file_blank),
|
||||||
title: Text(l10n.files),
|
title: Text(libL10n.file),
|
||||||
onTap: () => AppRoutes.localStorage().go(context),
|
onTap: () => AppRoutes.localStorage().go(context),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(MingCute.file_import_fill),
|
leading: const Icon(MingCute.file_import_fill),
|
||||||
title: Text(l10n.backup),
|
title: Text(libL10n.backup),
|
||||||
onTap: () => AppRoutes.backup().go(context),
|
onTap: () => AppRoutes.backup().go(context),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(OctIcons.feed_discussion),
|
leading: const Icon(OctIcons.feed_discussion),
|
||||||
title: Text('${l10n.about} & ${l10n.feedback}'),
|
title: Text('${libL10n.about} & ${libL10n.feedback}'),
|
||||||
onTap: _showAboutDialog,
|
onTap: _showAboutDialog,
|
||||||
)
|
)
|
||||||
].map((e) => CardX(child: e)).toList(),
|
].map((e) => CardX(child: e)).toList(),
|
||||||
@@ -269,7 +267,7 @@ class _HomePageState extends State<HomePage>
|
|||||||
|
|
||||||
void _showAboutDialog() {
|
void _showAboutDialog() {
|
||||||
context.showRoundDialog(
|
context.showRoundDialog(
|
||||||
title: l10n.about,
|
title: libL10n.about,
|
||||||
child: _buildAboutContent(),
|
child: _buildAboutContent(),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
@@ -278,7 +276,7 @@ class _HomePageState extends State<HomePage>
|
|||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => Urls.appHelp.launch(),
|
onPressed: () => Urls.appHelp.launch(),
|
||||||
child: Text(l10n.feedback),
|
child: Text(libL10n.feedback),
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => showLicensePage(context: context),
|
onPressed: () => showLicensePage(context: context),
|
||||||
@@ -323,7 +321,7 @@ ${GithubIds.participants.map((e) => '[$e](${e.url})').join(' ')}
|
|||||||
@override
|
@override
|
||||||
Future<void> afterFirstLayout(BuildContext context) async {
|
Future<void> afterFirstLayout(BuildContext context) async {
|
||||||
// Auth required for first launch
|
// Auth required for first launch
|
||||||
if (Stores.setting.useBioAuth.fetch()) BioAuth.go();
|
_goAuth();
|
||||||
|
|
||||||
//_reqNotiPerm();
|
//_reqNotiPerm();
|
||||||
|
|
||||||
@@ -335,8 +333,7 @@ ${GithubIds.participants.map((e) => '[$e](${e.url})').join(' ')}
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
HomeWidgetMC.update();
|
HomeWidgetMC.update();
|
||||||
await Pros.server.load();
|
await ServerProvider.refresh();
|
||||||
await Pros.server.refresh();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Future<void> _reqNotiPerm() async {
|
// Future<void> _reqNotiPerm() async {
|
||||||
@@ -361,6 +358,16 @@ ${GithubIds.participants.map((e) => '[$e](${e.url})').join(' ')}
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
void _goAuth() {
|
||||||
|
if (Stores.setting.useBioAuth.fetch()) {
|
||||||
|
if (BioAuthPage.route.isAlreadyIn) return;
|
||||||
|
BioAuthPage.route.go(
|
||||||
|
context,
|
||||||
|
args: BioAuthPageArgs(onAuthSuccess: () => _shouldAuth = false),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _onLongPressSetting() async {
|
Future<void> _onLongPressSetting() async {
|
||||||
final map = Stores.setting.box.toJson(includeInternal: false);
|
final map = Stores.setting.box.toJson(includeInternal: false);
|
||||||
final keys = map.keys;
|
final keys = map.keys;
|
||||||
@@ -370,7 +377,7 @@ ${GithubIds.participants.map((e) => '[$e](${e.url})').join(' ')}
|
|||||||
final result = await AppRoutes.editor(
|
final result = await AppRoutes.editor(
|
||||||
text: text,
|
text: text,
|
||||||
langCode: 'json',
|
langCode: 'json',
|
||||||
title: l10n.setting,
|
title: libL10n.setting,
|
||||||
).go<String>(context);
|
).go<String>(context);
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
return;
|
return;
|
||||||
@@ -385,7 +392,7 @@ ${GithubIds.participants.map((e) => '[$e](${e.url})').join(' ')}
|
|||||||
}
|
}
|
||||||
} catch (e, trace) {
|
} catch (e, trace) {
|
||||||
context.showRoundDialog(
|
context.showRoundDialog(
|
||||||
title: l10n.error,
|
title: libL10n.error,
|
||||||
child: Text('${l10n.save}:\n$e'),
|
child: Text('${l10n.save}:\n$e'),
|
||||||
);
|
);
|
||||||
Loggers.app.warning('Update json settings failed', e, trace);
|
Loggers.app.warning('Update json settings failed', e, trace);
|
||||||
|
|||||||