Files
sshclient/core/snippets.py
T
Your Name 2b29a5cf48 feat: command snippets + UI test coverage for new features
- core/snippets.py: SnippetManager with 20 prebuilt sysadmin commands,
  CRUD + reorder, persisted to ~/.sshclient/snippets.json
- ui/snippet_dialog.py: SnippetDialog (add/edit) + SnippetManagerDialog
  (table view with add/edit/delete/move up/down)
- ui/terminal_panel.py: snippet combo box in toolbar, send_command()
  method to inject commands into interactive shell, snippet manager button
- README.md: updated feature table, usage section, project structure
- test_ui.py: added [2/4] checks for sparkline/snippets/theme,
  added [4/4] theme toggle verification
2026-07-28 23:03:37 +08:00

87 lines
3.2 KiB
Python

"""
命令片段管理:保存常用命令,快速发送到终端。
持久化到 ~/.sshclient/snippets.json
"""
import json
from pathlib import Path
from typing import List, Optional
CONFIG_DIR = Path.home() / ".sshclient"
SNIPPETS_FILE = CONFIG_DIR / "snippets.json"
# 预置片段(首次运行时自动添加)
DEFAULT_SNIPPETS = [
{"name": "系统信息", "cmd": "uname -a && cat /etc/os-release"},
{"name": "磁盘使用", "cmd": "df -h"},
{"name": "内存使用", "cmd": "free -h"},
{"name": "CPU 信息", "cmd": "lscpu | head -20"},
{"name": "监听端口", "cmd": "ss -tlnp"},
{"name": "所有连接", "cmd": "ss -tnp"},
{"name": "进程 TOP10", "cmd": "ps aux --sort=-%cpu | head -11"},
{"name": "内存 TOP10", "cmd": "ps aux --sort=-%mem | head -11"},
{"name": "最近登录", "cmd": "last -10"},
{"name": "系统日志", "cmd": "journalctl -n 50 --no-pager"},
{"name": "DNS 解析", "cmd": "dig +short"},
{"name": "网络路由", "cmd": "ip route show"},
{"name": "网卡信息", "cmd": "ip addr show"},
{"name": "防火墙状态", "cmd": "iptables -L -n --line-numbers"},
{"name": "Docker 容器", "cmd": "docker ps -a"},
{"name": "Docker 日志", "cmd": "docker logs --tail 50"},
{"name": "systemctl 状态", "cmd": "systemctl status"},
{"name": "重启服务", "cmd": "systemctl restart"},
{"name": "定时任务", "cmd": "crontab -l"},
{"name": "大文件 TOP10", "cmd": "find / -type f -size +100M 2>/dev/null | head -10"},
]
class SnippetManager:
"""命令片段的增删改查 + 持久化"""
def __init__(self):
CONFIG_DIR.mkdir(parents=True, exist_ok=True)
self._snippets: List[dict] = []
self._load()
def _load(self):
if SNIPPETS_FILE.exists():
try:
with open(SNIPPETS_FILE, "r", encoding="utf-8") as f:
self._snippets = json.load(f)
except Exception:
self._snippets = []
if not self._snippets:
self._snippets = [dict(s) for s in DEFAULT_SNIPPETS]
self._save()
def _save(self):
with open(SNIPPETS_FILE, "w", encoding="utf-8") as f:
json.dump(self._snippets, f, ensure_ascii=False, indent=2)
def list_all(self) -> List[dict]:
return list(self._snippets)
def add(self, name: str, cmd: str) -> int:
"""添加片段,返回新索引"""
snippet = {"name": name.strip(), "cmd": cmd}
self._snippets.append(snippet)
self._save()
return len(self._snippets) - 1
def update(self, index: int, name: str, cmd: str):
if 0 <= index < len(self._snippets):
self._snippets[index] = {"name": name.strip(), "cmd": cmd}
self._save()
def remove(self, index: int):
if 0 <= index < len(self._snippets):
del self._snippets[index]
self._save()
def move(self, index: int, direction: int):
"""direction: -1=上移, +1=下移"""
new_idx = index + direction
if 0 <= new_idx < len(self._snippets):
self._snippets[index], self._snippets[new_idx] = \
self._snippets[new_idx], self._snippets[index]
self._save()