mirror of
https://github.com/lollipopkit/flutter_server_box.git
synced 2026-02-15 04:34:34 +01:00
new: docs website (#1033)
* new: docs website Fixes #1032 * opt. * opt. * opt. * fix
This commit is contained in:
BIN
docs/src/assets/houston.webp
Normal file
BIN
docs/src/assets/houston.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 96 KiB |
30
docs/src/assets/logo.svg
Normal file
30
docs/src/assets/logo.svg
Normal file
@@ -0,0 +1,30 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" fill="none">
|
||||
<!-- Background Circle -->
|
||||
<circle cx="32" cy="32" r="30" fill="#02569B"/>
|
||||
|
||||
<!-- Terminal Window -->
|
||||
<rect x="14" y="16" width="36" height="32" rx="3" fill="#fff" opacity="0.9"/>
|
||||
|
||||
<!-- Terminal Header -->
|
||||
<rect x="14" y="16" width="36" height="8" rx="3" fill="#02569B" opacity="0.3"/>
|
||||
<circle cx="19" cy="20" r="1.5" fill="#ff5f57"/>
|
||||
<circle cx="24" cy="20" r="1.5" fill="#ffbd2e"/>
|
||||
<circle cx="29" cy="20" r="1.5" fill="#28c940"/>
|
||||
|
||||
<!-- Terminal Prompt -->
|
||||
<text x="18" y="34" font-family="monospace" font-size="6" fill="#333">></text>
|
||||
|
||||
<!-- Terminal Cursor -->
|
||||
<rect x="23" y="30" width="6" height="6" fill="#02569B" opacity="0.5">
|
||||
<animate attributeName="opacity" values="0.5;1;0.5" dur="1s" repeatCount="indefinite"/>
|
||||
</rect>
|
||||
|
||||
<!-- Server Icon -->
|
||||
<g transform="translate(38, 24)">
|
||||
<rect x="0" y="0" width="8" height="12" rx="1" fill="#02569B"/>
|
||||
<circle cx="2" cy="3" r="0.8" fill="#28c940"/>
|
||||
<circle cx="4" cy="3" r="0.8" fill="#28c940"/>
|
||||
<circle cx="6" cy="3" r="0.8" fill="#28c940"/>
|
||||
<line x1="1" y1="8" x2="7" y2="8" stroke="#fff" stroke-width="0.5"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
7
docs/src/content.config.ts
Normal file
7
docs/src/content.config.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { defineCollection } from 'astro:content';
|
||||
import { docsLoader } from '@astrojs/starlight/loaders';
|
||||
import { docsSchema } from '@astrojs/starlight/schema';
|
||||
|
||||
export const collections = {
|
||||
docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }),
|
||||
};
|
||||
83
docs/src/content/docs/advanced/bulk-import.md
Normal file
83
docs/src/content/docs/advanced/bulk-import.md
Normal file
@@ -0,0 +1,83 @@
|
||||
---
|
||||
title: Bulk Import Servers
|
||||
description: Import multiple servers from JSON file
|
||||
---
|
||||
|
||||
Import multiple server configurations at once using a JSON file.
|
||||
|
||||
## JSON Format
|
||||
|
||||
:::danger[Security Warning]
|
||||
**Never store plaintext passwords in files!** This JSON example shows a password field for demonstration only, but you should:
|
||||
|
||||
- **Prefer SSH keys** (`keyId`) instead of `pwd` - they're more secure
|
||||
- **Use secret managers** or environment variables if you must use passwords
|
||||
- **Delete the file immediately** after import - don't leave credentials lying around
|
||||
- **Add to .gitignore** - never commit credential files to version control
|
||||
:::
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"name": "My Server",
|
||||
"ip": "example.com",
|
||||
"port": 22,
|
||||
"user": "root",
|
||||
"pwd": "password",
|
||||
"keyId": "",
|
||||
"tags": ["production"],
|
||||
"autoConnect": false
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## Fields
|
||||
|
||||
| Field | Required | Description |
|
||||
|-------|----------|-------------|
|
||||
| `name` | Yes | Display name |
|
||||
| `ip` | Yes | Domain or IP address |
|
||||
| `port` | Yes | SSH port (usually 22) |
|
||||
| `user` | Yes | SSH username |
|
||||
| `pwd` | No | Password (avoid - use SSH keys instead) |
|
||||
| `keyId` | No | SSH key name (from Private Keys - recommended) |
|
||||
| `tags` | No | Organization tags |
|
||||
| `autoConnect` | No | Auto-connect on startup |
|
||||
|
||||
## Import Steps
|
||||
|
||||
1. Create JSON file with server configurations
|
||||
2. Settings → Backup → Bulk Import Servers
|
||||
3. Select your JSON file
|
||||
4. Confirm import
|
||||
|
||||
## Example
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"name": "Production",
|
||||
"ip": "prod.example.com",
|
||||
"port": 22,
|
||||
"user": "admin",
|
||||
"keyId": "my-key",
|
||||
"tags": ["production", "web"]
|
||||
},
|
||||
{
|
||||
"name": "Development",
|
||||
"ip": "dev.example.com",
|
||||
"port": 2222,
|
||||
"user": "dev",
|
||||
"keyId": "dev-key",
|
||||
"tags": ["development"]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## Tips
|
||||
|
||||
- **Use SSH keys** instead of passwords when possible
|
||||
- **Test connection** after import
|
||||
- **Organize with tags** for easier management
|
||||
- **Delete JSON file** after import
|
||||
- **Never commit** JSON files with credentials to version control
|
||||
72
docs/src/content/docs/advanced/custom-commands.md
Normal file
72
docs/src/content/docs/advanced/custom-commands.md
Normal file
@@ -0,0 +1,72 @@
|
||||
---
|
||||
title: Custom Commands
|
||||
description: Display custom command output on server page
|
||||
---
|
||||
|
||||
Add custom shell commands to show their output on the server detail page.
|
||||
|
||||
## Setup
|
||||
|
||||
1. Server settings → Custom Commands
|
||||
2. Enter commands in JSON format
|
||||
|
||||
## Basic Format
|
||||
|
||||
```json
|
||||
{
|
||||
"Display Name": "shell command"
|
||||
}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```json
|
||||
{
|
||||
"Memory": "free -h",
|
||||
"Disk": "df -h",
|
||||
"Uptime": "uptime"
|
||||
}
|
||||
```
|
||||
|
||||
## Viewing Results
|
||||
|
||||
After setup, custom commands appear on server detail page and refresh automatically.
|
||||
|
||||
## Special Command Names
|
||||
|
||||
### server_card_top_right
|
||||
|
||||
Display on home page server card (top-right corner):
|
||||
|
||||
```json
|
||||
{
|
||||
"server_card_top_right": "your-command-here"
|
||||
}
|
||||
```
|
||||
|
||||
## Tips
|
||||
|
||||
**Use absolute paths:**
|
||||
```json
|
||||
{"My Script": "/usr/local/bin/my-script.sh"}
|
||||
```
|
||||
|
||||
**Pipe commands:**
|
||||
```json
|
||||
{"Top Process": "ps aux | sort -rk 3 | head -5"}
|
||||
```
|
||||
|
||||
**Format output:**
|
||||
```json
|
||||
{"CPU Load": "uptime | awk -F'load average:' '{print $2}'"}
|
||||
```
|
||||
|
||||
**Keep commands fast:** Under 5 seconds for best experience
|
||||
|
||||
**Limit output:**
|
||||
```json
|
||||
{"Logs": "tail -20 /var/log/syslog"}
|
||||
```
|
||||
|
||||
## Security
|
||||
|
||||
Commands run with SSH user permissions. Avoid commands that modify system state.
|
||||
54
docs/src/content/docs/advanced/custom-logo.md
Normal file
54
docs/src/content/docs/advanced/custom-logo.md
Normal file
@@ -0,0 +1,54 @@
|
||||
---
|
||||
title: Custom Server Logo
|
||||
description: Use custom images for server cards
|
||||
---
|
||||
|
||||
Display custom logos on server cards using image URLs.
|
||||
|
||||
## Setup
|
||||
|
||||
1. Server settings → Custom Logo
|
||||
2. Enter image URL
|
||||
|
||||
## URL Placeholders
|
||||
|
||||
### {DIST} - Linux Distribution
|
||||
|
||||
Auto-replaced with detected distribution:
|
||||
|
||||
```
|
||||
https://example.com/{DIST}.png
|
||||
```
|
||||
|
||||
Becomes: `debian.png`, `ubuntu.png`, `arch.png`, etc.
|
||||
|
||||
### {BRIGHT} - Theme
|
||||
|
||||
Auto-replaced with current theme:
|
||||
|
||||
```
|
||||
https://example.com/{BRIGHT}.png
|
||||
```
|
||||
|
||||
Becomes: `light.png` or `dark.png`
|
||||
|
||||
### Combine Both
|
||||
|
||||
```
|
||||
https://example.com/{DIST}-{BRIGHT}.png
|
||||
```
|
||||
|
||||
Becomes: `debian-light.png`, `ubuntu-dark.png`, etc.
|
||||
|
||||
## Tips
|
||||
|
||||
- Use PNG or SVG formats
|
||||
- Recommended size: 64x64 to 128x128 pixels
|
||||
- Use HTTPS URLs
|
||||
- Keep file sizes small
|
||||
|
||||
## Supported Distributions
|
||||
|
||||
debian, ubuntu, centos, fedora, opensuse, kali, alpine, arch, rocky, deepin, armbian, wrt
|
||||
|
||||
Full list: [`dist.dart`](https://github.com/lollipopkit/flutter_server_box/blob/main/lib/data/model/server/dist.dart)
|
||||
75
docs/src/content/docs/advanced/json-settings.md
Normal file
75
docs/src/content/docs/advanced/json-settings.md
Normal file
@@ -0,0 +1,75 @@
|
||||
---
|
||||
title: Hidden Settings (JSON)
|
||||
description: Access advanced settings via JSON editor
|
||||
---
|
||||
|
||||
Some settings are hidden from the UI but accessible via JSON editor.
|
||||
|
||||
## Access
|
||||
|
||||
Long-press **Settings** in drawer to open JSON editor.
|
||||
|
||||
## Common Hidden Settings
|
||||
|
||||
### serverTabUseOldUI
|
||||
|
||||
Use old server tab UI.
|
||||
|
||||
```json
|
||||
{"serverTabUseOldUI": true}
|
||||
```
|
||||
|
||||
**Type:** boolean | **Default:** false
|
||||
|
||||
### timeout
|
||||
|
||||
Connection timeout in seconds.
|
||||
|
||||
```json
|
||||
{"timeout": 10}
|
||||
```
|
||||
|
||||
**Type:** integer | **Default:** 5 | **Range:** 1-60
|
||||
|
||||
### recordHistory
|
||||
|
||||
Save history (SFTP paths, etc.).
|
||||
|
||||
```json
|
||||
{"recordHistory": true}
|
||||
```
|
||||
|
||||
**Type:** boolean | **Default:** true
|
||||
|
||||
### textFactor
|
||||
|
||||
Text scaling factor.
|
||||
|
||||
```json
|
||||
{"textFactor": 1.2}
|
||||
```
|
||||
|
||||
**Type:** double | **Default:** 1.0 | **Range:** 0.8-1.5
|
||||
|
||||
## Finding More Settings
|
||||
|
||||
All settings defined in [`setting.dart`](https://github.com/lollipopkit/flutter_server_box/blob/main/lib/data/store/setting.dart).
|
||||
|
||||
Look for:
|
||||
```dart
|
||||
late final settingName = StoreProperty(box, 'settingKey', defaultValue);
|
||||
```
|
||||
|
||||
## ⚠️ Important
|
||||
|
||||
**Before editing:**
|
||||
- **Create backup** - Wrong settings can cause app to not open
|
||||
- **Edit carefully** - JSON must be valid
|
||||
- **Change one at a time** - Test each setting
|
||||
|
||||
## Recovery
|
||||
|
||||
If app won't open after editing:
|
||||
1. Clear app data (last resort)
|
||||
2. Reinstall app
|
||||
3. Restore from backup
|
||||
118
docs/src/content/docs/advanced/troubleshooting.md
Normal file
118
docs/src/content/docs/advanced/troubleshooting.md
Normal file
@@ -0,0 +1,118 @@
|
||||
---
|
||||
title: Common Issues
|
||||
description: Solutions to common problems
|
||||
---
|
||||
|
||||
## Connection Issues
|
||||
|
||||
### SSH Won't Connect
|
||||
|
||||
**Symptoms:** Timeout, connection refused, auth failed
|
||||
|
||||
**Solutions:**
|
||||
|
||||
1. **Verify server type:** Only Unix-like systems supported (Linux, macOS, Android/Termux)
|
||||
2. **Test manually:** `ssh user@server -p port`
|
||||
3. **Check firewall:** Port 22 must be open
|
||||
4. **Verify credentials:** Username and password/key correct
|
||||
|
||||
### Frequent Disconnections
|
||||
|
||||
**Symptoms:** Terminal disconnects after inactivity
|
||||
|
||||
**Solutions:**
|
||||
|
||||
1. **Server keep-alive:**
|
||||
```bash
|
||||
# /etc/ssh/sshd_config
|
||||
ClientAliveInterval 60
|
||||
ClientAliveCountMax 3
|
||||
```
|
||||
|
||||
2. **Disable battery optimization:**
|
||||
- MIUI: Battery → "No limits"
|
||||
- Android: Settings → Apps → Disable optimization
|
||||
- iOS: Enable background refresh
|
||||
|
||||
## Input Issues
|
||||
|
||||
### Can't Type Certain Characters
|
||||
|
||||
**Solution:** Settings → Keyboard Type → Switch to `visiblePassword`
|
||||
|
||||
Note: CJK input may not work after this change.
|
||||
|
||||
## App Issues
|
||||
|
||||
### App Crashes on Startup
|
||||
|
||||
**Symptoms:** App won't open, black screen
|
||||
|
||||
**Causes:** Corrupted settings, especially from JSON editor
|
||||
|
||||
**Solutions:**
|
||||
|
||||
1. **Clear app data:**
|
||||
- Android: Settings → Apps → ServerBox → Clear Data
|
||||
- iOS: Delete and reinstall
|
||||
|
||||
2. **Restore backup:** Import backup created before changing settings
|
||||
|
||||
### Backup/Restore Issues
|
||||
|
||||
**Backup not working:**
|
||||
- Check storage space
|
||||
- Verify app has storage permissions
|
||||
- Try different location
|
||||
|
||||
**Restore fails:**
|
||||
- Verify backup file integrity
|
||||
- Check app version compatibility
|
||||
|
||||
## Widget Issues
|
||||
|
||||
### Widget Not Updating
|
||||
|
||||
**iOS:**
|
||||
- Wait up to 30 minutes for automatic refresh
|
||||
- Remove and re-add widget
|
||||
- Check URL ends with `/status`
|
||||
|
||||
**Android:**
|
||||
- Tap widget to force refresh
|
||||
- Verify widget ID matches configuration in app settings
|
||||
|
||||
**watchOS:**
|
||||
- Restart watch app
|
||||
- Wait a few minutes after config change
|
||||
- Verify URL format
|
||||
|
||||
### Widget Shows Error
|
||||
|
||||
- Verify ServerBox Monitor is running on server
|
||||
- Test URL in browser
|
||||
- Check authentication credentials
|
||||
|
||||
## Performance Issues
|
||||
|
||||
### App is Slow
|
||||
|
||||
**Solutions:**
|
||||
- Reduce refresh rate in settings
|
||||
- Check network speed
|
||||
- Disable unused servers
|
||||
|
||||
### High Battery Usage
|
||||
|
||||
**Solutions:**
|
||||
- Increase refresh intervals
|
||||
- Disable background refresh
|
||||
- Close unused SSH sessions
|
||||
|
||||
## Getting Help
|
||||
|
||||
If issues persist:
|
||||
|
||||
1. **Search GitHub Issues:** https://github.com/lollipopkit/flutter_server_box/issues
|
||||
2. **Create New Issue:** Include app version, platform, and steps to reproduce
|
||||
3. **Check Wiki:** This documentation and GitHub Wiki
|
||||
91
docs/src/content/docs/advanced/widgets.md
Normal file
91
docs/src/content/docs/advanced/widgets.md
Normal file
@@ -0,0 +1,91 @@
|
||||
---
|
||||
title: Home Screen Widgets
|
||||
description: Add server status widgets to your home screen
|
||||
---
|
||||
|
||||
Requires [ServerBox Monitor](https://github.com/lollipopkit/server_box_monitor) installed on your servers.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Install ServerBox Monitor on your server first. See [ServerBox Monitor Wiki](https://github.com/lollipopkit/server_box_monitor/wiki/Home) for setup instructions.
|
||||
|
||||
After installation, your server should have:
|
||||
- HTTP/HTTPS endpoint
|
||||
- `/status` API endpoint
|
||||
- Optional authentication
|
||||
|
||||
## URL Format
|
||||
|
||||
```
|
||||
https://your-server.com/status
|
||||
```
|
||||
|
||||
Must end with `/status`.
|
||||
|
||||
## iOS Widget
|
||||
|
||||
### Setup
|
||||
|
||||
1. Long press home screen → Tap **+**
|
||||
2. Search "ServerBox"
|
||||
3. Choose widget size
|
||||
4. Long press widget → **Edit Widget**
|
||||
5. Enter URL ending with `/status`
|
||||
|
||||
### Notes
|
||||
|
||||
- Must use HTTPS (except local IPs)
|
||||
- Max refresh rate: 30 minutes (iOS limit)
|
||||
- Add multiple widgets for multiple servers
|
||||
|
||||
## Android Widget
|
||||
|
||||
### Setup
|
||||
|
||||
1. Long press home screen → **Widgets**
|
||||
2. Find "ServerBox" → Add to home screen
|
||||
3. Note the widget ID number displayed
|
||||
4. Open ServerBox app → Settings
|
||||
5. Tap **Config home widget link**
|
||||
6. Add entry: `Widget ID` = `Status URL`
|
||||
|
||||
Example:
|
||||
- Key: `17`
|
||||
- Value: `https://my-server.com/status`
|
||||
|
||||
7. Tap widget on home screen to refresh
|
||||
|
||||
## watchOS Widget
|
||||
|
||||
### Setup
|
||||
|
||||
1. Open iPhone app → Settings
|
||||
2. **iOS Settings** → **Watch app**
|
||||
3. Tap **Add URL**
|
||||
4. Enter URL ending with `/status`
|
||||
5. Wait for watch app to sync
|
||||
|
||||
### Notes
|
||||
|
||||
- Try restarting watch app if not updating
|
||||
- Verify phone and watch are connected
|
||||
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Widget Not Updating
|
||||
|
||||
**iOS:** Wait up to 30 minutes, then remove and re-add
|
||||
**Android:** Tap widget to force refresh, verify ID in settings
|
||||
**watchOS:** Restart watch app, wait a few minutes
|
||||
|
||||
### Widget Shows Error
|
||||
|
||||
- Verify ServerBox Monitor is running
|
||||
- Test URL in browser
|
||||
- Check URL ends with `/status`
|
||||
|
||||
## Security
|
||||
|
||||
- **Always use HTTPS** when possible
|
||||
- **Local IPs only** on trusted networks
|
||||
124
docs/src/content/docs/configuration/appearance.md
Normal file
124
docs/src/content/docs/configuration/appearance.md
Normal file
@@ -0,0 +1,124 @@
|
||||
---
|
||||
title: Appearance
|
||||
description: Customize the look and feel
|
||||
---
|
||||
|
||||
Flutter Server Box offers extensive appearance customization.
|
||||
|
||||
## Themes
|
||||
|
||||
### Light Theme
|
||||
|
||||
Clean, bright interface for daytime use.
|
||||
|
||||
### Dark Theme
|
||||
|
||||
Easy on the eyes for low-light environments.
|
||||
|
||||
### AMOLED Dark
|
||||
|
||||
Pure black background for OLED screens, saves battery.
|
||||
|
||||
### System Theme
|
||||
|
||||
Automatically switches between light and dark based on system settings.
|
||||
|
||||
Set in: **Settings > Appearance > Theme**
|
||||
|
||||
## Terminal Appearance
|
||||
|
||||
### Customization Options
|
||||
|
||||
- **Font Size**: Adjust text size in terminal
|
||||
- **Font Family**: Choose from available fonts
|
||||
- **Text Color**: Customize text color
|
||||
- **Background Color**: Set terminal background
|
||||
- **Background Opacity**: Adjust transparency
|
||||
- **Blur Effect**: Enable background blur
|
||||
|
||||
### Terminal Themes
|
||||
|
||||
Create terminal color themes:
|
||||
|
||||
1. Go to Settings > Terminal > Themes
|
||||
2. Create new theme or edit existing
|
||||
3. Customize colors:
|
||||
- Text color
|
||||
- Background color
|
||||
- Cursor color
|
||||
- Selection color
|
||||
|
||||
## Server Cards
|
||||
|
||||
### Card Style
|
||||
|
||||
- **Compact**: Minimal information per card
|
||||
- **Detailed**: Extended information per card
|
||||
- **Custom**: Choose which metrics to show
|
||||
|
||||
### Card Order
|
||||
|
||||
1. Go to Settings > Server Card Order
|
||||
2. Drag cards to reorder
|
||||
3. Changes apply immediately
|
||||
|
||||
### Card Metrics
|
||||
|
||||
Enable/disable metrics:
|
||||
- CPU
|
||||
- Memory
|
||||
- Disk
|
||||
- Network
|
||||
- GPU
|
||||
- Temperature
|
||||
|
||||
## Charts and Graphs
|
||||
|
||||
### Chart Style
|
||||
|
||||
- **Line**: Continuous line chart
|
||||
- **Area**: Filled area chart
|
||||
- **Bar**: Bar chart visualization
|
||||
|
||||
### Chart Colors
|
||||
|
||||
Customize chart colors in:
|
||||
**Settings > Charts > Colors**
|
||||
|
||||
### Refresh Rate
|
||||
|
||||
Adjust how often charts update:
|
||||
**Settings > Charts > Refresh Rate**
|
||||
|
||||
- **Power Saving**: 5 seconds
|
||||
- **Normal**: 2 seconds
|
||||
- **High Performance**: 1 second
|
||||
|
||||
## Layout
|
||||
|
||||
### Navigation Style
|
||||
|
||||
- **Bottom Navigation**: Mobile-style bottom tabs
|
||||
- **Side Navigation**: Desktop-style sidebar
|
||||
- **Tabs**: Classic tab interface
|
||||
|
||||
### View Mode
|
||||
|
||||
- **List**: Vertical list view
|
||||
- **Grid**: Grid layout for servers
|
||||
- **Cards**: Card-based layout
|
||||
|
||||
## Icons and Symbols
|
||||
|
||||
Choose icon style:
|
||||
- **Filled**: Solid icons
|
||||
- **Outlined**: Line icons
|
||||
- **Rounded**: Soft, rounded icons
|
||||
|
||||
## Animations
|
||||
|
||||
Control animation speed:
|
||||
- **Off**: No animations
|
||||
- **Reduced**: Minimal animations
|
||||
- **Normal**: Standard animations
|
||||
- **Enhanced**: Extra animations
|
||||
80
docs/src/content/docs/configuration/backup.md
Normal file
80
docs/src/content/docs/configuration/backup.md
Normal file
@@ -0,0 +1,80 @@
|
||||
---
|
||||
title: Backup & Restore
|
||||
description: Backup and restore your app data
|
||||
---
|
||||
|
||||
Protect your server configurations and settings with built-in backup functionality.
|
||||
|
||||
## What Gets Backed Up
|
||||
|
||||
- **Server Configurations**: All saved servers
|
||||
- **SSH Keys**: Imported private keys (encrypted)
|
||||
- **Snippets**: Saved command snippets
|
||||
- **Settings**: App preferences
|
||||
|
||||
**Not included:** Passwords (for security)
|
||||
|
||||
## Creating Backups
|
||||
|
||||
### Manual Backup
|
||||
|
||||
1. Settings → Backup
|
||||
2. Tap **Create Backup**
|
||||
3. Choose location
|
||||
4. Backup saved with timestamp
|
||||
|
||||
### Auto Backup
|
||||
|
||||
Settings → Backup → Auto Backup:
|
||||
- Daily / Weekly / Monthly / Off
|
||||
|
||||
### Cloud Sync
|
||||
|
||||
- **iOS/macOS**: iCloud automatic backup
|
||||
- **Android**: Google Drive integration
|
||||
|
||||
## Restoring
|
||||
|
||||
### From Local Backup
|
||||
|
||||
1. Settings → Backup → Restore Backup
|
||||
2. Select backup file
|
||||
3. Authenticate (biometric/password)
|
||||
4. Confirm restore
|
||||
|
||||
### From Cloud
|
||||
|
||||
1. Sign in to same cloud account
|
||||
2. Settings → Backup → Restore from Cloud
|
||||
3. Select backup from list
|
||||
4. Authenticate and confirm
|
||||
|
||||
## Important Notes
|
||||
|
||||
### Passwords Not Backed Up
|
||||
|
||||
After restore, you'll need to re-enter passwords for each server.
|
||||
|
||||
**Tip:** Use SSH keys instead - they ARE backed up.
|
||||
|
||||
### Cross-Platform
|
||||
|
||||
Backups work across all platforms (iOS ↔ Android ↔ Desktop).
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Enable auto backup** for peace of mind
|
||||
2. **Test restore** periodically to verify backups work
|
||||
3. **Backup before** updating app or switching devices
|
||||
4. **Use SSH keys** to avoid re-entering passwords
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Restore failed:**
|
||||
- Check backup file integrity
|
||||
- Ensure sufficient storage
|
||||
- Verify app version compatibility
|
||||
|
||||
**Missing data after restore:**
|
||||
- Passwords are not backed up (re-enter them)
|
||||
- Check selective restore settings
|
||||
88
docs/src/content/docs/configuration/jump-server.md
Normal file
88
docs/src/content/docs/configuration/jump-server.md
Normal file
@@ -0,0 +1,88 @@
|
||||
---
|
||||
title: Jump Server
|
||||
description: Route connections through intermediate servers
|
||||
---
|
||||
|
||||
Connect to servers behind firewalls or in private networks by routing through an intermediate jump server.
|
||||
|
||||
## What is Jump Server?
|
||||
|
||||
A jump server acts as a gateway to access other servers that:
|
||||
- Are behind firewalls
|
||||
- Don't have direct SSH access
|
||||
- Are in private networks
|
||||
- Require multi-hop connections
|
||||
|
||||
## Setup
|
||||
|
||||
### Step 1: Configure Jump Server
|
||||
|
||||
Add the jump server as a normal server first:
|
||||
1. Add server with SSH credentials
|
||||
2. Test connection to ensure it works
|
||||
3. This server will be your jump host
|
||||
|
||||
### Step 2: Configure Target Servers
|
||||
|
||||
For each server you want to access via jump:
|
||||
1. Add target server (credentials for target, not jump)
|
||||
2. Server settings → Jump Server
|
||||
3. Select your jump server from list
|
||||
4. Save
|
||||
|
||||
### Step 3: Connect
|
||||
|
||||
Connect to target server normally. The app automatically:
|
||||
1. Connects to jump server
|
||||
2. Tunnels through to target server
|
||||
3. Maintains connection
|
||||
|
||||
## Use Cases
|
||||
|
||||
### Private Network Access
|
||||
|
||||
```
|
||||
Your Device → Jump Server (public IP) → Private Server (10.0.0.x)
|
||||
```
|
||||
|
||||
### Behind Firewall
|
||||
|
||||
```
|
||||
Your Device → Bastion Host → Internal Server
|
||||
```
|
||||
|
||||
### Multi-Hop
|
||||
|
||||
You can chain multiple jump servers for complex networks.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Jump server must be accessible from your device
|
||||
- Jump server must be able to reach target servers
|
||||
- SSH keys recommended for jump server (faster authentication)
|
||||
|
||||
## Tips
|
||||
|
||||
- **Use SSH keys** on jump server for faster connections
|
||||
- **Test direct access** to jump server first
|
||||
- **Check firewall rules** on both ends
|
||||
- **Monitor connection** - issues could be on jump or target
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Connection Times Out
|
||||
|
||||
- Verify jump server is accessible
|
||||
- Check jump server can reach target
|
||||
- Test manually: `ssh -J jump@jump-server user@target-server`
|
||||
|
||||
### Authentication Fails
|
||||
|
||||
- Verify credentials for target server (not jump)
|
||||
- Check SSH keys if using key authentication
|
||||
|
||||
### Slow Connection
|
||||
|
||||
- Normal for jump connections (extra hop)
|
||||
- Consider using SSH keys for faster auth
|
||||
- Check network latency to jump server
|
||||
93
docs/src/content/docs/configuration/localizations.md
Normal file
93
docs/src/content/docs/configuration/localizations.md
Normal file
@@ -0,0 +1,93 @@
|
||||
---
|
||||
title: Localizations
|
||||
description: Language and region settings
|
||||
---
|
||||
|
||||
Flutter Server Box supports 12+ languages with full localization.
|
||||
|
||||
## Supported Languages
|
||||
|
||||
| Language | Status |
|
||||
|----------|--------|
|
||||
| English (en) | ✅ Native |
|
||||
| 简体中文 (zh) | ✅ Native |
|
||||
| 繁體中文 (zh-Hant) | ✅ Native |
|
||||
| Deutsch (de) | ✅ Native |
|
||||
| Français (fr) | ✅ Native |
|
||||
| Español (es) | ✅ Native |
|
||||
| Português (pt) | ✅ Native |
|
||||
| Русский (ru) | ✅ Native |
|
||||
| Türkçe (tr) | ✅ Native |
|
||||
| Українська (uk) | ✅ Native |
|
||||
| Bahasa Indonesia (id) | ✅ Native |
|
||||
| Nederlands (nl) | ✅ Native |
|
||||
| 日本語 (ja) | ✅ AI-translated |
|
||||
|
||||
## Changing Language
|
||||
|
||||
1. Go to **Settings > Language**
|
||||
2. Select preferred language
|
||||
3. App restarts to apply changes
|
||||
|
||||
## Number Formatting
|
||||
|
||||
Numbers are formatted according to locale:
|
||||
|
||||
- **Thousands separator**: Comma vs period
|
||||
- **Decimal separator**: Period vs comma
|
||||
- **Date format**: Locale-specific
|
||||
|
||||
## Time Format
|
||||
|
||||
Choose between:
|
||||
|
||||
- **24-hour**: 13:00, 14:30
|
||||
- **12-hour**: 1:00 PM, 2:30 PM
|
||||
|
||||
Set in: **Settings > Time Format**
|
||||
|
||||
## Contributing Translations
|
||||
|
||||
We welcome community translations!
|
||||
|
||||
### Translation Files
|
||||
|
||||
Located in `lib/l10n/`:
|
||||
|
||||
- `app_en.arb` - English (reference)
|
||||
- `app_zh.arb` - Simplified Chinese
|
||||
- etc.
|
||||
|
||||
### How to Contribute
|
||||
|
||||
1. Fork the repository
|
||||
2. Copy `app_en.arb` to `app_YOUR_LOCALE.arb`
|
||||
3. Translate values (keep keys the same)
|
||||
4. Test your translations
|
||||
5. Submit pull request
|
||||
|
||||
### Translation Guidelines
|
||||
|
||||
- Keep technical terms consistent
|
||||
- Use formal address for professional tone
|
||||
- Maintain placeholder format: `{variable}`
|
||||
- Test UI with translated strings
|
||||
|
||||
## Adding New Language
|
||||
|
||||
1. Create new ARB file: `app_xx.arb`
|
||||
2. Copy all keys from `app_en.arb`
|
||||
3. Translate all values
|
||||
4. Add to `l10n.yaml` configuration
|
||||
5. Run `flutter gen-l10n`
|
||||
6. Test with new locale
|
||||
|
||||
## RTL Languages
|
||||
|
||||
Right-to-left languages (Arabic, Hebrew) are partially supported. Full RTL layout support is planned for future releases.
|
||||
|
||||
## Quality Notes
|
||||
|
||||
- Some languages are AI-translated and may contain errors
|
||||
- Native speaker reviews are appreciated
|
||||
- Report translation issues via GitHub
|
||||
90
docs/src/content/docs/configuration/server.md
Normal file
90
docs/src/content/docs/configuration/server.md
Normal file
@@ -0,0 +1,90 @@
|
||||
---
|
||||
title: Server Setup
|
||||
description: Configure and manage server connections
|
||||
---
|
||||
|
||||
## Adding a Server
|
||||
|
||||
1. Tap the **+** button on the main screen
|
||||
2. Fill in connection details:
|
||||
- **Name**: Friendly name for identification
|
||||
- **Host**: IP address or domain name
|
||||
- **Port**: SSH port (default: 22)
|
||||
- **Username**: SSH login user
|
||||
- **Authentication**: Password or SSH key
|
||||
|
||||
3. Configure optional settings:
|
||||
- **Initial Directory**: Starting directory for terminal/SFTP
|
||||
- **Environment**: Custom environment variables
|
||||
- **Keep-alive Interval**: Connection keep-alive setting
|
||||
|
||||
4. Tap **Save**
|
||||
|
||||
## Connection Types
|
||||
|
||||
### Password Authentication
|
||||
|
||||
Simple username/password authentication:
|
||||
|
||||
- Enter password in the password field
|
||||
- Password is stored securely (encrypted)
|
||||
- Requires re-entry on app restart (unless saved)
|
||||
|
||||
### SSH Key Authentication
|
||||
|
||||
More secure, passwordless authentication:
|
||||
|
||||
1. Generate or import SSH key
|
||||
2. Add key to server's `~/.ssh/authorized_keys`
|
||||
3. Select key in app when adding server
|
||||
|
||||
See [SSH Keys](/configuration/ssh-keys/) for detailed setup.
|
||||
|
||||
## Server Groups
|
||||
|
||||
Organize servers into groups for easier management:
|
||||
|
||||
1. Go to Settings > Server Groups
|
||||
2. Create a new group
|
||||
3. Assign servers to groups
|
||||
4. Groups appear as sections in main view
|
||||
|
||||
## Server Cards
|
||||
|
||||
Customize what information appears on server cards:
|
||||
|
||||
1. Go to Settings > Server Card Settings
|
||||
2. Enable/disable metrics:
|
||||
- CPU
|
||||
- Memory
|
||||
- Disk
|
||||
- Network
|
||||
3. Reorder cards by dragging
|
||||
|
||||
## Connection Profiles
|
||||
|
||||
Save connection profiles for different use cases:
|
||||
|
||||
- **Default Profile**: Standard settings
|
||||
- **Low Bandwidth**: Reduced refresh rate
|
||||
- **High Performance**: Maximum refresh rate
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Connection Refused
|
||||
|
||||
- Check server is running
|
||||
- Verify SSH port
|
||||
- Check firewall rules
|
||||
|
||||
### Authentication Failed
|
||||
|
||||
- Verify username/password
|
||||
- Check SSH key permissions
|
||||
- Ensure SSH service is running
|
||||
|
||||
### Timeout
|
||||
|
||||
- Check network connectivity
|
||||
- Increase timeout in settings
|
||||
- Try different network
|
||||
118
docs/src/content/docs/configuration/sftp.md
Normal file
118
docs/src/content/docs/configuration/sftp.md
Normal file
@@ -0,0 +1,118 @@
|
||||
---
|
||||
title: SFTP File Browser
|
||||
description: File transfer and management via SFTP
|
||||
---
|
||||
|
||||
Browse, edit, and transfer files on your servers with a built-in SFTP client.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
### Opening SFTP
|
||||
|
||||
1. Connect to server
|
||||
2. Tap **Files** button on server page
|
||||
3. Or from terminal: Tap **SFTP** button
|
||||
|
||||
### Navigation
|
||||
|
||||
- **Tap folder**: Enter directory
|
||||
- **Tap file**: View/Edit/Download options
|
||||
- **Back button**: Previous directory
|
||||
- **Home button**: User's home directory
|
||||
- **Goto button**: Jump to path with autocomplete
|
||||
|
||||
## File Operations
|
||||
|
||||
### Common Actions
|
||||
|
||||
| Action | How |
|
||||
|--------|-----|
|
||||
| **Download** | Long-press file → Download |
|
||||
| **Upload** | Folder icon → Select file |
|
||||
| **Rename** | Long-press → Rename |
|
||||
| **Delete** | Long-press → Delete |
|
||||
| **Copy/Move** | Long-press → Select → Choose destination |
|
||||
| **Permissions** | Tap file info → Edit permissions |
|
||||
|
||||
### Permission Editor
|
||||
|
||||
Unix permissions editor:
|
||||
|
||||
- **3x3 Grid**: User/Group/Other × Read/Write/Execute
|
||||
- **Numeric**: Direct input (755, 644, etc.)
|
||||
- **Symbolic**: rwxr-xr-x format
|
||||
|
||||
### Edit Files
|
||||
|
||||
1. Tap file → Edit
|
||||
2. Edit in built-in editor
|
||||
3. Save → Upload back to server
|
||||
|
||||
**Size limit:** Files up to 1 MB. For larger files, use the terminal with vim/nano instead.
|
||||
|
||||
**Editor settings:** Settings → SFTP Editor
|
||||
- Preferred editor (vim, nano, etc.)
|
||||
- Close after save
|
||||
- Soft wrap
|
||||
- Syntax highlighting
|
||||
|
||||
## Display Settings
|
||||
|
||||
### Sort Order
|
||||
|
||||
Settings → Sort By:
|
||||
- Name (alphabetical)
|
||||
- Size (largest first)
|
||||
- Time (newest first)
|
||||
|
||||
### Folders First
|
||||
|
||||
Show directories before files:
|
||||
Settings → Folders First
|
||||
|
||||
### Hidden Files
|
||||
|
||||
Show dotfiles (`.git`, `.bashrc`, etc.):
|
||||
Settings → Show Hidden Files
|
||||
|
||||
## Archive Support
|
||||
|
||||
Extract common archive formats directly on your server.
|
||||
|
||||
| Format | Variants | Command Required |
|
||||
|--------|----------|------------------|
|
||||
| .tar.gz | .tgz, .tar.Z | tar |
|
||||
| .tar.bz2 | .tbz2, .tar.bz2 | tar |
|
||||
| .tar.xz | .txz | tar |
|
||||
| .zip | .zipx | unzip |
|
||||
| .7z | - | 7z |
|
||||
| .rar | - | unrar |
|
||||
|
||||
**Note:** The corresponding command (`tar`, `unzip`, `7z`, `unrar`) must be installed on your server. These tools handle many sub-formats not listed above.
|
||||
|
||||
## Quick Access
|
||||
|
||||
### From Terminal
|
||||
|
||||
Tap **SFTP** button to open current terminal directory in file browser.
|
||||
|
||||
### Remember Last Path
|
||||
|
||||
Automatically return to last visited directory:
|
||||
Settings → SFTP Open Last Path
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Permission Denied
|
||||
|
||||
- Check user has read access to directory
|
||||
- Verify directory permissions: `ls -la`
|
||||
- Ensure SFTP is enabled in sshd_config
|
||||
|
||||
### Slow Listing
|
||||
|
||||
Large directories (1000+ items) use pagination for performance.
|
||||
|
||||
### Can't Edit File
|
||||
|
||||
File larger than 1 MB? Use terminal with vim/nano instead.
|
||||
127
docs/src/content/docs/configuration/terminal.md
Normal file
127
docs/src/content/docs/configuration/terminal.md
Normal file
@@ -0,0 +1,127 @@
|
||||
---
|
||||
title: Terminal & SSH
|
||||
description: SSH terminal setup and configuration
|
||||
---
|
||||
|
||||
Complete SSH terminal access with full keyboard support and customizable interface.
|
||||
|
||||
## Basic Setup
|
||||
|
||||
### First Connection
|
||||
|
||||
1. Add server with SSH credentials
|
||||
2. Tap server card to connect
|
||||
3. Accept host key fingerprint (first time only)
|
||||
4. Terminal opens automatically
|
||||
|
||||
### Virtual Keyboard (Mobile)
|
||||
|
||||
Customizable virtual keyboard for terminal access:
|
||||
|
||||
| Button | Function |
|
||||
|--------|----------|
|
||||
| **Ctrl, Alt, Shift** | Modifier keys (tap before other key) |
|
||||
| **Esc, Tab** | Special characters |
|
||||
| **Arrows** | Navigation |
|
||||
| **F1-F12** | Function keys |
|
||||
| **SFTP** | Open current directory in file browser |
|
||||
| **Clipboard** | Copy selection / Paste clipboard |
|
||||
| **Snippets** | Quick command execution |
|
||||
|
||||
**Customize keyboard:** Settings → SSH Virtual Keys
|
||||
- Enable/disable keys
|
||||
- Reorder layout
|
||||
- Add/remove buttons
|
||||
|
||||
## Terminal Settings
|
||||
|
||||
### Appearance
|
||||
|
||||
**Font Size:** Settings → Terminal Font Size
|
||||
- Affects all new sessions
|
||||
- Typical range: 8-24 pixels
|
||||
|
||||
**Colors:** Settings → Terminal Color
|
||||
- Text color
|
||||
- Background color & opacity
|
||||
- Blur effect (iOS/macOS)
|
||||
- Cursor color
|
||||
|
||||
### Keyboard Type
|
||||
|
||||
If you can't input certain characters:
|
||||
|
||||
1. Settings → Keyboard Type
|
||||
2. Switch to `visiblePassword`
|
||||
3. Note: CJK input may not work after this change
|
||||
|
||||
## Connection Management
|
||||
|
||||
### Multi-Tab
|
||||
|
||||
- **Desktop**: Ctrl+T (new), Ctrl+W (close)
|
||||
- **Mobile**: Tap + button
|
||||
- Sessions persist between app launches
|
||||
|
||||
### Auto-Connect
|
||||
|
||||
Set server to auto-connect on app open:
|
||||
1. Server settings → Auto-Connect
|
||||
2. Enable toggle
|
||||
|
||||
### Jump Server
|
||||
|
||||
Route through intermediate server:
|
||||
|
||||
1. Add and configure jump server first
|
||||
2. Target server settings → Select jump server
|
||||
3. Connection routes through jump server automatically
|
||||
|
||||
## SSH Keys (Recommended)
|
||||
|
||||
More secure than passwords:
|
||||
|
||||
1. Generate key: Settings → Private Keys → Add
|
||||
2. Upload public key to server: `ssh-copy-id -i pubkey user@server`
|
||||
3. Server settings → Use key instead of password
|
||||
|
||||
## Common Issues
|
||||
|
||||
### Can't Connect
|
||||
|
||||
**Timeout/Refused:**
|
||||
- Verify server is Unix-like (Linux, macOS, Android/Termux)
|
||||
- Check firewall allows SSH port (default 22)
|
||||
- Test manually: `ssh user@server -p port`
|
||||
|
||||
**Auth Failed:**
|
||||
- Verify username and password
|
||||
- Check SSH key is uploaded correctly
|
||||
- Ensure account is not locked
|
||||
|
||||
### Terminal Disconnects
|
||||
|
||||
**Frequent disconnections:**
|
||||
|
||||
1. Check server keep-alive settings:
|
||||
```bash
|
||||
# /etc/ssh/sshd_config
|
||||
ClientAliveInterval 60
|
||||
ClientAliveCountMax 3
|
||||
```
|
||||
|
||||
2. Disable battery optimization:
|
||||
- **MIUI**: Battery → "No limits"
|
||||
- **Android**: Settings → Apps → Disable optimization
|
||||
- **iOS**: Enable background refresh
|
||||
|
||||
### Can't Input Characters
|
||||
|
||||
Change keyboard type to `visiblePassword` in settings.
|
||||
|
||||
## Tips
|
||||
|
||||
- **Test connection** first with regular SSH client
|
||||
- **Use SSH keys** instead of passwords for security
|
||||
- **Save snippets** for frequently used commands
|
||||
- **Pinch to zoom** for temporary font size change (mobile)
|
||||
86
docs/src/content/docs/development/architecture.md
Normal file
86
docs/src/content/docs/development/architecture.md
Normal file
@@ -0,0 +1,86 @@
|
||||
---
|
||||
title: Architecture
|
||||
description: Architecture patterns and design decisions
|
||||
---
|
||||
|
||||
Flutter Server Box follows clean architecture principles with clear separation between data, domain, and presentation layers.
|
||||
|
||||
## Layered Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ Presentation Layer │
|
||||
│ (lib/view/page/) │
|
||||
│ - Pages, Widgets, Controllers │
|
||||
└─────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────┐
|
||||
│ Business Logic Layer │
|
||||
│ (lib/data/provider/) │
|
||||
│ - Riverpod Providers │
|
||||
│ - State Management │
|
||||
└─────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────┐
|
||||
│ Data Layer │
|
||||
│ (lib/data/model/, store/) │
|
||||
│ - Models, Storage, Services │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Key Patterns
|
||||
|
||||
### State Management: Riverpod
|
||||
|
||||
- **Code Generation**: Uses `riverpod_generator` for type-safe providers
|
||||
- **State Notifiers**: For mutable state with business logic
|
||||
- **Async Notifiers**: For loading and error states
|
||||
- **Stream Providers**: For real-time data
|
||||
|
||||
### Immutable Models: Freezed
|
||||
|
||||
- All data models use Freezed for immutability
|
||||
- Union types for state representation
|
||||
- Built-in JSON serialization
|
||||
- CopyWith extensions for updates
|
||||
|
||||
### Local Storage: Hive
|
||||
|
||||
- **hive_ce**: Community edition of Hive
|
||||
- No manual `@HiveField` or `@HiveType` needed
|
||||
- Type adapters auto-generated
|
||||
- Persistent key-value storage
|
||||
|
||||
## Dependency Injection
|
||||
|
||||
Services and stores are injected via:
|
||||
|
||||
1. **Providers**: Expose dependencies to UI
|
||||
2. **GetIt**: Service location (where applicable)
|
||||
3. **Constructor Injection**: Explicit dependencies
|
||||
|
||||
## Data Flow
|
||||
|
||||
```
|
||||
User Action → Widget → Provider → Service/Store → Model Update → UI Rebuild
|
||||
```
|
||||
|
||||
1. User interacts with widget
|
||||
2. Widget calls provider method
|
||||
3. Provider updates state via service/store
|
||||
3. State change triggers UI rebuild
|
||||
4. New state reflected in widget
|
||||
|
||||
## Custom Dependencies
|
||||
|
||||
The project uses several custom forks to extend functionality:
|
||||
|
||||
- **dartssh2**: Enhanced SSH features
|
||||
- **xterm**: Terminal emulator with mobile support
|
||||
- **fl_lib**: Shared UI components and utilities
|
||||
|
||||
## Threading
|
||||
|
||||
- **Isolates**: Heavy computation off main thread
|
||||
- **computer package**: Multi-threading utilities
|
||||
- **Async/Await**: Non-blocking I/O operations
|
||||
116
docs/src/content/docs/development/building.md
Normal file
116
docs/src/content/docs/development/building.md
Normal file
@@ -0,0 +1,116 @@
|
||||
---
|
||||
title: Building
|
||||
description: Build instructions for different platforms
|
||||
---
|
||||
|
||||
Flutter Server Box uses a custom build system (`fl_build`) for cross-platform builds.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Flutter SDK (stable channel)
|
||||
- Platform-specific tools (Xcode for iOS, Android Studio for Android)
|
||||
- Rust toolchain (for some native dependencies)
|
||||
|
||||
## Development Build
|
||||
|
||||
```bash
|
||||
# Run in development mode
|
||||
flutter run
|
||||
|
||||
# Run on specific device
|
||||
flutter run -d <device-id>
|
||||
```
|
||||
|
||||
## Production Build
|
||||
|
||||
The project uses `fl_build` for building:
|
||||
|
||||
```bash
|
||||
# Build for specific platform
|
||||
dart run fl_build -p <platform>
|
||||
|
||||
# Available platforms:
|
||||
# - ios
|
||||
# - android
|
||||
# - macos
|
||||
# - linux
|
||||
# - windows
|
||||
```
|
||||
|
||||
## Platform-Specific Builds
|
||||
|
||||
### iOS
|
||||
|
||||
```bash
|
||||
dart run fl_build -p ios
|
||||
```
|
||||
|
||||
Requires:
|
||||
- macOS with Xcode
|
||||
- CocoaPods
|
||||
- Apple Developer account for signing
|
||||
|
||||
### Android
|
||||
|
||||
```bash
|
||||
dart run fl_build -p android
|
||||
```
|
||||
|
||||
Requires:
|
||||
- Android SDK
|
||||
- Java Development Kit
|
||||
- Keystore for signing
|
||||
|
||||
### macOS
|
||||
|
||||
```bash
|
||||
dart run fl_build -p macos
|
||||
```
|
||||
|
||||
### Linux
|
||||
|
||||
```bash
|
||||
dart run fl_build -p linux
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
||||
```bash
|
||||
dart run fl_build -p windows
|
||||
```
|
||||
|
||||
Requires Windows with Visual Studio.
|
||||
|
||||
## Pre/Post Build
|
||||
|
||||
The `make.dart` script handles:
|
||||
|
||||
- Metadata generation
|
||||
- Version string updates
|
||||
- Platform-specific configurations
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Clean Build
|
||||
|
||||
```bash
|
||||
flutter clean
|
||||
dart run build_runner build --delete-conflicting-outputs
|
||||
flutter pub get
|
||||
```
|
||||
|
||||
### Version Mismatch
|
||||
|
||||
Ensure all dependencies are compatible:
|
||||
```bash
|
||||
flutter pub upgrade
|
||||
```
|
||||
|
||||
## Release Checklist
|
||||
|
||||
1. Update version in `pubspec.yaml`
|
||||
2. Run code generation
|
||||
3. Run tests
|
||||
4. Build for all target platforms
|
||||
5. Test on physical devices
|
||||
6. Create GitHub release
|
||||
98
docs/src/content/docs/development/codegen.md
Normal file
98
docs/src/content/docs/development/codegen.md
Normal file
@@ -0,0 +1,98 @@
|
||||
---
|
||||
title: Code Generation
|
||||
description: Using build_runner for code generation
|
||||
---
|
||||
|
||||
Flutter Server Box heavily uses code generation for models, state management, and serialization.
|
||||
|
||||
## When to Run Code Generation
|
||||
|
||||
Run after modifying:
|
||||
|
||||
- Models with `@freezed` annotation
|
||||
- Classes with `@JsonSerializable`
|
||||
- Hive models
|
||||
- Providers with `@riverpod`
|
||||
- Localizations (ARB files)
|
||||
|
||||
## Running Code Generation
|
||||
|
||||
```bash
|
||||
# Generate all code
|
||||
dart run build_runner build --delete-conflicting-outputs
|
||||
|
||||
# Clean and regenerate
|
||||
dart run build_runner build --delete-conflicting-outputs --clean
|
||||
```
|
||||
|
||||
## Generated Files
|
||||
|
||||
### Freezed (`*.freezed.dart`)
|
||||
|
||||
Immutable data models with union types:
|
||||
|
||||
```dart
|
||||
@freezed
|
||||
class ServerState with _$ServerState {
|
||||
const factory ServerState.connected() = Connected;
|
||||
const factory ServerState.disconnected() = Disconnected;
|
||||
const factory ServerState.error(String message) = Error;
|
||||
}
|
||||
```
|
||||
|
||||
### JSON Serialization (`*.g.dart`)
|
||||
|
||||
Generated from `json_serializable`:
|
||||
|
||||
```dart
|
||||
@JsonSerializable()
|
||||
class Server {
|
||||
final String id;
|
||||
final String name;
|
||||
final String host;
|
||||
|
||||
Server({required this.id, required this.name, required this.host});
|
||||
|
||||
factory Server.fromJson(Map<String, dynamic> json) =>
|
||||
_$ServerFromJson(json);
|
||||
Map<String, dynamic> toJson() => _$ServerToJson(this);
|
||||
}
|
||||
```
|
||||
|
||||
### Riverpod Providers (`*.g.dart`)
|
||||
|
||||
Generated from `@riverpod` annotation:
|
||||
|
||||
```dart
|
||||
@riverpod
|
||||
class MyNotifier extends _$MyNotifier {
|
||||
@override
|
||||
int build() => 0;
|
||||
}
|
||||
```
|
||||
|
||||
### Hive Adapters (`*.g.dart`)
|
||||
|
||||
Auto-generated for Hive models (hive_ce):
|
||||
|
||||
```dart
|
||||
@HiveType(typeId: 0)
|
||||
class ServerModel {
|
||||
@HiveField(0)
|
||||
final String id;
|
||||
}
|
||||
```
|
||||
|
||||
## Localization Generation
|
||||
|
||||
```bash
|
||||
flutter gen-l10n
|
||||
```
|
||||
|
||||
Generates `lib/generated/l10n/` from `lib/l10n/*.arb` files.
|
||||
|
||||
## Tips
|
||||
|
||||
- Use `--delete-conflicting-outputs` to avoid conflicts
|
||||
- Add generated files to `.gitignore`
|
||||
- Never manually edit generated files
|
||||
115
docs/src/content/docs/development/state.md
Normal file
115
docs/src/content/docs/development/state.md
Normal file
@@ -0,0 +1,115 @@
|
||||
---
|
||||
title: State Management
|
||||
description: Riverpod-based state management patterns
|
||||
---
|
||||
|
||||
Flutter Server Box uses Riverpod with code generation for state management.
|
||||
|
||||
## Provider Types
|
||||
|
||||
### StateProvider
|
||||
|
||||
Simple state that can be read and written:
|
||||
|
||||
```dart
|
||||
@riverpod
|
||||
class Settings extends _$Settings {
|
||||
@override
|
||||
SettingsModel build() {
|
||||
return SettingsModel.defaults();
|
||||
}
|
||||
|
||||
void update(SettingsModel newSettings) {
|
||||
state = newSettings;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### AsyncNotifierProvider
|
||||
|
||||
State that loads asynchronously with loading/error states:
|
||||
|
||||
```dart
|
||||
@riverpod
|
||||
class ServerStatus extends _$ServerStatus {
|
||||
@override
|
||||
Future<StatusModel> build(Server server) async {
|
||||
return fetchStatus(server);
|
||||
}
|
||||
|
||||
Future<void> refresh() async {
|
||||
state = const AsyncValue.loading();
|
||||
state = await AsyncValue.guard(() => fetchStatus(server));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### StreamProvider
|
||||
|
||||
Real-time data from streams:
|
||||
|
||||
```dart
|
||||
@riverpod
|
||||
Stream<CpuUsage> cpuUsage(CpuUsageRef ref, Server server) {
|
||||
return cpuService.monitor(server);
|
||||
}
|
||||
```
|
||||
|
||||
## State Patterns
|
||||
|
||||
### Loading States
|
||||
|
||||
```dart
|
||||
state.when(
|
||||
data: (data) => DataWidget(data),
|
||||
loading: () => LoadingWidget(),
|
||||
error: (error, stack) => ErrorWidget(error),
|
||||
)
|
||||
```
|
||||
|
||||
### Family Providers
|
||||
|
||||
Parameterized providers:
|
||||
|
||||
```dart
|
||||
@riverpod
|
||||
List<Container> containers(ContainersRef ref, Server server) {
|
||||
return containerService.list(server);
|
||||
}
|
||||
```
|
||||
|
||||
### Auto-Dispose
|
||||
|
||||
Providers that dispose when no longer referenced:
|
||||
|
||||
```dart
|
||||
@Riverpod(keepAlive: false)
|
||||
class TempState extends _$TempState {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use code generation**: Always use `@riverpod` annotation
|
||||
2. **Co-locate providers**: Place near consuming widgets
|
||||
3. **Avoid singletons**: Use providers instead
|
||||
4. **Layer correctly**: Keep UI logic separate from business logic
|
||||
|
||||
## Reading State in Widgets
|
||||
|
||||
```dart
|
||||
class ServerWidget extends ConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final status = ref.watch(serverStatusProvider(server));
|
||||
return status.when(...);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Modifying State
|
||||
|
||||
```dart
|
||||
ref.read(settingsProvider.notifier).update(newSettings);
|
||||
```
|
||||
96
docs/src/content/docs/development/structure.md
Normal file
96
docs/src/content/docs/development/structure.md
Normal file
@@ -0,0 +1,96 @@
|
||||
---
|
||||
title: Project Structure
|
||||
description: Understanding the Flutter Server Box codebase
|
||||
---
|
||||
|
||||
The Flutter Server Box project follows a modular architecture with clear separation of concerns.
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
lib/
|
||||
├── core/ # Core utilities and extensions
|
||||
├── data/ # Data layer
|
||||
│ ├── model/ # Data models by feature
|
||||
│ ├── provider/ # Riverpod providers
|
||||
│ └── store/ # Local storage (Hive)
|
||||
├── view/ # UI layer
|
||||
│ ├── page/ # Main pages
|
||||
│ └── widget/ # Reusable widgets
|
||||
├── generated/ # Generated localization
|
||||
├── l10n/ # Localization ARB files
|
||||
└── hive/ # Hive adapters
|
||||
```
|
||||
|
||||
## Core Layer (`lib/core/`)
|
||||
|
||||
Contains utilities, extensions, and routing configuration:
|
||||
|
||||
- **Extensions**: Dart extensions for common types
|
||||
- **Routes**: App routing configuration
|
||||
- **Utils**: Shared utility functions
|
||||
|
||||
## Data Layer (`lib/data/`)
|
||||
|
||||
### Models (`lib/data/model/`)
|
||||
|
||||
Organized by feature:
|
||||
|
||||
- `server/` - Server connection and status models
|
||||
- `container/` - Docker container models
|
||||
- `ssh/` - SSH session models
|
||||
- `sftp/` - SFTP file models
|
||||
- `app/` - App-specific models
|
||||
|
||||
### Providers (`lib/data/provider/`)
|
||||
|
||||
Riverpod providers for dependency injection and state management:
|
||||
|
||||
- Server providers
|
||||
- UI state providers
|
||||
- Service providers
|
||||
|
||||
### Stores (`lib/data/store/`)
|
||||
|
||||
Hive-based local storage:
|
||||
|
||||
- Server storage
|
||||
- Settings storage
|
||||
- Cache storage
|
||||
|
||||
## View Layer (`lib/view/`)
|
||||
|
||||
### Pages (`lib/view/page/`)
|
||||
|
||||
Main application screens:
|
||||
|
||||
- `server/` - Server management pages
|
||||
- `ssh/` - SSH terminal pages
|
||||
- `container/` - Container pages
|
||||
- `setting/` - Settings pages
|
||||
- `storage/` - SFTP pages
|
||||
- `snippet/` - Snippet pages
|
||||
|
||||
### Widgets (`lib/view/widget/`)
|
||||
|
||||
Reusable UI components:
|
||||
|
||||
- Server cards
|
||||
- Status charts
|
||||
- Input components
|
||||
- Dialogs
|
||||
|
||||
## Generated Files
|
||||
|
||||
- `lib/generated/l10n/` - Auto-generated localization
|
||||
- `*.g.dart` - Generated code (json_serializable, freezed, hive, riverpod)
|
||||
- `*.freezed.dart` - Freezed immutable classes
|
||||
|
||||
## Packages Directory (`/packages/`)
|
||||
|
||||
Contains custom forks of dependencies:
|
||||
|
||||
- `dartssh2/` - SSH library
|
||||
- `xterm/` - Terminal emulator
|
||||
- `fl_lib/` - Shared utilities
|
||||
- `fl_build/` - Build system
|
||||
113
docs/src/content/docs/development/testing.md
Normal file
113
docs/src/content/docs/development/testing.md
Normal file
@@ -0,0 +1,113 @@
|
||||
---
|
||||
title: Testing
|
||||
description: Testing strategies and running tests
|
||||
---
|
||||
|
||||
## Running Tests
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
flutter test
|
||||
|
||||
# Run specific test file
|
||||
flutter test test/battery_test.dart
|
||||
|
||||
# Run with coverage
|
||||
flutter test --coverage
|
||||
```
|
||||
|
||||
## Test Structure
|
||||
|
||||
Tests are located in the `test/` directory mirroring the lib structure:
|
||||
|
||||
```
|
||||
test/
|
||||
├── data/
|
||||
│ ├── model/
|
||||
│ └── provider/
|
||||
├── view/
|
||||
│ └── widget/
|
||||
└── test_helpers.dart
|
||||
```
|
||||
|
||||
## Unit Tests
|
||||
|
||||
Test business logic and data models:
|
||||
|
||||
```dart
|
||||
test('should calculate CPU percentage', () {
|
||||
final cpu = CpuModel(usage: 75.0);
|
||||
expect(cpu.usagePercentage, '75%');
|
||||
});
|
||||
```
|
||||
|
||||
## Widget Tests
|
||||
|
||||
Test UI components:
|
||||
|
||||
```dart
|
||||
testWidgets('ServerCard displays server name', (tester) async {
|
||||
await tester.pumpWidget(
|
||||
ProviderScope(
|
||||
child: MaterialApp(
|
||||
home: ServerCard(server: testServer),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.text('Test Server'), findsOneWidget);
|
||||
});
|
||||
```
|
||||
|
||||
## Provider Tests
|
||||
|
||||
Test Riverpod providers:
|
||||
|
||||
```dart
|
||||
test('serverStatusProvider returns status', () async {
|
||||
final container = ProviderContainer();
|
||||
final status = await container.read(serverStatusProvider(testServer).future);
|
||||
expect(status, isA<StatusModel>());
|
||||
});
|
||||
```
|
||||
|
||||
## Mocking
|
||||
|
||||
Use mocks for external dependencies:
|
||||
|
||||
```dart
|
||||
class MockSshService extends Mock implements SshService {}
|
||||
|
||||
test('connects to server', () async {
|
||||
final mockSsh = MockSshService();
|
||||
when(mockSsh.connect(any)).thenAnswer((_) async => true);
|
||||
|
||||
// Test with mock
|
||||
});
|
||||
```
|
||||
|
||||
## Integration Tests
|
||||
|
||||
Test complete user flows (in `integration_test/`):
|
||||
|
||||
```dart
|
||||
testWidgets('add server flow', (tester) async {
|
||||
await tester.pumpWidget(MyApp());
|
||||
|
||||
// Tap add button
|
||||
await tester.tap(find.byIcon(Icons.add));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Fill form
|
||||
await tester.enterText(find.byKey(Key('name')), 'Test Server');
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Arrange-Act-Assert**: Structure tests clearly
|
||||
2. **Descriptive names**: Test names should describe behavior
|
||||
3. **One assertion per test**: Keep tests focused
|
||||
4. **Mock external deps**: Don't depend on real servers
|
||||
5. **Test edge cases**: Empty lists, null values, etc.
|
||||
55
docs/src/content/docs/features/docker.md
Normal file
55
docs/src/content/docs/features/docker.md
Normal file
@@ -0,0 +1,55 @@
|
||||
---
|
||||
title: Docker Management
|
||||
description: Monitor and manage Docker containers
|
||||
---
|
||||
|
||||
Flutter Server Box provides an intuitive interface for managing Docker containers on your servers.
|
||||
|
||||
## Features
|
||||
|
||||
### Container List
|
||||
|
||||
- View all containers (running and stopped)
|
||||
- Container ID and name display
|
||||
- Image information
|
||||
- Status indicators
|
||||
- Creation time
|
||||
|
||||
### Container Actions
|
||||
|
||||
- **Start**: Launch stopped containers
|
||||
- **Stop**: Gracefully stop running containers
|
||||
- **Restart**: Restart containers
|
||||
- **Remove**: Delete containers
|
||||
- **View Logs**: Check container logs
|
||||
- **Inspect**: View container details
|
||||
|
||||
### Container Details
|
||||
|
||||
- Environment variables
|
||||
- Port mappings
|
||||
- Volume mounts
|
||||
- Network configuration
|
||||
- Resource usage
|
||||
|
||||
## Requirements
|
||||
|
||||
- Docker must be installed on your server
|
||||
- SSH user must have Docker permissions
|
||||
- For non-root users, add to docker group:
|
||||
```bash
|
||||
sudo usermod -aG docker your_username
|
||||
```
|
||||
|
||||
## Quick Actions
|
||||
|
||||
- Single tap: View container details
|
||||
- Long press: Quick action menu
|
||||
- Swipe: Quick start/stop
|
||||
- Bulk select: Multiple container operations
|
||||
|
||||
## Tips
|
||||
|
||||
- Use **auto-refresh** to monitor container status changes
|
||||
- Filter by running/stopped containers
|
||||
- Search containers by name or ID
|
||||
73
docs/src/content/docs/features/monitoring.md
Normal file
73
docs/src/content/docs/features/monitoring.md
Normal file
@@ -0,0 +1,73 @@
|
||||
---
|
||||
title: Server Monitoring
|
||||
description: Real-time server status monitoring with beautiful charts
|
||||
---
|
||||
|
||||
Flutter Server Box provides comprehensive real-time monitoring of your server's health and performance.
|
||||
|
||||
## Status Cards
|
||||
|
||||
The server detail page displays configurable status cards for different system metrics. You can enable/disable cards in settings.
|
||||
|
||||
### CPU Monitoring
|
||||
|
||||
- Real-time CPU usage percentage
|
||||
- Per-core CPU usage breakdown
|
||||
- Historical usage charts
|
||||
- CPU frequency information
|
||||
|
||||
### Memory Monitoring
|
||||
|
||||
- **RAM Usage**: Used vs total memory with percentage
|
||||
- **Swap Usage**: Swap memory utilization
|
||||
- Memory pressure indicators
|
||||
- Historical memory trends
|
||||
|
||||
### Disk Monitoring
|
||||
|
||||
- Mount point usage with percentage
|
||||
- Total, used, and free space
|
||||
- I/O statistics
|
||||
- Multiple disk support
|
||||
|
||||
### Network Monitoring
|
||||
|
||||
- Real-time upload/download speeds
|
||||
- Bandwidth usage charts
|
||||
- Network interface statistics
|
||||
- Total data transferred
|
||||
|
||||
### Advanced Metrics
|
||||
|
||||
- **GPU Status**: NVIDIA and AMD GPU monitoring
|
||||
- **Temperature**: CPU, GPU, and system temperatures
|
||||
- **Sensors**: Fan speeds, voltages, and other sensor data
|
||||
- **S.M.A.R.T**: Disk health monitoring
|
||||
- **Battery**: UPS or battery status (if available)
|
||||
|
||||
## Customizing Display
|
||||
|
||||
### Reordering Cards
|
||||
|
||||
1. Go to Settings
|
||||
2. Select Server Settings
|
||||
3. Drag cards to reorder them on the server detail page
|
||||
|
||||
### Enabling/Disabling Cards
|
||||
|
||||
1. Open a server's detail page
|
||||
2. Tap the edit/menu button
|
||||
3. Toggle individual cards on or off
|
||||
|
||||
## Auto-Refresh
|
||||
|
||||
- Status cards automatically refresh
|
||||
- Refresh interval is configurable in settings
|
||||
- Manual refresh available with pull-to-refresh gesture
|
||||
|
||||
## Charts and Visualizations
|
||||
|
||||
- **Line Charts**: Historical data trends
|
||||
- **Gauge Charts**: Current usage percentage
|
||||
- **Color Coding**: Visual indicators for status levels
|
||||
- **Zoom**: Pinch to zoom on charts for detailed views
|
||||
67
docs/src/content/docs/features/network.md
Normal file
67
docs/src/content/docs/features/network.md
Normal file
@@ -0,0 +1,67 @@
|
||||
---
|
||||
title: Network Tools
|
||||
description: Network testing and diagnostic tools
|
||||
---
|
||||
|
||||
Flutter Server Box includes several network tools for testing and diagnostics.
|
||||
|
||||
## iPerf
|
||||
|
||||
Perform network speed tests between your device and server.
|
||||
|
||||
### Features
|
||||
|
||||
- **Upload/Download Speed**: Test bandwidth
|
||||
- **Server Mode**: Use server as iPerf server
|
||||
- **Client Mode**: Connect to iPerf servers
|
||||
- **Custom Parameters**: Duration, parallel streams, etc.
|
||||
|
||||
### Usage
|
||||
|
||||
1. Open a server
|
||||
2. Tap **iPerf**
|
||||
3. Choose server or client mode
|
||||
4. Configure parameters
|
||||
5. Start test
|
||||
|
||||
## Ping
|
||||
|
||||
Test network connectivity and latency.
|
||||
|
||||
### Features
|
||||
|
||||
- **ICMP Ping**: Standard ping tool
|
||||
- **Packet Count**: Specify number of packets
|
||||
- **Packet Size**: Custom packet size
|
||||
- **Interval**: Time between pings
|
||||
|
||||
### Usage
|
||||
|
||||
1. Open a server
|
||||
2. Tap **Ping**
|
||||
3. Enter target host
|
||||
4. Configure parameters
|
||||
5. Start pinging
|
||||
|
||||
## Wake on LAN
|
||||
|
||||
Wake up remote servers via magic packet.
|
||||
|
||||
### Features
|
||||
|
||||
- **MAC Address**: Target device MAC
|
||||
- **Broadcast**: Send broadcast magic packet
|
||||
- **Saved Profiles**: Store WoL configurations
|
||||
|
||||
### Requirements
|
||||
|
||||
- Target device must support Wake-on-LAN
|
||||
- WoL must be enabled in BIOS/UEFI
|
||||
- Device must be in sleep/soft-off state
|
||||
- Device must be on the same network or reachable via broadcast
|
||||
|
||||
## Tips
|
||||
|
||||
- Use iPerf to diagnose network bottlenecks
|
||||
- Ping multiple hosts to compare latency
|
||||
- Save WoL profiles for frequently woken devices
|
||||
56
docs/src/content/docs/features/process.md
Normal file
56
docs/src/content/docs/features/process.md
Normal file
@@ -0,0 +1,56 @@
|
||||
---
|
||||
title: Process & Services
|
||||
description: Monitor processes and manage systemd services
|
||||
---
|
||||
|
||||
## Process Management
|
||||
|
||||
View and manage running processes on your servers.
|
||||
|
||||
### Process List
|
||||
|
||||
- All running processes with details
|
||||
- PID (Process ID)
|
||||
- CPU and memory usage
|
||||
- User ownership
|
||||
- Process command
|
||||
|
||||
### Process Actions
|
||||
|
||||
- **Kill**: Terminate processes
|
||||
- **Filter**: By name or user
|
||||
- **Sort**: By CPU, memory, or PID
|
||||
- **Search**: Find specific processes
|
||||
|
||||
## Systemd Services
|
||||
|
||||
Manage systemd services for service control.
|
||||
|
||||
### Service List
|
||||
|
||||
- All systemd services
|
||||
- Active/inactive status
|
||||
- Enabled/disabled state
|
||||
- Service description
|
||||
|
||||
### Service Actions
|
||||
|
||||
- **Start**: Launch a stopped service
|
||||
- **Stop**: Stop a running service
|
||||
- **Restart**: Restart a service
|
||||
- **Enable**: Enable auto-start on boot
|
||||
- **Disable**: Disable auto-start
|
||||
- **View Status**: Check service status and logs
|
||||
- **Reload**: Reload service configuration
|
||||
|
||||
## Requirements
|
||||
|
||||
- SSH user must have appropriate permissions
|
||||
- For service management: `sudo` access may be required
|
||||
- Process viewing: Standard user permissions usually sufficient
|
||||
|
||||
## Tips
|
||||
|
||||
- Use process list to identify resource hogs
|
||||
- Check service logs for troubleshooting
|
||||
- Monitor critical services with auto-refresh
|
||||
105
docs/src/content/docs/features/pve.md
Normal file
105
docs/src/content/docs/features/pve.md
Normal file
@@ -0,0 +1,105 @@
|
||||
---
|
||||
title: Proxmox (PVE)
|
||||
description: Proxmox Virtual Environment management
|
||||
---
|
||||
|
||||
Flutter Server Box includes support for managing Proxmox VE virtualization platform.
|
||||
|
||||
## Features
|
||||
|
||||
### VM Management
|
||||
|
||||
- **List VMs**: View all virtual machines
|
||||
- **VM Status**: Check running/stopped states
|
||||
- **VM Actions**: Start, stop, restart VMs
|
||||
- **VM Details**: View configuration and resources
|
||||
|
||||
### Container (LXC) Management
|
||||
|
||||
- **List Containers**: View all LXC containers
|
||||
- **Container Status**: Monitor container states
|
||||
- **Container Actions**: Start, stop, restart containers
|
||||
- **Console Access**: Terminal access to containers
|
||||
|
||||
### Node Monitoring
|
||||
|
||||
- **Resource Usage**: CPU, memory, disk, network
|
||||
- **Node Status**: Check node health
|
||||
- **Cluster View**: Multi-node cluster overview
|
||||
|
||||
## Setup
|
||||
|
||||
### Adding PVE Server
|
||||
|
||||
1. Add server as normal SSH connection
|
||||
2. Ensure user has PVE permissions
|
||||
3. Access PVE features from server detail page
|
||||
|
||||
### Permissions Required
|
||||
|
||||
PVE user needs:
|
||||
|
||||
- **VM.Audit**: View VM status
|
||||
- **VM.PowerMgmt**: Start/stop VMs
|
||||
- **VM.Console**: Console access
|
||||
|
||||
Example permissions setup:
|
||||
|
||||
```bash
|
||||
pveum useradd myuser -password mypass
|
||||
pveum aclmod /vms -user myuser@pve -role VMAdmin
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### VM Management
|
||||
|
||||
1. Open server with PVE
|
||||
2. Tap **PVE** button
|
||||
3. View VM list
|
||||
4. Tap VM for details
|
||||
5. Use action buttons for management
|
||||
|
||||
### Container Management
|
||||
|
||||
1. Open server with PVE
|
||||
2. Tap **PVE** button
|
||||
3. Switch to Containers tab
|
||||
4. View and manage LXC containers
|
||||
|
||||
### Monitoring
|
||||
|
||||
- Real-time resource usage
|
||||
- Historical data charts
|
||||
- Multiple node support
|
||||
|
||||
## Features by Status
|
||||
|
||||
### Implemented
|
||||
|
||||
- VM listing and status
|
||||
- Container listing and status
|
||||
- Basic VM operations (start/stop/restart)
|
||||
- Resource monitoring
|
||||
|
||||
### Planned
|
||||
|
||||
- VM creation from templates
|
||||
- Snapshot management
|
||||
- Console access
|
||||
- Storage management
|
||||
- Network configuration
|
||||
|
||||
## Requirements
|
||||
|
||||
- **PVE Version**: 6.x or 7.x
|
||||
- **Access**: SSH access to PVE host
|
||||
- **Permissions**: Appropriate PVE user roles
|
||||
- **Network**: Connectivity to PVE API (via SSH)
|
||||
|
||||
## Tips
|
||||
|
||||
- Use **dedicated PVE user** with limited permissions
|
||||
- Monitor **resource usage** for optimal performance
|
||||
- Check **VM status** before maintenance
|
||||
- Use **snapshots** before major changes
|
||||
60
docs/src/content/docs/features/snippets.md
Normal file
60
docs/src/content/docs/features/snippets.md
Normal file
@@ -0,0 +1,60 @@
|
||||
---
|
||||
title: Snippets
|
||||
description: Save and execute custom shell commands
|
||||
---
|
||||
|
||||
Snippets allow you to save frequently used shell commands for quick execution.
|
||||
|
||||
## Creating Snippets
|
||||
|
||||
1. Go to the **Snippets** tab
|
||||
2. Tap the **+** button
|
||||
3. Fill in snippet details:
|
||||
- **Name**: Friendly name for the snippet
|
||||
- **Command**: The shell command to execute
|
||||
- **Description**: Optional notes
|
||||
4. Save the snippet
|
||||
|
||||
## Using Snippets
|
||||
|
||||
1. Open a server
|
||||
2. Tap the **Snippet** button
|
||||
3. Select a snippet to execute
|
||||
4. View output in the terminal
|
||||
|
||||
## Snippet Features
|
||||
|
||||
- **Quick Execute**: One-tap command execution
|
||||
- **Variables**: Use server-specific variables
|
||||
- **Organization**: Group related snippets
|
||||
- **Import/Export**: Share snippets between devices
|
||||
- **Sync**: Optional cloud sync
|
||||
|
||||
## Example Snippets
|
||||
|
||||
### System Update
|
||||
```bash
|
||||
sudo apt update && sudo apt upgrade -y
|
||||
```
|
||||
|
||||
### Disk Cleanup
|
||||
```bash
|
||||
sudo apt autoremove -y && sudo apt clean
|
||||
```
|
||||
|
||||
### Docker Cleanup
|
||||
```bash
|
||||
docker system prune -a
|
||||
```
|
||||
|
||||
### View System Logs
|
||||
```bash
|
||||
journalctl -n 50 -f
|
||||
```
|
||||
|
||||
## Tips
|
||||
|
||||
- Use **descriptive names** for easy identification
|
||||
- Add **comments** for complex commands
|
||||
- Test commands before saving as snippets
|
||||
- Organize snippets by category or server type
|
||||
46
docs/src/content/docs/index.mdx
Normal file
46
docs/src/content/docs/index.mdx
Normal file
@@ -0,0 +1,46 @@
|
||||
---
|
||||
title: Flutter Server Box
|
||||
description: A comprehensive cross-platform server management application
|
||||
hero:
|
||||
tagline: Manage your Linux servers from anywhere
|
||||
actions:
|
||||
- text: Get Started
|
||||
link: /introduction/
|
||||
icon: right-arrow
|
||||
variant: primary
|
||||
- text: View on GitHub
|
||||
link: https://github.com/lollipopkit/flutter_server_box
|
||||
icon: github
|
||||
variant: minimal
|
||||
---
|
||||
|
||||
import { Card, CardGrid } from '@astrojs/starlight/components';
|
||||
|
||||
## Features
|
||||
|
||||
<CardGrid stagger>
|
||||
<Card title="Real-time Monitoring" icon="chart">
|
||||
Monitor CPU, memory, disk, network, GPU, and temperature with beautiful real-time charts.
|
||||
</Card>
|
||||
<Card title="SSH Terminal" icon="terminal">
|
||||
Full-featured SSH terminal with multi-tab support and virtual keyboard for mobile devices.
|
||||
</Card>
|
||||
<Card title="SFTP File Browser" icon="folder">
|
||||
Manage files on your servers with the built-in SFTP client and local file browser.
|
||||
</Card>
|
||||
<Card title="Docker Management" icon="box">
|
||||
Start, stop, and monitor Docker containers with an intuitive interface.
|
||||
</Card>
|
||||
<Card title="Cross-Platform" icon="device-mobile">
|
||||
Available on iOS, Android, macOS, Linux, Windows, and watchOS.
|
||||
</Card>
|
||||
<Card title="12+ Languages" icon="globe">
|
||||
Full localization support including English, Chinese, German, French, and more.
|
||||
</Card>
|
||||
</CardGrid>
|
||||
|
||||
## Quick Links
|
||||
|
||||
- **Download**: Available on [App Store](https://apps.apple.com/app/flutter-server-box), [Google Play](https://play.google.com/store/apps/details), [GitHub](https://github.com/lollipopkit/flutter_server_box/releases), and [F-Droid](https://f-droid.org/)
|
||||
- **Documentation**: Explore the guides to get started with Flutter Server Box
|
||||
- **Support**: Join our community on GitHub for discussions and issues
|
||||
52
docs/src/content/docs/installation.mdx
Normal file
52
docs/src/content/docs/installation.mdx
Normal file
@@ -0,0 +1,52 @@
|
||||
---
|
||||
title: Installation
|
||||
description: Download and install Flutter Server Box on your device
|
||||
---
|
||||
|
||||
Flutter Server Box is available on multiple platforms. Choose your preferred method of installation.
|
||||
|
||||
## Mobile Apps
|
||||
|
||||
### iOS
|
||||
|
||||
Download from the **[App Store](https://apps.apple.com/app/flutter-server-box)**.
|
||||
|
||||
### Android
|
||||
|
||||
Choose your preferred source:
|
||||
|
||||
- **[Google Play](https://play.google.com/store/apps/details)** - Recommended for most users
|
||||
- **[F-Droid](https://f-droid.org/)** - For users who prefer FOSS-only sources
|
||||
- **[GitHub Releases](https://github.com/lollipopkit/flutter_server_box/releases)** - For the latest version directly from the source
|
||||
|
||||
## Desktop Apps
|
||||
|
||||
### macOS
|
||||
|
||||
Download from **[GitHub Releases](https://github.com/lollipopkit/flutter_server_box/releases)**.
|
||||
|
||||
Features:
|
||||
- Native menu bar integration
|
||||
- Support for both Intel and Apple Silicon
|
||||
|
||||
### Linux
|
||||
|
||||
Download from **[GitHub Releases](https://github.com/lollipopkit/flutter_server_box/releases)**.
|
||||
|
||||
Available as AppImage, deb, or tar.gz packages.
|
||||
|
||||
### Windows
|
||||
|
||||
Download from **[GitHub Releases](https://github.com/lollipopkit/flutter_server_box/releases)**.
|
||||
|
||||
## watchOS
|
||||
|
||||
Available on the **[App Store](https://apps.apple.com/app/flutter-server-box)** as part of the iOS app.
|
||||
|
||||
## Building from Source
|
||||
|
||||
To build Flutter Server Box from source, see the [Building](/development/building) section in the Development documentation.
|
||||
|
||||
## Version Information
|
||||
|
||||
Check the [GitHub Releases](https://github.com/lollipopkit/flutter_server_box/releases) page for the latest version and changelog.
|
||||
33
docs/src/content/docs/introduction.mdx
Normal file
33
docs/src/content/docs/introduction.mdx
Normal file
@@ -0,0 +1,33 @@
|
||||
---
|
||||
title: Introduction
|
||||
description: Learn what Flutter Server Box is and what it can do
|
||||
---
|
||||
|
||||
Flutter Server Box is a comprehensive cross-platform server management application built with Flutter. It allows you to monitor, manage, and control your Linux, Unix, and Windows servers from anywhere.
|
||||
|
||||
## What is Flutter Server Box?
|
||||
|
||||
Flutter Server Box provides a unified interface for server administration tasks through SSH connections. Whether you're a system administrator, developer, or hobbyist running home servers, this app puts powerful server management tools in your pocket.
|
||||
|
||||
## Key Capabilities
|
||||
|
||||
- **Real-time Monitoring**: Track CPU, memory, disk usage, network speed, GPU status, and system temperatures
|
||||
- **SSH Terminal**: Full terminal access with multi-tab support and customizable appearance
|
||||
- **SFTP Client**: Browse and manage files on your servers
|
||||
- **Docker Management**: Control containers with ease
|
||||
- **Process Management**: View and manage system processes
|
||||
- **Systemd Services**: Start, stop, and monitor systemd services
|
||||
- **Network Tools**: iPerf testing, ping, and Wake-on-LAN
|
||||
- **Snippets**: Save and execute custom shell commands
|
||||
|
||||
## Supported Platforms
|
||||
|
||||
Flutter Server Box is truly cross-platform:
|
||||
|
||||
- **Mobile**: iOS and Android
|
||||
- **Desktop**: macOS, Linux, and Windows
|
||||
- **Wearable**: watchOS (Apple Watch)
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under AGPL v3. Source code is available on [GitHub](https://github.com/lollipopkit/flutter_server_box).
|
||||
80
docs/src/content/docs/platforms/desktop.md
Normal file
80
docs/src/content/docs/platforms/desktop.md
Normal file
@@ -0,0 +1,80 @@
|
||||
---
|
||||
title: Desktop Features
|
||||
description: macOS, Linux, and Windows specific features
|
||||
---
|
||||
|
||||
Flutter Server Box on desktop platforms provides additional productivity features.
|
||||
|
||||
## macOS
|
||||
|
||||
### Menu Bar Integration
|
||||
|
||||
- Quick server status in menu bar
|
||||
- One-click server access
|
||||
- Compact mode for minimal distraction
|
||||
- Native macOS menu bar styling
|
||||
|
||||
### Window State Persistence
|
||||
|
||||
- Remembers window position and size
|
||||
- Restore previous session on launch
|
||||
- Multiple monitor support
|
||||
|
||||
### Native Features
|
||||
|
||||
- **Title Bar**: Custom or system title bar option
|
||||
- **Full Screen Mode**: Dedicated server monitoring
|
||||
- **Keyboard Shortcuts**: macOS-native shortcuts
|
||||
- **Touch Bar** (supported devices): Quick actions
|
||||
|
||||
## Linux
|
||||
|
||||
### Native Integration
|
||||
|
||||
- System tray support
|
||||
- Desktop notification integration
|
||||
- File picker integration
|
||||
|
||||
### Window Management
|
||||
|
||||
- X11 and Wayland support
|
||||
- Tiling window manager friendly
|
||||
- Custom window decorations option
|
||||
|
||||
## Windows
|
||||
|
||||
### Features
|
||||
|
||||
- System tray integration
|
||||
- Jump List quick actions
|
||||
- Native window controls
|
||||
- Auto-start on boot option
|
||||
|
||||
## Cross-Platform Desktop Features
|
||||
|
||||
### Keyboard Shortcuts
|
||||
|
||||
- **Cmd/Ctrl + N**: New server
|
||||
- **Cmd/Ctrl + W**: Close tab
|
||||
- **Cmd/Ctrl + T**: New terminal tab
|
||||
- **Cmd/Ctrl + ,**: Settings
|
||||
|
||||
### Themes
|
||||
|
||||
- Light theme
|
||||
- Dark theme
|
||||
- AMOLED theme (pure black)
|
||||
- System theme (follows OS)
|
||||
|
||||
### Multiple Windows
|
||||
|
||||
- Open multiple servers in separate windows
|
||||
- Drag tabs to new window
|
||||
- Compare server stats side-by-side
|
||||
|
||||
### Advantages Over Mobile
|
||||
|
||||
- Larger screen for monitoring
|
||||
- Full keyboard for terminal
|
||||
- Faster file operations
|
||||
- Better multitasking
|
||||
77
docs/src/content/docs/platforms/mobile.md
Normal file
77
docs/src/content/docs/platforms/mobile.md
Normal file
@@ -0,0 +1,77 @@
|
||||
---
|
||||
title: Mobile Features
|
||||
description: iOS and Android specific features
|
||||
---
|
||||
|
||||
Flutter Server Box provides several mobile-specific features for iOS and Android devices.
|
||||
|
||||
## Biometric Authentication
|
||||
|
||||
Secure your servers with biometric authentication:
|
||||
|
||||
- **iOS**: Face ID or Touch ID
|
||||
- **Android**: Fingerprint authentication
|
||||
|
||||
Enable in Settings > Security > Biometric Authentication
|
||||
|
||||
## Home Screen Widgets
|
||||
|
||||
Add server status widgets to your home screen for quick monitoring.
|
||||
|
||||
### iOS
|
||||
|
||||
- Long press on home screen
|
||||
- Tap **+** to add widget
|
||||
- Search for "Flutter Server Box"
|
||||
- Choose widget size:
|
||||
- Small: Single server status
|
||||
- Medium: Multiple servers
|
||||
- Large: Detailed info
|
||||
|
||||
### Android
|
||||
|
||||
- Long press on home screen
|
||||
- Tap **Widgets**
|
||||
- Find "Flutter Server Box"
|
||||
- Select widget type
|
||||
|
||||
## Background Running
|
||||
|
||||
### Android
|
||||
|
||||
Keep connections alive in the background:
|
||||
|
||||
- Enable in Settings > Advanced > Background Running
|
||||
- Requires battery optimization exclusion
|
||||
- Persistent notifications for active connections
|
||||
|
||||
### iOS
|
||||
|
||||
Background limitations apply:
|
||||
|
||||
- Connections may pause in background
|
||||
- Quick reconnect on return to app
|
||||
- Background refresh support
|
||||
|
||||
## Push Notifications
|
||||
|
||||
Receive notifications for:
|
||||
|
||||
- Server offline alerts
|
||||
- High resource usage warnings
|
||||
- Task completion alerts
|
||||
|
||||
Configure in Settings > Notifications
|
||||
|
||||
## Mobile UI Features
|
||||
|
||||
- **Pull to Refresh**: Update server status
|
||||
- **Swipe Actions**: Quick server operations
|
||||
- **Landscape Mode**: Better terminal experience
|
||||
- **Virtual Keyboard**: Terminal shortcuts
|
||||
|
||||
## File Integration
|
||||
|
||||
- **Files App (iOS)**: Direct SFTP access from Files
|
||||
- **Storage Access Framework (Android)**: Share files with other apps
|
||||
- **Document Picker**: Easy file selection
|
||||
55
docs/src/content/docs/platforms/watchos.md
Normal file
55
docs/src/content/docs/platforms/watchos.md
Normal file
@@ -0,0 +1,55 @@
|
||||
---
|
||||
title: watchOS App
|
||||
description: Apple Watch companion app
|
||||
---
|
||||
|
||||
Flutter Server Box includes a companion app for Apple Watch, providing quick server monitoring on your wrist.
|
||||
|
||||
## Features
|
||||
|
||||
### Server Status at a Glance
|
||||
|
||||
- View server online/offline status
|
||||
- Quick CPU and memory stats
|
||||
- One-tap server connectivity check
|
||||
|
||||
### Complications
|
||||
|
||||
Add server information to your watch face:
|
||||
|
||||
- **Modular**: Server status icon
|
||||
- **Infograph**: Small complication
|
||||
- **Extra Large**: Large complication
|
||||
|
||||
### Quick Actions
|
||||
|
||||
- Ping servers
|
||||
- Wake on LAN
|
||||
- Quick terminal command
|
||||
|
||||
## Requirements
|
||||
|
||||
- Apple Watch Series 3 or later
|
||||
- watchOS 8.0 or later
|
||||
- Paired iPhone with Flutter Server Box installed
|
||||
- iPhone and Watch on same Wi-Fi
|
||||
|
||||
## Setup
|
||||
|
||||
1. Install Flutter Server Box on iPhone
|
||||
2. Open the Watch app on iPhone
|
||||
3. Find Flutter Server Box under "Available Apps"
|
||||
4. Toggle "Show App on Apple Watch"
|
||||
|
||||
## Limitations
|
||||
|
||||
- No full terminal access
|
||||
- Reduced SFTP functionality
|
||||
- Dependent on iPhone connection
|
||||
- Simplified server management
|
||||
|
||||
## Tips
|
||||
|
||||
- Use complications for persistent status
|
||||
- Quick actions for emergency checks
|
||||
- Sync servers from iPhone app
|
||||
214
docs/src/content/docs/principles/architecture.md
Normal file
214
docs/src/content/docs/principles/architecture.md
Normal file
@@ -0,0 +1,214 @@
|
||||
---
|
||||
title: Architecture Overview
|
||||
description: High-level application architecture
|
||||
---
|
||||
|
||||
Flutter Server Box follows a layered architecture with clear separation of concerns.
|
||||
|
||||
## Architecture Layers
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ Presentation Layer (UI) │
|
||||
│ lib/view/page/, lib/view/widget/ │
|
||||
│ - Pages, Widgets, Controllers │
|
||||
└─────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ Business Logic Layer │
|
||||
│ lib/data/provider/ │
|
||||
│ - Riverpod Providers, State Notifiers │
|
||||
└─────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ Data Access Layer │
|
||||
│ lib/data/store/, lib/data/model/ │
|
||||
│ - Hive Stores, Data Models │
|
||||
└─────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ External Integration Layer │
|
||||
│ - SSH (dartssh2), Terminal (xterm), SFTP │
|
||||
│ - Platform-specific code (iOS, Android, etc.) │
|
||||
└─────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Application Foundation
|
||||
|
||||
### Main Entry Point
|
||||
|
||||
`lib/main.dart` initializes the app:
|
||||
|
||||
```dart
|
||||
void main() {
|
||||
runApp(
|
||||
ProviderScope(
|
||||
child: MyApp(),
|
||||
),
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Root Widget
|
||||
|
||||
`MyApp` provides:
|
||||
- **Theme Management**: Light/dark theme switching
|
||||
- **Routing Configuration**: Navigation structure
|
||||
- **Provider Scope**: Dependency injection root
|
||||
|
||||
### Home Page
|
||||
|
||||
`HomePage` serves as navigation hub:
|
||||
- **Tabbed Interface**: Server, Snippet, Container, SSH
|
||||
- **State Management**: Per-tab state
|
||||
- **Navigation**: Feature access
|
||||
|
||||
## Core Systems
|
||||
|
||||
### State Management: Riverpod
|
||||
|
||||
**Why Riverpod?**
|
||||
- Compile-time safety
|
||||
- Easy testing
|
||||
- No Build context dependency
|
||||
- Works across platforms
|
||||
|
||||
**Provider Types Used:**
|
||||
- `StateProvider`: Simple mutable state
|
||||
- `AsyncNotifierProvider`: Loading/error/data states
|
||||
- `StreamProvider`: Real-time data streams
|
||||
- Future providers: One-time async operations
|
||||
|
||||
### Data Persistence: Hive CE
|
||||
|
||||
**Why Hive CE?**
|
||||
- No native code dependencies
|
||||
- Fast key-value storage
|
||||
- Type-safe with code generation
|
||||
- No manual field annotations needed
|
||||
|
||||
**Stores:**
|
||||
- `SettingStore`: App preferences
|
||||
- `ServerStore`: Server configurations
|
||||
- `SnippetStore`: Command snippets
|
||||
- `KeyStore`: SSH keys
|
||||
|
||||
### Immutable Models: Freezed
|
||||
|
||||
**Benefits:**
|
||||
- Compile-time immutability
|
||||
- Union types for state
|
||||
- Built-in JSON serialization
|
||||
- CopyWith extensions
|
||||
|
||||
## Cross-Platform Strategy
|
||||
|
||||
### Plugin System
|
||||
|
||||
Flutter plugins provide platform integration:
|
||||
|
||||
| Platform | Integration Method |
|
||||
|----------|-------------------|
|
||||
| iOS | CocoaPods, Swift/Obj-C |
|
||||
| Android | Gradle, Kotlin/Java |
|
||||
| macOS | CocoaPods, Swift |
|
||||
| Linux | CMake, C++ |
|
||||
| Windows | CMake, C# |
|
||||
|
||||
### Platform-Specific Features
|
||||
|
||||
**iOS Only:**
|
||||
- Home screen widgets
|
||||
- Live Activities
|
||||
- Apple Watch companion
|
||||
|
||||
**Android Only:**
|
||||
- Background service
|
||||
- Push notifications
|
||||
- File system access
|
||||
|
||||
**Desktop Only:**
|
||||
- Menu bar integration
|
||||
- Multiple windows
|
||||
- Custom title bar
|
||||
|
||||
## Custom Dependencies
|
||||
|
||||
### dartssh2 Fork
|
||||
|
||||
Enhanced SSH client with:
|
||||
- Better mobile support
|
||||
- Enhanced error handling
|
||||
- Performance optimizations
|
||||
|
||||
### xterm.dart Fork
|
||||
|
||||
Terminal emulator with:
|
||||
- Mobile-optimized rendering
|
||||
- Touch gesture support
|
||||
- Virtual keyboard integration
|
||||
|
||||
### fl_lib
|
||||
|
||||
Shared utilities package with:
|
||||
- Common widgets
|
||||
- Extensions
|
||||
- Helper functions
|
||||
|
||||
## Build System
|
||||
|
||||
### fl_build Package
|
||||
|
||||
Custom build system for:
|
||||
- Multi-platform builds
|
||||
- Code signing
|
||||
- Asset bundling
|
||||
- Version management
|
||||
|
||||
### Build Process
|
||||
|
||||
```
|
||||
make.dart (version) → fl_build (build) → Platform output
|
||||
```
|
||||
|
||||
1. **Pre-build**: Calculate version from Git
|
||||
2. **Build**: Compile for target platform
|
||||
3. **Post-build**: Package and sign
|
||||
|
||||
## Data Flow Example
|
||||
|
||||
### Server Status Update
|
||||
|
||||
```
|
||||
1. Timer triggers →
|
||||
2. Provider calls service →
|
||||
3. Service executes SSH command →
|
||||
4. Response parsed to model →
|
||||
5. State updated →
|
||||
6. UI rebuilds with new data
|
||||
```
|
||||
|
||||
### User Action Flow
|
||||
|
||||
```
|
||||
1. User taps button →
|
||||
2. Widget calls provider method →
|
||||
3. Provider updates state →
|
||||
4. State change triggers rebuild →
|
||||
5. New state reflected in UI
|
||||
```
|
||||
|
||||
## Security Architecture
|
||||
|
||||
### Data Protection
|
||||
|
||||
- **Passwords**: Encrypted with flutter_secure_storage
|
||||
- **SSH Keys**: Encrypted at rest
|
||||
- **Host Fingerprints**: Stored securely
|
||||
- **Session Data**: Not persisted
|
||||
|
||||
### Connection Security
|
||||
|
||||
- **Host Key Verification**: MITM detection
|
||||
- **Encryption**: Standard SSH encryption
|
||||
- **No Plain Text**: Sensitive data never stored plain
|
||||
490
docs/src/content/docs/principles/sftp.md
Normal file
490
docs/src/content/docs/principles/sftp.md
Normal file
@@ -0,0 +1,490 @@
|
||||
---
|
||||
title: SFTP System
|
||||
description: How the SFTP file browser works
|
||||
---
|
||||
|
||||
The SFTP system provides file management capabilities over SSH.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ SFTP UI Layer │
|
||||
│ - File browser (remote) │
|
||||
│ - File browser (local) │
|
||||
│ - Transfer queue │
|
||||
└─────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ SFTP State Management │
|
||||
│ - sftpProvider │
|
||||
│ - Path management │
|
||||
│ - Operation queue │
|
||||
└─────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ SFTP Protocol Layer │
|
||||
│ - SSH subsystem │
|
||||
│ - File operations │
|
||||
│ - Directory listing │
|
||||
└─────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ SSH Transport │
|
||||
│ - Secure channel │
|
||||
│ - Data streaming │
|
||||
└─────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Connection Establishment
|
||||
|
||||
### SFTP Client Creation
|
||||
|
||||
```dart
|
||||
Future<SftpClient> createSftpClient(Spi spi) async {
|
||||
// 1. Get SSH client (reuse if available)
|
||||
final sshClient = await genClient(spi);
|
||||
|
||||
// 2. Open SFTP subsystem
|
||||
final sftp = await sshClient.openSftp();
|
||||
|
||||
return sftp;
|
||||
}
|
||||
```
|
||||
|
||||
### Connection Reuse
|
||||
|
||||
SFTP reuses existing SSH connections:
|
||||
|
||||
```dart
|
||||
class ServerProvider {
|
||||
SSHClient? _sshClient;
|
||||
SftpClient? _sftpClient;
|
||||
|
||||
Future<SftpClient> getSftpClient(String spiId) async {
|
||||
_sftpClient ??= await _sshClient!.openSftp();
|
||||
return _sftpClient!;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## File System Operations
|
||||
|
||||
### Directory Listing
|
||||
|
||||
```dart
|
||||
Future<List<SftpFile>> listDirectory(String path) async {
|
||||
final sftp = await getSftpClient(spiId);
|
||||
|
||||
// List directory
|
||||
final files = await sftp.listDir(path);
|
||||
|
||||
// Sort based on settings
|
||||
files.sort((a, b) {
|
||||
switch (sortOption) {
|
||||
case SortOption.name:
|
||||
return a.name.toLowerCase().compareTo(b.name.toLowerCase());
|
||||
case SortOption.size:
|
||||
return a.size.compareTo(b.size);
|
||||
case SortOption.time:
|
||||
return a.modified.compareTo(b.modified);
|
||||
}
|
||||
});
|
||||
|
||||
// Folders first if enabled
|
||||
if (showFoldersFirst) {
|
||||
final dirs = files.where((f) => f.isDirectory);
|
||||
final regular = files.where((f) => !f.isDirectory);
|
||||
return [...dirs, ...regular];
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
```
|
||||
|
||||
### File Metadata
|
||||
|
||||
```dart
|
||||
class SftpFile {
|
||||
final String name;
|
||||
final String path;
|
||||
final int size; // Bytes
|
||||
final int modified; // Unix timestamp
|
||||
final String permissions; // e.g., "rwxr-xr-x"
|
||||
final String owner;
|
||||
final String group;
|
||||
final bool isDirectory;
|
||||
final bool isSymlink;
|
||||
|
||||
String get sizeFormatted => formatBytes(size);
|
||||
String get modifiedFormatted => formatDate(modified);
|
||||
}
|
||||
```
|
||||
|
||||
## File Operations
|
||||
|
||||
### Upload
|
||||
|
||||
```dart
|
||||
Future<void> uploadFile(
|
||||
String localPath,
|
||||
String remotePath,
|
||||
) async {
|
||||
final sftp = await getSftpClient(spiId);
|
||||
|
||||
// Create request
|
||||
final req = SftpReq(
|
||||
spi: spi,
|
||||
remotePath: remotePath,
|
||||
localPath: localPath,
|
||||
type: SftpReqType.upload,
|
||||
);
|
||||
|
||||
// Add to queue
|
||||
_transferQueue.add(req);
|
||||
|
||||
// Execute transfer with progress
|
||||
final file = File(localPath);
|
||||
final size = await file.length();
|
||||
final stream = file.openRead();
|
||||
|
||||
await sftp.upload(
|
||||
stream: stream,
|
||||
toPath: remotePath,
|
||||
onProgress: (transferred) {
|
||||
_updateProgress(req, transferred, size);
|
||||
},
|
||||
);
|
||||
|
||||
// Complete
|
||||
_transferQueue.remove(req);
|
||||
}
|
||||
```
|
||||
|
||||
### Download
|
||||
|
||||
```dart
|
||||
Future<void> downloadFile(
|
||||
String remotePath,
|
||||
String localPath,
|
||||
) async {
|
||||
final sftp = await getSftpClient(spiId);
|
||||
|
||||
// Create local file
|
||||
final file = File(localPath);
|
||||
final sink = file.openWrite();
|
||||
|
||||
// Download with progress
|
||||
final stat = await sftp.stat(remotePath);
|
||||
|
||||
await sftp.download(
|
||||
fromPath: remotePath,
|
||||
toSink: sink,
|
||||
onProgress: (transferred) {
|
||||
_updateProgress(
|
||||
SftpReq(...),
|
||||
transferred,
|
||||
stat.size,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
await sink.close();
|
||||
}
|
||||
```
|
||||
|
||||
### Permission Editing
|
||||
|
||||
```dart
|
||||
Future<void> setPermissions(
|
||||
String path,
|
||||
String permissions,
|
||||
) async {
|
||||
final sftp = await getSftpClient(spiId);
|
||||
|
||||
// Parse permissions (e.g., "rwxr-xr-x" or "755")
|
||||
final mode = parsePermissions(permissions);
|
||||
|
||||
// Set via SSH command (more reliable than SFTP)
|
||||
final ssh = await getSshClient(spiId);
|
||||
await ssh.exec('chmod $mode "$path"');
|
||||
}
|
||||
```
|
||||
|
||||
## Path Management
|
||||
|
||||
### Path Structure
|
||||
|
||||
```dart
|
||||
class PathWithPrefix {
|
||||
final String prefix; // e.g., "/home/user"
|
||||
final String path; // Relative or absolute
|
||||
|
||||
String get fullPath {
|
||||
if (path.startsWith('/')) {
|
||||
return path; // Absolute path
|
||||
}
|
||||
return '$prefix/$path'; // Relative path
|
||||
}
|
||||
|
||||
PathWithPrefix cd(String subPath) {
|
||||
return PathWithPrefix(
|
||||
prefix: fullPath,
|
||||
path: subPath,
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Navigation History
|
||||
|
||||
```dart
|
||||
class PathHistory {
|
||||
final List<String> _history = [];
|
||||
int _index = -1;
|
||||
|
||||
void push(String path) {
|
||||
// Remove forward history
|
||||
_history.removeRange(_index + 1, _history.length);
|
||||
_history.add(path);
|
||||
_index = _history.length - 1;
|
||||
}
|
||||
|
||||
String? back() {
|
||||
if (_index > 0) {
|
||||
_index--;
|
||||
return _history[_index];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
String? forward() {
|
||||
if (_index < _history.length - 1) {
|
||||
_index++;
|
||||
return _history[_index];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Transfer System
|
||||
|
||||
### Transfer Request
|
||||
|
||||
```dart
|
||||
class SftpReq {
|
||||
final Spi spi;
|
||||
final String remotePath;
|
||||
final String localPath;
|
||||
final SftpReqType type;
|
||||
final DateTime createdAt;
|
||||
|
||||
int? totalBytes;
|
||||
int? transferredBytes;
|
||||
String? error;
|
||||
}
|
||||
```
|
||||
|
||||
### Progress Tracking
|
||||
|
||||
```dart
|
||||
class TransferProgress {
|
||||
final SftpReq request;
|
||||
final int total;
|
||||
final int transferred;
|
||||
final DateTime startTime;
|
||||
|
||||
double get percentage => (transferred / total) * 100;
|
||||
Duration get elapsed => DateTime.now().difference(startTime);
|
||||
|
||||
String get speedFormatted {
|
||||
final bytesPerSecond = transferred / elapsed.inSeconds;
|
||||
return formatSpeed(bytesPerSecond);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Queue Management
|
||||
|
||||
```dart
|
||||
class TransferQueue {
|
||||
final List<SftpReq> _queue = [];
|
||||
final Map<String, TransferProgress> _progress = {};
|
||||
int _concurrent = 3; // Max concurrent transfers
|
||||
|
||||
Future<void> process() async {
|
||||
final active = _progress.values.where((p) => p.isInProgress);
|
||||
if (active.length >= _concurrent) return;
|
||||
|
||||
final pending = _queue.where((r) => !_progress.containsKey(r.id));
|
||||
for (final req in pending.take(_concurrent - active.length)) {
|
||||
_executeTransfer(req);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _executeTransfer(SftpReq req) async {
|
||||
try {
|
||||
_progress[req.id] = TransferProgress.inProgress(req);
|
||||
|
||||
if (req.type == SftpReqType.upload) {
|
||||
await uploadFile(req.localPath, req.remotePath);
|
||||
} else {
|
||||
await downloadFile(req.remotePath, req.localPath);
|
||||
}
|
||||
|
||||
_progress[req.id] = TransferProgress.completed(req);
|
||||
} catch (e) {
|
||||
_progress[req.id] = TransferProgress.failed(req, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Local Storage Pattern
|
||||
|
||||
### Download Cache
|
||||
|
||||
Downloaded files stored at:
|
||||
|
||||
```dart
|
||||
String getLocalDownloadPath(String spiId, String remotePath) {
|
||||
final normalized = remotePath.replaceAll('/', '_');
|
||||
return 'Paths.file/$spiId/$normalized';
|
||||
}
|
||||
```
|
||||
|
||||
Example:
|
||||
- Remote: `/var/log/nginx/access.log`
|
||||
- spiId: `server-123`
|
||||
- Local: `Paths.file/server-123/_var_log_nginx_access.log`
|
||||
|
||||
## File Editing
|
||||
|
||||
### Edit Workflow
|
||||
|
||||
```dart
|
||||
Future<void> editFile(String path) async {
|
||||
final sftp = await getSftpClient(spiId);
|
||||
|
||||
// 1. Check size
|
||||
final stat = await sftp.stat(path);
|
||||
if (stat.size > editorMaxSize) {
|
||||
showWarning('File too large for built-in editor');
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. Download to temp
|
||||
final temp = await downloadToTemp(path);
|
||||
|
||||
// 3. Open in editor
|
||||
final content = await openEditor(temp.path);
|
||||
|
||||
// 4. Upload back
|
||||
await uploadFile(temp.path, path);
|
||||
|
||||
// 5. Cleanup
|
||||
await temp.delete();
|
||||
}
|
||||
```
|
||||
|
||||
### External Editor Integration
|
||||
|
||||
```dart
|
||||
Future<void> editInExternalEditor(String path) async {
|
||||
final ssh = await getSshClient(spiId);
|
||||
|
||||
// Open terminal with editor
|
||||
final editor = getSetting('sftpEditor', 'vim');
|
||||
await ssh.exec('$editor "$path"');
|
||||
|
||||
// User edits in terminal
|
||||
// After save, refresh SFTP view
|
||||
}
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Permission Errors
|
||||
|
||||
```dart
|
||||
try {
|
||||
await sftp.upload(...);
|
||||
} on SftpPermissionException {
|
||||
showError('Permission denied: ${stat.path}');
|
||||
showHint('Check file permissions and ownership');
|
||||
}
|
||||
```
|
||||
|
||||
### Connection Errors
|
||||
|
||||
```dart
|
||||
try {
|
||||
await sftp.listDir(path);
|
||||
} on SftpConnectionException {
|
||||
showError('Connection lost');
|
||||
await reconnect();
|
||||
}
|
||||
```
|
||||
|
||||
### Space Errors
|
||||
|
||||
```dart
|
||||
try {
|
||||
await sftp.upload(...);
|
||||
} on SftpNoSpaceException {
|
||||
showError('Disk full on remote server');
|
||||
}
|
||||
```
|
||||
|
||||
## Performance Optimizations
|
||||
|
||||
### Directory Caching
|
||||
|
||||
```dart
|
||||
class DirectoryCache {
|
||||
final Map<String, CachedDirectory> _cache = {};
|
||||
final Duration ttl = Duration(minutes: 5);
|
||||
|
||||
Future<List<SftpFile>> list(String path) async {
|
||||
final cached = _cache[path];
|
||||
if (cached != null && !cached.isExpired) {
|
||||
return cached.files;
|
||||
}
|
||||
|
||||
final files = await sftp.listDir(path);
|
||||
_cache[path] = CachedDirectory(files);
|
||||
return files;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Lazy Loading
|
||||
|
||||
For large directories (>1000 items):
|
||||
|
||||
```dart
|
||||
List<SftpFile> loadPage(String path, int page, int pageSize) {
|
||||
final all = cache[path] ?? [];
|
||||
final start = page * pageSize;
|
||||
final end = start + pageSize;
|
||||
return all.sublist(start, end.clamp(0, all.length));
|
||||
}
|
||||
```
|
||||
|
||||
### Pagination
|
||||
|
||||
```dart
|
||||
class PaginatedDirectory {
|
||||
static const pageSize = 100;
|
||||
|
||||
Future<List<SftpFile>> getPage(int page) async {
|
||||
final offset = page * pageSize;
|
||||
return await sftp.listDir(
|
||||
path,
|
||||
offset: offset,
|
||||
limit: pageSize,
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
299
docs/src/content/docs/principles/ssh.md
Normal file
299
docs/src/content/docs/principles/ssh.md
Normal file
@@ -0,0 +1,299 @@
|
||||
---
|
||||
title: SSH Connection
|
||||
description: How SSH connections are established and managed
|
||||
---
|
||||
|
||||
Understanding SSH connections in Flutter Server Box.
|
||||
|
||||
## Connection Flow
|
||||
|
||||
```
|
||||
User Input → Spi Config → genClient() → SSH Client → Session
|
||||
```
|
||||
|
||||
### Step 1: Configuration
|
||||
|
||||
The `Spi` (Server Parameter Info) model contains:
|
||||
|
||||
```dart
|
||||
class Spi {
|
||||
String name; // Server name
|
||||
String ip; // IP address
|
||||
int port; // SSH port (default 22)
|
||||
String user; // Username
|
||||
String? pwd; // Password (encrypted)
|
||||
String? keyId; // SSH key ID
|
||||
String? jumpId; // Jump server ID
|
||||
String? alterUrl; // Alternative URL
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2: Client Generation
|
||||
|
||||
`genClient(spi)` creates SSH client:
|
||||
|
||||
```dart
|
||||
Future<SSHClient> genClient(Spi spi) async {
|
||||
// 1. Establish socket
|
||||
final socket = await connect(spi.ip, spi.port);
|
||||
|
||||
// 2. Try alternative URL if failed
|
||||
if (socket == null && spi.alterUrl != null) {
|
||||
socket = await connect(spi.alterUrl, spi.port);
|
||||
}
|
||||
|
||||
// 3. Authenticate
|
||||
final client = SSHClient(
|
||||
socket: socket,
|
||||
username: spi.user,
|
||||
onPasswordRequest: () => spi.pwd,
|
||||
onIdentityRequest: () => loadKey(spi.keyId),
|
||||
);
|
||||
|
||||
// 4. Verify host key
|
||||
await verifyHostKey(client, spi);
|
||||
|
||||
return client;
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3: Jump Server (if configured)
|
||||
|
||||
For jump servers, recursive connection:
|
||||
|
||||
```dart
|
||||
if (spi.jumpId != null) {
|
||||
final jumpClient = await genClient(getJumpSpi(spi.jumpId));
|
||||
final forwarded = await jumpClient.forwardLocal(
|
||||
spi.ip,
|
||||
spi.port,
|
||||
);
|
||||
// Connect through forwarded socket
|
||||
}
|
||||
```
|
||||
|
||||
## Authentication Methods
|
||||
|
||||
### Password Authentication
|
||||
|
||||
```dart
|
||||
onPasswordRequest: () => spi.pwd
|
||||
```
|
||||
|
||||
- Password stored encrypted in Hive
|
||||
- Decrypted on connection
|
||||
- Sent to server for verification
|
||||
|
||||
### Private Key Authentication
|
||||
|
||||
```dart
|
||||
onIdentityRequest: () async {
|
||||
final key = await KeyStore.get(spi.keyId);
|
||||
return decyptPem(key.pem, key.password);
|
||||
}
|
||||
```
|
||||
|
||||
**Key Loading Process:**
|
||||
1. Retrieve encrypted key from `KeyStore`
|
||||
2. Decrypt password (biometric/prompt)
|
||||
3. Parse PEM format
|
||||
4. Standardize line endings (LF)
|
||||
5. Return for authentication
|
||||
|
||||
### Keyboard-Interactive
|
||||
|
||||
```dart
|
||||
onUserInfoRequest: (instructions) async {
|
||||
// Handle challenge-response
|
||||
return responses;
|
||||
}
|
||||
```
|
||||
|
||||
Supports:
|
||||
- Password authentication
|
||||
- OTP tokens
|
||||
- Two-factor authentication
|
||||
|
||||
## Host Key Verification
|
||||
|
||||
### Why Verify Host Keys?
|
||||
|
||||
Prevents **Man-in-the-Middle (MITM)** attacks by ensuring you're connecting to the same server.
|
||||
|
||||
### Storage Format
|
||||
|
||||
```
|
||||
{spi.id}::{keyType}
|
||||
```
|
||||
|
||||
Example:
|
||||
```
|
||||
my-server::ssh-ed25519
|
||||
my-server::ecdsa-sha2-nistp256
|
||||
```
|
||||
|
||||
### Fingerprint Formats
|
||||
|
||||
**MD5 Hex:**
|
||||
```
|
||||
aa:bb:cc:dd:ee:ff:00:11:22:33:44:55:66:77:88:99
|
||||
```
|
||||
|
||||
**Base64:**
|
||||
```
|
||||
SHA256:AbCdEf1234567890...=
|
||||
```
|
||||
|
||||
### Verification Flow
|
||||
|
||||
```dart
|
||||
Future<void> verifyHostKey(SSHClient client, Spi spi) async {
|
||||
final key = await client.hostKey;
|
||||
final fingerprint = md5Hex(key); // or base64
|
||||
|
||||
final stored = SettingStore.sshKnownHostsFingerprints
|
||||
['$keyId::$keyType'];
|
||||
|
||||
if (stored == null) {
|
||||
// New host - prompt user
|
||||
final trust = await promptUser(
|
||||
'Unknown host',
|
||||
'Fingerprint: $fingerprint',
|
||||
);
|
||||
if (trust) {
|
||||
SettingStore.sshKnownHostsFingerprints
|
||||
['$keyId::$keyType'] = fingerprint;
|
||||
}
|
||||
} else if (stored != fingerprint) {
|
||||
// Changed - warn user
|
||||
await warnUser(
|
||||
'Host key changed!',
|
||||
'Possible MITM attack',
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Session Management
|
||||
|
||||
### Connection Pooling
|
||||
|
||||
Active clients maintained in `ServerProvider`:
|
||||
|
||||
```dart
|
||||
class ServerProvider {
|
||||
final Map<String, SSHClient> _clients = {};
|
||||
|
||||
SSHClient getClient(String spiId) {
|
||||
return _clients[spiId] ??= connect(spiId);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Keep-Alive
|
||||
|
||||
Maintain connection during inactivity:
|
||||
|
||||
```dart
|
||||
Timer.periodic(
|
||||
Duration(seconds: 30),
|
||||
(_) => client.sendKeepAlive(),
|
||||
);
|
||||
```
|
||||
|
||||
### Auto-Reconnect
|
||||
|
||||
On connection loss:
|
||||
|
||||
```dart
|
||||
client.onError.listen((error) async {
|
||||
await Future.delayed(Duration(seconds: 5));
|
||||
reconnect();
|
||||
});
|
||||
```
|
||||
|
||||
## Connection Lifecycle
|
||||
|
||||
```
|
||||
┌─────────────┐
|
||||
│ Initial │
|
||||
└──────┬──────┘
|
||||
│ connect()
|
||||
↓
|
||||
┌─────────────┐
|
||||
│ Connecting │ ←──┐
|
||||
└──────┬──────┘ │
|
||||
│ success │
|
||||
↓ │ fail (retry)
|
||||
┌─────────────┐ │
|
||||
│ Connected │───┘
|
||||
└──────┬──────┘
|
||||
│
|
||||
↓
|
||||
┌─────────────┐
|
||||
│ Active │ ──→ Send commands
|
||||
└──────┬──────┘
|
||||
│
|
||||
↓ (error/disconnect)
|
||||
┌─────────────┐
|
||||
│ Disconnected│
|
||||
└─────────────┘
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Connection Timeout
|
||||
|
||||
```dart
|
||||
try {
|
||||
await client.connect().timeout(
|
||||
Duration(seconds: 30),
|
||||
);
|
||||
} on TimeoutException {
|
||||
throw ConnectionException('Connection timeout');
|
||||
}
|
||||
```
|
||||
|
||||
### Authentication Failure
|
||||
|
||||
```dart
|
||||
onAuthFail: (error) {
|
||||
if (error.contains('password')) {
|
||||
return 'Invalid password';
|
||||
} else if (error.contains('key')) {
|
||||
return 'Invalid SSH key';
|
||||
}
|
||||
return 'Authentication failed';
|
||||
}
|
||||
```
|
||||
|
||||
### Host Key Mismatch
|
||||
|
||||
```dart
|
||||
onHostKeyMismatch: (stored, current) {
|
||||
showSecurityWarning(
|
||||
'Host key has changed!',
|
||||
'Possible MITM attack',
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
### Connection Reuse
|
||||
|
||||
- Reuse clients across features
|
||||
- Don't disconnect/reconnect unnecessarily
|
||||
- Pool connections for concurrent operations
|
||||
|
||||
### Optimal Settings
|
||||
|
||||
- **Timeout**: 30 seconds (adjustable)
|
||||
- **Keep-alive**: Every 30 seconds
|
||||
- **Retry delay**: 5 seconds
|
||||
|
||||
### Network Efficiency
|
||||
|
||||
- Single connection for multiple operations
|
||||
- Pipeline commands when possible
|
||||
- Avoid opening multiple connections
|
||||
405
docs/src/content/docs/principles/state.md
Normal file
405
docs/src/content/docs/principles/state.md
Normal file
@@ -0,0 +1,405 @@
|
||||
---
|
||||
title: State Management
|
||||
description: How state is managed with Riverpod
|
||||
---
|
||||
|
||||
Understanding the state management architecture in Flutter Server Box.
|
||||
|
||||
## Why Riverpod?
|
||||
|
||||
**Key Benefits:**
|
||||
- **Compile-time safety**: Catch errors at compile time
|
||||
- **No BuildContext needed**: Access state anywhere
|
||||
- **Easy testing**: Simple to test providers in isolation
|
||||
- **Code generation**: Less boilerplate, type-safe
|
||||
|
||||
## Provider Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ UI Layer (Widgets) │
|
||||
│ - ConsumerWidget / ConsumerStatefulWidget │
|
||||
│ - ref.watch() / ref.read() │
|
||||
└─────────────────────────────────────────────┘
|
||||
↓ watches
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ Provider Layer │
|
||||
│ - @riverpod annotations │
|
||||
│ - Generated *.g.dart files │
|
||||
└─────────────────────────────────────────────┘
|
||||
↓ uses
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ Service / Store Layer │
|
||||
│ - Business logic │
|
||||
│ - Data access │
|
||||
└─────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Provider Types Used
|
||||
|
||||
### 1. StateProvider (Simple State)
|
||||
|
||||
For simple, observable state:
|
||||
|
||||
```dart
|
||||
@riverpod
|
||||
class ThemeNotifier extends _$ThemeNotifier {
|
||||
@override
|
||||
ThemeMode build() {
|
||||
// Load from settings
|
||||
return SettingStore.themeMode;
|
||||
}
|
||||
|
||||
void setTheme(ThemeMode mode) {
|
||||
state = mode;
|
||||
SettingStore.themeMode = mode; // Persist
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
```dart
|
||||
class MyWidget extends ConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final theme = ref.watch(themeNotifierProvider);
|
||||
return Text('Theme: $theme');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. AsyncNotifierProvider (Async State)
|
||||
|
||||
For data that loads asynchronously:
|
||||
|
||||
```dart
|
||||
@riverpod
|
||||
class ServerStatus extends _$ServerStatus {
|
||||
@override
|
||||
Future<StatusModel> build(Server server) async {
|
||||
// Initial load
|
||||
return await fetchStatus(server);
|
||||
}
|
||||
|
||||
Future<void> refresh() async {
|
||||
state = const AsyncValue.loading();
|
||||
state = await AsyncValue.guard(() async {
|
||||
return await fetchStatus(server);
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
```dart
|
||||
final status = ref.watch(serverStatusProvider(server));
|
||||
|
||||
status.when(
|
||||
data: (data) => StatusWidget(data),
|
||||
loading: () => LoadingWidget(),
|
||||
error: (error, stack) => ErrorWidget(error),
|
||||
)
|
||||
```
|
||||
|
||||
### 3. StreamProvider (Real-time Data)
|
||||
|
||||
For continuous data streams:
|
||||
|
||||
```dart
|
||||
@riverpod
|
||||
Stream<CpuUsage> cpuUsage(CpuUsageRef ref, Server server) {
|
||||
final client = ref.watch(sshClientProvider(server));
|
||||
final stream = client.monitorCpu();
|
||||
|
||||
// Auto-dispose when not watched
|
||||
ref.onDispose(() {
|
||||
client.stopMonitoring();
|
||||
});
|
||||
|
||||
return stream;
|
||||
}
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
```dart
|
||||
final cpu = ref.watch(cpuUsageProvider(server));
|
||||
|
||||
cpu.when(
|
||||
data: (usage) => CpuChart(usage),
|
||||
loading: () => CircularProgressIndicator(),
|
||||
error: (error, stack) => ErrorWidget(error),
|
||||
)
|
||||
```
|
||||
|
||||
### 4. Family Providers (Parameterized)
|
||||
|
||||
Providers that accept parameters:
|
||||
|
||||
```dart
|
||||
@riverpod
|
||||
Future<List<Container>> containers(ContainersRef ref, Server server) async {
|
||||
final client = await ref.watch(sshClientProvider(server).future);
|
||||
return await client.listContainers();
|
||||
}
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
```dart
|
||||
final containers = ref.watch(containersProvider(server));
|
||||
|
||||
// Different servers = different cached states
|
||||
final containers2 = ref.watch(containersProvider(server2));
|
||||
```
|
||||
|
||||
## State Update Patterns
|
||||
|
||||
### Direct State Update
|
||||
|
||||
```dart
|
||||
ref.read(settingsProvider.notifier).updateTheme(darkMode);
|
||||
```
|
||||
|
||||
### Computed State
|
||||
|
||||
```dart
|
||||
@riverpod
|
||||
int totalServers(TotalServersRef ref) {
|
||||
final servers = ref.watch(serversProvider);
|
||||
return servers.length;
|
||||
}
|
||||
```
|
||||
|
||||
### Derived State
|
||||
|
||||
```dart
|
||||
@riverpod
|
||||
List<Server> onlineServers(OnlineServersRef ref) {
|
||||
final all = ref.watch(serversProvider);
|
||||
return all.where((s) => s.isOnline).toList();
|
||||
}
|
||||
```
|
||||
|
||||
## Server-Specific State
|
||||
|
||||
### Per-Server Providers
|
||||
|
||||
Each server has isolated state:
|
||||
|
||||
```dart
|
||||
@riverpod
|
||||
class ServerProvider extends _$ServerProvider {
|
||||
@override
|
||||
ServerState build(Server server) {
|
||||
return ServerState.disconnected();
|
||||
}
|
||||
|
||||
Future<void> connect() async {
|
||||
state = ServerState.connecting();
|
||||
try {
|
||||
final client = await genClient(server.spi);
|
||||
state = ServerState.connected(client);
|
||||
} catch (e) {
|
||||
state = ServerState.error(e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Provider Keys
|
||||
|
||||
```dart
|
||||
// Unique provider per server
|
||||
@riverpod
|
||||
ServerStatus serverStatus(ServerStatusRef ref, Server server) {
|
||||
// server.id used as key
|
||||
}
|
||||
```
|
||||
|
||||
## Reactive Patterns
|
||||
|
||||
### Auto-Refresh
|
||||
|
||||
```dart
|
||||
@riverpod
|
||||
class AutoRefreshServerStatus extends _$AutoRefreshServerStatus {
|
||||
Timer? _timer;
|
||||
|
||||
@override
|
||||
Future<StatusModel> build(Server server) async {
|
||||
// Start timer
|
||||
_timer = Timer.periodic(Duration(seconds: 5), (_) {
|
||||
refresh();
|
||||
});
|
||||
|
||||
ref.onDispose(() {
|
||||
_timer?.cancel();
|
||||
});
|
||||
|
||||
return await fetchStatus(server);
|
||||
}
|
||||
|
||||
Future<void> refresh() async {
|
||||
state = const AsyncValue.loading();
|
||||
state = await AsyncValue.guard(() => fetchStatus(server));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Multi-Provider Dependencies
|
||||
|
||||
```dart
|
||||
@riverpod
|
||||
Future<SystemInfo> systemInfo(SystemInfoRef ref, Server server) async {
|
||||
// Wait for SSH client first
|
||||
final client = await ref.watch(sshClientProvider(server).future);
|
||||
|
||||
// Then fetch system info
|
||||
return await client.getSystemInfo();
|
||||
}
|
||||
```
|
||||
|
||||
## State Persistence
|
||||
|
||||
### Hive Integration
|
||||
|
||||
```dart
|
||||
@riverpod
|
||||
class ServerStoreNotifier extends _$ServerStoreNotifier {
|
||||
@override
|
||||
List<Server> build() {
|
||||
// Load from Hive
|
||||
return Hive.box<Server>('servers').values.toList();
|
||||
}
|
||||
|
||||
void addServer(Server server) {
|
||||
state = [...state, server];
|
||||
// Persist to Hive
|
||||
Hive.box<Server>('servers').put(server.id, server);
|
||||
}
|
||||
|
||||
void removeServer(String id) {
|
||||
state = state.where((s) => s.id != id).toList();
|
||||
// Remove from Hive
|
||||
Hive.box<Server>('servers').delete(id);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Error States
|
||||
|
||||
```dart
|
||||
@riverpod
|
||||
class ConnectionManager extends _$ConnectionManager {
|
||||
@override
|
||||
ConnectionState build() {
|
||||
return ConnectionState.idle();
|
||||
}
|
||||
|
||||
Future<void> connect(Server server) async {
|
||||
state = ConnectionState.connecting();
|
||||
try {
|
||||
final client = await genClient(server.spi);
|
||||
state = ConnectionState.connected(client);
|
||||
} on SocketException catch (e) {
|
||||
state = ConnectionState.error('Network error: $e');
|
||||
} on AuthenticationException catch (e) {
|
||||
state = ConnectionState.error('Auth failed: $e');
|
||||
} catch (e) {
|
||||
state = ConnectionState.error('Unknown error: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Error Recovery
|
||||
|
||||
```dart
|
||||
@riverpod
|
||||
class ResilientFetcher extends _$ResilientFetcher {
|
||||
int _retryCount = 0;
|
||||
|
||||
@override
|
||||
Future<Data> build(Server server) async {
|
||||
return await _fetchWithRetry();
|
||||
}
|
||||
|
||||
Future<Data> _fetchWithRetry() async {
|
||||
try {
|
||||
return await fetchData(server);
|
||||
} catch (e) {
|
||||
if (_retryCount < 3) {
|
||||
_retryCount++;
|
||||
await Future.delayed(Duration(seconds: 2));
|
||||
return await _fetchWithRetry();
|
||||
}
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Performance Optimizations
|
||||
|
||||
### Provider Keep-Alive
|
||||
|
||||
```dart
|
||||
@Riverpod(keepAlive: true) // Don't dispose when no listeners
|
||||
class GlobalSettings extends _$GlobalSettings {
|
||||
@override
|
||||
Settings build() {
|
||||
return Settings.defaults();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Selective Watching
|
||||
|
||||
```dart
|
||||
// Watch only specific part of state
|
||||
final name = ref.watch(serverProvider.select((s) => s.name));
|
||||
```
|
||||
|
||||
### Provider Caching
|
||||
|
||||
Family providers cache results per parameter:
|
||||
|
||||
```dart
|
||||
// Cached per server ID
|
||||
final status1 = ref.watch(serverStatusProvider(server1));
|
||||
final status2 = ref.watch(serverStatusProvider(server2));
|
||||
// Different states, both cached
|
||||
```
|
||||
|
||||
## Testing with Riverpod
|
||||
|
||||
### Provider Container
|
||||
|
||||
```dart
|
||||
test('fetch server status', () async {
|
||||
final container = ProviderContainer();
|
||||
addTearDown(container.dispose);
|
||||
|
||||
// Override provider
|
||||
container.overrideFactory(
|
||||
sshClientProvider,
|
||||
(ref, server) => MockSshClient(),
|
||||
);
|
||||
|
||||
final status = await container.read(
|
||||
serverStatusProvider(testServer).future,
|
||||
);
|
||||
|
||||
expect(status, isA<StatusModel>());
|
||||
});
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Co-locate providers**: Place near consuming widgets
|
||||
2. **Use code generation**: Always use `@riverpod`
|
||||
3. **Keep providers focused**: Single responsibility
|
||||
4. **Handle loading states**: Always handle AsyncValue states
|
||||
5. **Dispose resources**: Use `ref.onDispose()` for cleanup
|
||||
6. **Avoid deep provider trees**: Keep provider graph flat
|
||||
343
docs/src/content/docs/principles/terminal.md
Normal file
343
docs/src/content/docs/principles/terminal.md
Normal file
@@ -0,0 +1,343 @@
|
||||
---
|
||||
title: Terminal Implementation
|
||||
description: How the SSH terminal works internally
|
||||
---
|
||||
|
||||
The SSH terminal is one of the most complex features, built on a custom xterm.dart fork.
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ Terminal UI Layer │
|
||||
│ - Tab management │
|
||||
│ - Virtual keyboard │
|
||||
│ - Text selection │
|
||||
└─────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ xterm.dart Emulator │
|
||||
│ - PTY (Pseudo Terminal) │
|
||||
│ - VT100/ANSI emulation │
|
||||
│ - Rendering engine │
|
||||
└─────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ SSH Client Layer │
|
||||
│ - SSH session │
|
||||
│ - Channel management │
|
||||
│ - Data streaming │
|
||||
└─────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ Remote Server │
|
||||
│ - Shell process │
|
||||
│ - Command execution │
|
||||
└─────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Terminal Session Lifecycle
|
||||
|
||||
### 1. Session Creation
|
||||
|
||||
```dart
|
||||
Future<TerminalSession> createSession(Spi spi) async {
|
||||
// 1. Get SSH client
|
||||
final client = await genClient(spi);
|
||||
|
||||
// 2. Create PTY
|
||||
final pty = await client.openPty(
|
||||
term: 'xterm-256color',
|
||||
cols: 80,
|
||||
rows: 24,
|
||||
);
|
||||
|
||||
// 3. Initialize terminal emulator
|
||||
final terminal = Terminal(
|
||||
backend: PtyBackend(pty),
|
||||
);
|
||||
|
||||
// 4. Setup resize handler
|
||||
terminal.onResize.listen((size) {
|
||||
pty.resize(size.cols, size.rows);
|
||||
});
|
||||
|
||||
return TerminalSession(
|
||||
terminal: terminal,
|
||||
pty: pty,
|
||||
client: client,
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Terminal Emulation
|
||||
|
||||
The xterm.dart fork provides:
|
||||
|
||||
**VT100/ANSI Emulation:**
|
||||
- Cursor movement
|
||||
- Colors (256-color support)
|
||||
- Text attributes (bold, underline, etc.)
|
||||
- Scrolling regions
|
||||
- Alternate screen buffer
|
||||
|
||||
**Rendering:**
|
||||
- Line-based rendering
|
||||
- Bidirectional text support
|
||||
- Unicode/emoji support
|
||||
- Optimized redraws
|
||||
|
||||
### 3. Data Flow
|
||||
|
||||
```
|
||||
User Input
|
||||
↓
|
||||
Virtual Keyboard / Physical Keyboard
|
||||
↓
|
||||
Terminal Emulator (key → escape sequence)
|
||||
↓
|
||||
SSH Channel (send)
|
||||
↓
|
||||
Remote PTY
|
||||
↓
|
||||
Remote Shell
|
||||
↓
|
||||
Command Output
|
||||
↓
|
||||
SSH Channel (receive)
|
||||
↓
|
||||
Terminal Emulator (parse ANSI codes)
|
||||
↓
|
||||
Render to Screen
|
||||
```
|
||||
|
||||
## Multi-Tab System
|
||||
|
||||
### Tab Management
|
||||
|
||||
```dart
|
||||
class TerminalTabs {
|
||||
final Map<String, TabData> _tabs = {};
|
||||
String? _activeTabId;
|
||||
|
||||
void createTab(Server server) {
|
||||
final id = _generateTabId(server);
|
||||
_tabs[id] = TabData(
|
||||
id: id,
|
||||
name: _generateTabName(server),
|
||||
session: createSession(server),
|
||||
);
|
||||
_activeTabId = id;
|
||||
}
|
||||
|
||||
String _generateTabName(Server server) {
|
||||
final count = _tabs.values
|
||||
.where((t) => t.name.startsWith(server.name))
|
||||
.length;
|
||||
return count == 0 ? server.name : '${server.name}($count)';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Session Persistence
|
||||
|
||||
Tabs maintain state across navigation:
|
||||
|
||||
- SSH connection kept alive
|
||||
- Terminal state preserved
|
||||
- Scroll buffer maintained
|
||||
- Input history retained
|
||||
|
||||
## Virtual Keyboard
|
||||
|
||||
### Platform-Specific Implementation
|
||||
|
||||
**iOS:**
|
||||
- UIView-based custom keyboard
|
||||
- Toggleable with keyboard button
|
||||
- Auto-show/hide based on focus
|
||||
|
||||
**Android:**
|
||||
- Custom input method
|
||||
- Integrated with system keyboard
|
||||
- Quick action buttons
|
||||
|
||||
### Keyboard Buttons
|
||||
|
||||
| Button | Action |
|
||||
|--------|--------|
|
||||
| **Toggle** | Show/hide system keyboard |
|
||||
| **Ctrl** | Send Ctrl modifier |
|
||||
| **Alt** | Send Alt modifier |
|
||||
| **SFTP** | Open current directory |
|
||||
| **Clipboard** | Copy/Paste context-aware |
|
||||
| **Snippets** | Execute snippet |
|
||||
|
||||
### Key Encoding
|
||||
|
||||
```dart
|
||||
String encodeKey(Key key) {
|
||||
switch (key) {
|
||||
case Key.enter:
|
||||
return '\r';
|
||||
case Key.tab:
|
||||
return '\t';
|
||||
case Key.escape:
|
||||
return '\x1b';
|
||||
case Key.ctrlC:
|
||||
return '\x03';
|
||||
// ... more keys
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Text Selection
|
||||
|
||||
### Selection Mode
|
||||
|
||||
1. **Long press**: Enter selection mode
|
||||
2. **Drag**: Extend selection
|
||||
3. **Release**: Copy to clipboard
|
||||
|
||||
### Selection Storage
|
||||
|
||||
```dart
|
||||
class TextSelection {
|
||||
final BufferRange range;
|
||||
final String text;
|
||||
|
||||
void copyToClipboard() {
|
||||
Clipboard.setData(ClipboardData(text: text));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Font and Dimensions
|
||||
|
||||
### Size Calculation
|
||||
|
||||
```dart
|
||||
class TerminalDimensions {
|
||||
static Size calculate(double fontSize, Size screenSize) {
|
||||
final charWidth = fontSize * 0.6; // Monospace aspect ratio
|
||||
final charHeight = fontSize * 1.2;
|
||||
|
||||
final cols = (screenSize.width / charWidth).floor();
|
||||
final rows = (screenSize.height / charHeight).floor();
|
||||
|
||||
return Size(cols.toDouble(), rows.toDouble());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Pinch-to-Zoom
|
||||
|
||||
```dart
|
||||
GestureDetector(
|
||||
onScaleStart: () => _baseFontSize = currentFontSize,
|
||||
onScaleUpdate: (details) {
|
||||
final newFontSize = _baseFontSize * details.scale;
|
||||
resize(newFontSize);
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
## Color Scheme
|
||||
|
||||
### ANSI Color Mapping
|
||||
|
||||
```dart
|
||||
const colorMap = {
|
||||
0: Color(0x000000), // Black
|
||||
1: Color(0x800000), // Red
|
||||
2: Color(0x008000), // Green
|
||||
3: Color(0x808000), // Yellow
|
||||
4: Color(0x000080), // Blue
|
||||
5: Color(0x800080), // Magenta
|
||||
6: Color(0x008080), // Cyan
|
||||
7: Color(0xC0C0C0), // White
|
||||
// ... 256-color palette
|
||||
};
|
||||
```
|
||||
|
||||
### Theme Support
|
||||
|
||||
- **Light**: Light background, dark text
|
||||
- **Dark**: Dark background, light text
|
||||
- **AMOLED**: Pure black background
|
||||
|
||||
## Performance Optimizations
|
||||
|
||||
### Rendering Optimizations
|
||||
|
||||
- **Dirty rectangle**: Only redraw changed regions
|
||||
- **Line caching**: Cache rendered lines
|
||||
- **Lazy scrolling**: Virtual scrolling for long buffers
|
||||
|
||||
### Data Optimizations
|
||||
|
||||
- **Batch updates**: Coalesce multiple writes
|
||||
- **Compression**: Compress scroll buffer
|
||||
- **Debouncing**: Debounce rapid inputs
|
||||
|
||||
## Clipboard Integration
|
||||
|
||||
### Copy Selection
|
||||
|
||||
```dart
|
||||
void copySelection() {
|
||||
final selected = terminal.getSelection();
|
||||
Clipboard.setData(ClipboardData(text: selected));
|
||||
}
|
||||
```
|
||||
|
||||
### Paste Clipboard
|
||||
|
||||
```dart
|
||||
Future<void> pasteClipboard() async {
|
||||
final data = await Clipboard.getData('text/plain');
|
||||
if (data?.text != null) {
|
||||
terminal.paste(data!.text!);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Context-Aware Button
|
||||
|
||||
- **Has selection**: Show "Copy"
|
||||
- **Has clipboard**: Show "Paste"
|
||||
- **Both**: Show primary action
|
||||
|
||||
## Special Features
|
||||
|
||||
### Snippet Execution
|
||||
|
||||
```dart
|
||||
void executeSnippet(Snippet snippet) {
|
||||
final formatted = formatSnippet(snippet);
|
||||
terminal.paste(formatted);
|
||||
terminal.paste('\r'); // Execute
|
||||
}
|
||||
```
|
||||
|
||||
### SFTP Quick Access
|
||||
|
||||
```dart
|
||||
void openSftp() async {
|
||||
final cwd = await terminal.getCurrentWorkingDirectory();
|
||||
Navigator.push(
|
||||
context,
|
||||
SftpPage(initialPath: cwd),
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Keep-Alive
|
||||
|
||||
```dart
|
||||
Timer.periodic(Duration(seconds: 30), (_) {
|
||||
if (terminal.isActive) {
|
||||
terminal.send('\x00'); // NUL - no-op keep-alive
|
||||
}
|
||||
});
|
||||
```
|
||||
51
docs/src/content/docs/quick-start.mdx
Normal file
51
docs/src/content/docs/quick-start.mdx
Normal file
@@ -0,0 +1,51 @@
|
||||
---
|
||||
title: Quick Start
|
||||
description: Get up and running with Flutter Server Box in minutes
|
||||
---
|
||||
|
||||
Follow this quick start guide to connect to your first server and start monitoring.
|
||||
|
||||
## Step 1: Add a Server
|
||||
|
||||
1. Open Flutter Server Box
|
||||
2. Tap the **+** button to add a new server
|
||||
3. Fill in the server information:
|
||||
- **Name**: A friendly name for your server
|
||||
- **Host**: IP address or domain name
|
||||
- **Port**: SSH port (default: 22)
|
||||
- **User**: SSH username
|
||||
- **Password or Key**: Authentication method
|
||||
|
||||
4. Tap **Save** to add the server
|
||||
|
||||
## Step 2: Connect and Monitor
|
||||
|
||||
1. Tap on your server card to connect
|
||||
2. The app will establish an SSH connection
|
||||
3. You'll see real-time status for:
|
||||
- CPU usage
|
||||
- Memory (RAM) and Swap
|
||||
- Disk usage
|
||||
- Network speed
|
||||
|
||||
## Step 3: Explore Features
|
||||
|
||||
Once connected, you can:
|
||||
|
||||
- **Open Terminal**: Tap the terminal button for full SSH access
|
||||
- **Browse Files**: Use SFTP to manage files
|
||||
- **Manage Containers**: View and control Docker containers
|
||||
- **View Processes**: Check running processes
|
||||
- **Run Snippets**: Execute saved commands
|
||||
|
||||
## Tips
|
||||
|
||||
- **Biometric Authentication**: Enable Face ID / Touch ID / Fingerprint for quick access (mobile)
|
||||
- **Home Screen Widgets**: Add server status widgets to your home screen (iOS/Android)
|
||||
- **Background Running**: Keep connections alive in the background (Android)
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Explore the [Features](/features/) section for detailed guides
|
||||
- Configure [SSH Keys](/configuration/ssh-keys/) for passwordless authentication
|
||||
- Customize your experience in [Settings](/configuration/appearance/)
|
||||
7
docs/src/styles/custom.css
Normal file
7
docs/src/styles/custom.css
Normal file
@@ -0,0 +1,7 @@
|
||||
/* Flutter Server Box Custom Styles */
|
||||
|
||||
:root {
|
||||
--sl-color-accent: #02569b;
|
||||
--sl-color-accent-low: #02569b15;
|
||||
--starlight-cards--border: var(--sl-color-accent-low);
|
||||
}
|
||||
Reference in New Issue
Block a user