JKJan Kaminský/ code
RepositoriesAdmin

ServerWatch

ServerWatch hlídá aktuální stav serveru. CPU, RAM, Network využití

Python
‹› Code▤ READMEUpdated před 1 h
◇main.py
from pathlib import Path
import textwrap, zipfile, os, json

root = Path("/mnt/data/serverwatch")
root.mkdir(exist_ok=True)

serverwatch_py = r'''#!/usr/bin/env python3
import argparse
import os
import platform
import shutil
import socket
import subprocess
import time
from datetime import datetime

try:
    import psutil
    from rich.console import Console
    from rich.table import Table
    from rich.panel import Panel
    from rich.layout import Layout
    from rich.live import Live
    from rich.progress_bar import ProgressBar
    from rich.text import Text
    from rich.align import Align
except ImportError as e:
    print("ServerWatch: chybí závislosti. Spusť: sudo /opt/serverwatch/install.sh")
    raise SystemExit(1)

console = Console()
VERSION = "1.0.0"

SERVICES = [
    ("nginx", "Nginx"),
    ("php8.3-fpm", "PHP 8.3 FPM"),
    ("mariadb", "MariaDB"),
    ("mysql", "MySQL"),
    ("docker", "Docker"),
    ("ssh", "SSH"),
    ("sshd", "SSHD"),
]

def run(cmd):
    try:
        return subprocess.check_output(cmd, stderr=subprocess.DEVNULL, text=True, timeout=2).strip()
    except Exception:
        return ""

def service_state(name):
    try:
        p = subprocess.run(["systemctl", "is-active", name], capture_output=True, text=True, timeout=2)
        return p.stdout.strip() == "active"
    except Exception:
        return False

def existing_services():
    found = []
    seen_labels = set()
    for service, label in SERVICES:
        status = run(["systemctl", "show", service, "--property=LoadState", "--value"])
        if status and status != "not-found" and label not in seen_labels:
            found.append((service, label))
            seen_labels.add(label)
    return found

def human_bytes(n):
    n = float(n)
    for unit in ["B", "KB", "MB", "GB", "TB"]:
        if n < 1024:
            return f"{n:.1f} {unit}"
        n /= 1024
    return f"{n:.1f} PB"

def uptime():
    sec = int(time.time() - psutil.boot_time())
    days, sec = divmod(sec, 86400)
    hours, sec = divmod(sec, 3600)
    mins, _ = divmod(sec, 60)
    if days:
        return f"{days}d {hours:02}h {mins:02}m"
    return f"{hours}h {mins:02}m"

def temperature():
    try:
        temps = psutil.sensors_temperatures()
        vals = []
        for entries in temps.values():
            for e in entries:
                if e.current and 0 < e.current < 150:
                    vals.append(e.current)
        return f"{max(vals):.0f} °C" if vals else "N/A"
    except Exception:
        return "N/A"

def public_ip():
    # No external network call: report primary local/server IP.
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s.connect(("1.1.1.1", 80))
        ip = s.getsockname()[0]
        s.close()
        return ip
    except Exception:
        return socket.gethostbyname(socket.gethostname())

def metric_row(label, percent, detail):
    grid = Table.grid(expand=True)
    grid.add_column(width=8)
    grid.add_column(ratio=1)
    grid.add_column(width=24, justify="right")
    style = "green" if percent < 70 else ("yellow" if percent < 90 else "red")
    grid.add_row(
        f"[bold]{label}[/]",
        ProgressBar(total=100, completed=max(0, min(100, percent)), width=None, style="grey37", complete_style=style),
        f"[{style}]{percent:5.1f}%[/]  {detail}"
    )
    return grid

def snapshot(prev_net=None, prev_time=None):
    cpu = psutil.cpu_percent(interval=0.15)
    mem = psutil.virtual_memory()
    disk = psutil.disk_usage("/")
    swap = psutil.swap_memory()
    net = psutil.net_io_counters()
    now = time.time()
    rx = tx = 0.0
    if prev_net and prev_time:
        dt = max(now - prev_time, .001)
        rx = max(0, net.bytes_recv - prev_net.bytes_recv) / dt
        tx = max(0, net.bytes_sent - prev_net.bytes_sent) / dt
    return {
        "cpu": cpu, "mem": mem, "disk": disk, "swap": swap,
        "net": net, "rx": rx, "tx": tx, "time": now,
        "load": os.getloadavg() if hasattr(os, "getloadavg") else (0,0,0),
    }

def dashboard_render(data, services):
    layout = Layout()
    layout.split_column(
        Layout(name="header", size=3),
        Layout(name="metrics", size=10),
        Layout(name="middle", size=7),
        Layout(name="processes", ratio=1),
        Layout(name="footer", size=3),
    )

    host = socket.gethostname()
    header = Table.grid(expand=True)
    header.add_column()
    header.add_column(justify="right")
    header.add_row(
        f"[bold cyan]SERVERWATCH[/] [dim]v{VERSION}[/]  [bold]{host}[/]",
        f"[green]● ONLINE[/]   Uptime [bold]{uptime()}[/]"
    )
    layout["header"].update(Panel(header, border_style="cyan"))

    metrics = Table.grid(expand=True)
    metrics.add_row(metric_row("CPU", data["cpu"], f"{psutil.cpu_count()} threads"))
    metrics.add_row(metric_row("RAM", data["mem"].percent, f"{human_bytes(data['mem'].used)} / {human_bytes(data['mem'].total)}"))
    metrics.add_row(metric_row("DISK", data["disk"].percent, f"{human_bytes(data['disk'].used)} / {human_bytes(data['disk'].total)}"))
    metrics.add_row(metric_row("SWAP", data["swap"].percent, f"{human_bytes(data['swap'].used)} / {human_bytes(data['swap'].total)}"))
    layout["metrics"].update(Panel(metrics, title="Resources", border_style="blue"))

    mid = Table.grid(expand=True)
    mid.add_column(ratio=1)
    mid.add_column(ratio=1)
    load = data["load"]
    svc_text = []
    for name, label in services:
        ok = service_state(name)
        svc_text.append(f"[green]●[/] {label}" if ok else f"[red]●[/] {label}")
    if not svc_text:
        svc_text = ["[dim]No watched services detected[/]"]
    left = (
        f"[bold]LOAD[/]   {load[0]:.2f}   {load[1]:.2f}   {load[2]:.2f}\n"
        f"[bold]TEMP[/]   {temperature()}\n"
        f"[bold]NET ↓[/]  {human_bytes(data['rx'])}/s\n"
        f"[bold]NET ↑[/]  {human_bytes(data['tx'])}/s\n"
        f"[bold]IP[/]     {public_ip()}"
    )
    mid.add_row(left, "\n".join(svc_text[:5]))
    layout["middle"].update(Panel(mid, title="System / Services", border_style="magenta"))

    proc = Table(expand=True, box=None)
    proc.add_column("PID", width=9)
    proc.add_column("PROCESS", ratio=1)
    proc.add_column("CPU", justify="right", width=9)
    proc.add_column("RAM", justify="right", width=12)
    rows = []
    for p in psutil.process_iter(["pid", "name", "cpu_percent", "memory_info"]):
        try:
            info = p.info
            rows.append((info["cpu_percent"] or 0, info["pid"], info["name"] or "?", info["memory_info"].rss))
        except Exception:
            pass
    for cpu, pid, name, ram in sorted(rows, reverse=True)[:7]:
        proc.add_row(str(pid), name[:40], f"{cpu:.1f}%", human_bytes(ram))
    layout["processes"].update(Panel(proc, title="Top processes", border_style="green"))

    footer = Align.center("[bold]Q[/] Quit   [bold]D[/] Doctor   [bold]S[/] Services   [bold]P[/] Processes   [dim]Refresh 1s[/]")
    layout["footer"].update(Panel(footer, border_style="grey50"))
    return layout

def dashboard():
    services = existing_services()
    prev_net = psutil.net_io_counters()
    prev_time = time.time()
    console.clear()
    try:
        with Live(console=console, screen=True, refresh_per_second=4) as live:
            while True:
                data = snapshot(prev_net, prev_time)
                prev_net, prev_time = data["net"], data["time"]
                live.update(dashboard_render(data, services), refresh=True)
                time.sleep(1)
    except KeyboardInterrupt:
        pass

def show_info():
    mem = psutil.virtual_memory()
    disk = psutil.disk_usage("/")
    t = Table(title="ServerWatch · System info")
    t.add_column("Item", style="cyan")
    t.add_column("Value")
    rows = [
        ("Hostname", socket.gethostname()),
        ("OS", f"{platform.system()} {platform.release()}"),
        ("Architecture", platform.machine()),
        ("CPU", platform.processor() or run(["lscpu"])[:80] or "N/A"),
        ("CPU threads", str(psutil.cpu_count())),
        ("RAM", human_bytes(mem.total)),
        ("Disk /", human_bytes(disk.total)),
        ("IP", public_ip()),
        ("Uptime", uptime()),
        ("Python", platform.python_version()),
    ]
    for a,b in rows: t.add_row(a,b)
    console.print(t)

def show_services():
    t = Table(title="ServerWatch · Services")
    t.add_column("Service")
    t.add_column("State")
    svcs = existing_services()
    if not svcs:
        console.print("[yellow]Nebyly nalezeny sledované služby.[/]")
        return
    for name, label in svcs:
        ok = service_state(name)
        t.add_row(label, "[green]● running[/]" if ok else "[red]● stopped[/]")
    console.print(t)

def show_processes():
    t = Table(title="ServerWatch · Top processes")
    t.add_column("PID")
    t.add_column("Process")
    t.add_column("CPU", justify="right")
    t.add_column("RAM", justify="right")
    # prime CPU counters
    for p in psutil.process_iter():
        try: p.cpu_percent(None)
        except Exception: pass
    time.sleep(.25)
    rows = []
    for p in psutil.process_iter(["pid","name","memory_info"]):
        try:
            rows.append((p.cpu_percent(None), p.info["pid"], p.info["name"], p.info["memory_info"].rss))
        except Exception: pass
    for cpu,pid,name,ram in sorted(rows, reverse=True)[:20]:
        t.add_row(str(pid), (name or "?")[:50], f"{cpu:.1f}%", human_bytes(ram))
    console.print(t)

def doctor():
    checks = []
    cpu = psutil.cpu_percent(interval=.4)
    mem = psutil.virtual_memory().percent
    disk = psutil.disk_usage("/").percent
    checks.append(("CPU load", cpu < 90, f"{cpu:.1f}%"))
    checks.append(("RAM usage", mem < 90, f"{mem:.1f}%"))
    checks.append(("Disk usage", disk < 90, f"{disk:.1f}%"))
    for name, label in existing_services():
        checks.append((label, service_state(name), "running" if service_state(name) else "stopped"))
    failed = sum(1 for _, ok, _ in checks if not ok)
    score = max(0, round(100 * (len(checks)-failed) / max(1,len(checks))))
    console.print(Panel.fit(f"[bold cyan]SERVERWATCH SYSTEM DIAGNOSTICS[/]\nHealth score: [bold]{score}/100[/]"))
    for label, ok, detail in checks:
        console.print(f"{'[green]✓[/]' if ok else '[red]✗[/]'} {label}: {detail}")
    if shutil.which("apt"):
        upgradable = run(["bash","-lc","apt list --upgradable 2>/dev/null | tail -n +2 | wc -l"])
        if upgradable and upgradable != "0":
            console.print(f"[yellow]⚠[/] {upgradable} balíčků lze aktualizovat")

def simple():
    d = snapshot()
    console.print(
        f"[bold cyan]ServerWatch[/] {socket.gethostname()}  "
        f"CPU [bold]{d['cpu']:.1f}%[/]  RAM [bold]{d['mem'].percent:.1f}%[/]  "
        f"DISK [bold]{d['disk'].percent:.1f}%[/]  Uptime [bold]{uptime()}[/]"
    )

def main():
    parser = argparse.ArgumentParser(description="ServerWatch - lightweight Linux server monitoring CLI")
    parser.add_argument("command", nargs="?", choices=["doctor","services","processes","info"], help="Command")
    parser.add_argument("--simple", action="store_true", help="One-line summary")
    parser.add_argument("--version", action="version", version=f"ServerWatch {VERSION}")
    args = parser.parse_args()
    if args.simple: return simple()
    if args.command == "doctor": return doctor()
    if args.command == "services": return show_services()
    if args.command == "processes": return show_processes()
    if args.command == "info": return show_info()
    dashboard()

if __name__ == "__main__":
    main()
'''

install_sh = r'''#!/usr/bin/env bash
set -euo pipefail

if [ "${EUID}" -ne 0 ]; then
  echo "Spusť instalaci jako root: sudo ./install.sh"
  exit 1
fi

APP_DIR="/opt/serverwatch"
BIN="/usr/local/bin/serverwatch"

echo "== ServerWatch installer =="

if ! command -v python3 >/dev/null 2>&1; then
  apt-get update
  apt-get install -y python3 python3-venv
fi

mkdir -p "$APP_DIR"
cp serverwatch.py "$APP_DIR/serverwatch.py"
chmod 755 "$APP_DIR/serverwatch.py"

python3 -m venv "$APP_DIR/venv"
"$APP_DIR/venv/bin/pip" install --upgrade pip >/dev/null
"$APP_DIR/venv/bin/pip" install -r requirements.txt

cat > "$BIN" <<'EOF'
#!/usr/bin/env bash
exec /opt/serverwatch/venv/bin/python /opt/serverwatch/serverwatch.py "$@"
EOF

chmod 755 "$BIN"

echo
echo "✓ ServerWatch nainstalován."
echo "Spusť: serverwatch"
echo "Další: serverwatch doctor | services | processes | info | --simple"
'''

uninstall_sh = r'''#!/usr/bin/env bash
set -e
if [ "${EUID}" -ne 0 ]; then
  echo "Spusť jako root: sudo ./uninstall.sh"
  exit 1
fi
rm -f /usr/local/bin/serverwatch
rm -rf /opt/serverwatch
echo "ServerWatch byl odinstalován."
'''

readme = r'''# ServerWatch v1.0

Lehký interaktivní monitoring Linux VPS přímo v terminálu.

## Funkce
- živý fullscreen dashboard
- CPU, RAM, disk, swap
- load average, teplota (pokud ji VPS poskytuje)
- aktuální RX/TX síťový provoz
- uptime a IP
- automatická detekce běžných služeb (Nginx, PHP-FPM, MariaDB/MySQL, Docker, SSH)
- TOP procesy
- diagnostika `serverwatch doctor`
- jednorázový přehled `serverwatch --simple`

## Instalace

```bash
unzip serverwatch.zip
cd serverwatch
chmod +x install.sh
sudo ./install.sh
```

Potom odkudkoliv:

```bash
serverwatch
```

Další příkazy:

```bash
serverwatch --simple
serverwatch doctor
serverwatch services
serverwatch processes
serverwatch info
serverwatch --version
```

Fullscreen dashboard ukončíš `Ctrl+C`.

## Odinstalace

```bash
sudo ./uninstall.sh
```

## Poznámky
ServerWatch neodesílá telemetrii na externí server. Primární IP zjišťuje lokálně přes routing socket; nevykonává HTTP dotaz na externí "what is my IP" službu.

V1 je zaměřena na Debian/Ubuntu VPS se systemd. Teplota na virtuálních serverech často není hypervizorem zpřístupněna, takže může zobrazit N/A.
'''

(root/"serverwatch.py").write_text(serverwatch_py, encoding="utf-8")
(root/"install.sh").write_text(install_sh, encoding="utf-8")
(root/"uninstall.sh").write_text(uninstall_sh, encoding="utf-8")
(root/"requirements.txt").write_text("psutil>=5.9,<8\nrich>=13,<15\n", encoding="utf-8")
(root/"README.md").write_text(readme, encoding="utf-8")
os.chmod(root/"install.sh", 0o755)
os.chmod(root/"uninstall.sh", 0o755)
os.chmod(root/"serverwatch.py", 0o755)

zip_path = Path("/mnt/data/serverwatch-v1.0.zip")
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as z:
    for p in root.rglob("*"):
        if p.is_file():
            z.write(p, p.relative_to(root.parent))
print(zip_path)
README.md

ServerWatch

# 🖥️ ServerWatch

Lightweight real-time monitoring tool for Linux VPS servers.

ServerWatch poskytuje přehledný terminálový dashboard pro sledování
aktuálního stavu serveru bez potřeby webového rozhraní.

## ✨ Features

- ⚡ Real-time CPU monitoring
- 🧠 RAM & SWAP usage
- 💾 Disk usage
- 🌐 Network RX / TX
- 📊 System load
- ⏱️ Server uptime
- 🌡️ CPU temperature (if available)
- ⚙️ Service monitoring
- 🔍 Top processes
- 🩺 Built-in system diagnostics
- ❤️ Server health score
- 🖥️ Interactive terminal dashboard

## 📦 Installation

```bash
unzip serverwatch-v1.0.zip
cd serverwatch
chmod +x install.sh
sudo ./install.sh