Skip to content

Commit 2c64136

Browse files
EstrellaXDclaude
andcommitted
feat: rewrite backend from Python/FastAPI to Rust/Axum
Replace the entire Python backend (~1500 lines across 15 files) with an idiomatic Rust implementation using Axum, Tokio, and native crates. The JSON API contract is preserved exactly for the Vue 3 frontend. Backend changes: - Axum 0.8 web framework with tower-http CORS - russh for SSH collectors (Linux + UNAS), replacing asyncssh - bollard for Docker collector, replacing docker-py - reqwest for HTTP collectors (qBittorrent + UniFi) - Shared SshConnection abstraction eliminates Linux/UNAS duplication - tokio::sync::broadcast for WebSocket fan-out (serialize once) - Arc<AppState> via Axum State extractor (no module-level singletons) - Mock mode preserved for development/testing CI/CD: - Add GitHub Actions CI workflow (check, clippy, test, build) - Add Docker publish workflow (multi-arch to ghcr.io on version tags) Infrastructure: - Update Dockerfile to multi-stage Rust build - Add healthcheck to docker-compose.yml - Update .gitignore for Rust target directory Docs: - Update README, DEPLOY.md, CLAUDE.md for Rust backend and bun Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 58aebcd commit 2c64136

65 files changed

Lines changed: 6677 additions & 4921 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
env:
10+
CARGO_TERM_COLOR: always
11+
12+
jobs:
13+
check:
14+
name: Cargo Check
15+
runs-on: ubuntu-latest
16+
steps:
17+
- uses: actions/checkout@v4
18+
19+
- name: Install stable toolchain
20+
uses: dtolnay/rust-toolchain@stable
21+
22+
- name: Cache cargo registry and target
23+
uses: actions/cache@v4
24+
with:
25+
path: |
26+
~/.cargo/registry
27+
~/.cargo/git
28+
backend/target
29+
key: ${{ runner.os }}-cargo-check-${{ hashFiles('backend/Cargo.lock') }}
30+
restore-keys: |
31+
${{ runner.os }}-cargo-check-
32+
33+
- name: Run cargo check
34+
working-directory: backend/
35+
run: cargo check
36+
37+
clippy:
38+
name: Clippy
39+
runs-on: ubuntu-latest
40+
steps:
41+
- uses: actions/checkout@v4
42+
43+
- name: Install stable toolchain
44+
uses: dtolnay/rust-toolchain@stable
45+
with:
46+
components: clippy
47+
48+
- name: Cache cargo registry and target
49+
uses: actions/cache@v4
50+
with:
51+
path: |
52+
~/.cargo/registry
53+
~/.cargo/git
54+
backend/target
55+
key: ${{ runner.os }}-cargo-clippy-${{ hashFiles('backend/Cargo.lock') }}
56+
restore-keys: |
57+
${{ runner.os }}-cargo-clippy-
58+
59+
- name: Run clippy
60+
working-directory: backend/
61+
run: cargo clippy -- -D warnings
62+
63+
test:
64+
name: Tests
65+
runs-on: ubuntu-latest
66+
steps:
67+
- uses: actions/checkout@v4
68+
69+
- name: Install stable toolchain
70+
uses: dtolnay/rust-toolchain@stable
71+
72+
- name: Cache cargo registry and target
73+
uses: actions/cache@v4
74+
with:
75+
path: |
76+
~/.cargo/registry
77+
~/.cargo/git
78+
backend/target
79+
key: ${{ runner.os }}-cargo-test-${{ hashFiles('backend/Cargo.lock') }}
80+
restore-keys: |
81+
${{ runner.os }}-cargo-test-
82+
83+
- name: Run tests
84+
working-directory: backend/
85+
run: cargo test
86+
87+
build:
88+
name: Release Build
89+
runs-on: ubuntu-latest
90+
steps:
91+
- uses: actions/checkout@v4
92+
93+
- name: Install stable toolchain
94+
uses: dtolnay/rust-toolchain@stable
95+
96+
- name: Cache cargo registry and target
97+
uses: actions/cache@v4
98+
with:
99+
path: |
100+
~/.cargo/registry
101+
~/.cargo/git
102+
backend/target
103+
key: ${{ runner.os }}-cargo-build-${{ hashFiles('backend/Cargo.lock') }}
104+
restore-keys: |
105+
${{ runner.os }}-cargo-build-
106+
107+
- name: Build release
108+
working-directory: backend/
109+
run: cargo build --release
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
name: Build and Publish Docker Image
2+
3+
on:
4+
push:
5+
tags:
6+
- "v*"
7+
workflow_dispatch:
8+
9+
permissions:
10+
packages: write
11+
contents: read
12+
13+
jobs:
14+
build-and-push:
15+
runs-on: ubuntu-latest
16+
17+
steps:
18+
- name: Checkout repository
19+
uses: actions/checkout@v4
20+
21+
- name: Set up Docker Buildx
22+
uses: docker/setup-buildx-action@v3
23+
24+
- name: Log in to GitHub Container Registry
25+
uses: docker/login-action@v3
26+
with:
27+
registry: ghcr.io
28+
username: ${{ github.actor }}
29+
password: ${{ secrets.GITHUB_TOKEN }}
30+
31+
- name: Extract metadata for Docker
32+
id: meta
33+
uses: docker/metadata-action@v5
34+
with:
35+
images: ghcr.io/${{ github.repository_owner }}/server-monitor
36+
tags: |
37+
type=semver,pattern={{version}}
38+
type=raw,value=latest
39+
type=sha
40+
41+
- name: Build and push Docker image
42+
uses: docker/build-push-action@v6
43+
with:
44+
context: ./backend
45+
platforms: linux/amd64,linux/arm64
46+
push: true
47+
tags: ${{ steps.meta.outputs.tags }}
48+
labels: ${{ steps.meta.outputs.labels }}
49+
cache-from: type=gha
50+
cache-to: type=gha,mode=max

.gitignore

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
# Dependencies
22
node_modules/
3-
venv/
4-
.venv/
5-
__pycache__/
6-
*.pyc
3+
4+
# Rust build outputs
5+
backend/target/
76

87
# Build outputs
98
dist/
109
build/
11-
*.egg-info/
1210

1311
# IDE
1412
.idea/

CLAUDE.md

Lines changed: 58 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2,70 +2,81 @@
22

33
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
44

5-
## Developer Preferences
6-
7-
- Use `uv` instead of direct `python` commands (e.g., `uv run python` instead of `python`)
8-
95
## Project Overview
106

117
ServerMonitor is a homelab server monitoring dashboard with real-time metrics visualization.
128

139
**Tech Stack:**
1410
- Frontend: Vue 3 (Composition API) + TypeScript + TailwindCSS
15-
- Backend: FastAPI (Python) with WebSocket support
11+
- Backend: Rust (Axum + Tokio) with WebSocket support
1612
- Data: In-memory storage (no persistence)
1713

1814
**Monitored Systems:**
19-
- Linux servers via SSH
20-
- Docker containers
21-
- qBittorrent (Web API)
22-
- UniFi router
23-
- UNAS storage
15+
- Linux servers via SSH (russh)
16+
- Docker containers (bollard)
17+
- qBittorrent (Web API via reqwest)
18+
- UniFi router (API via reqwest)
19+
- UNAS storage (SSH via russh)
2420

2521
## Build and Development Commands
2622

2723
### Backend
2824
```bash
2925
cd backend
30-
python -m venv venv
31-
source venv/bin/activate # or `venv\Scripts\activate` on Windows
32-
pip install -r requirements.txt
33-
uvicorn app.main:app --reload --host 0.0.0.0 --port 8742
26+
cargo build --release # Build
27+
cargo run --release # Run
28+
cargo test # Tests
29+
cargo clippy -- -D warnings # Lint
30+
31+
# Mock mode (no real infrastructure needed)
32+
SERVERMONITOR_MOCK_MODE=true cargo run --release
3433
```
3534

3635
### Frontend
3736
```bash
3837
cd frontend
39-
npm install
40-
npm run dev # Development server
41-
npm run build # Production build
42-
npm run preview # Preview production build
38+
bun install
39+
bun run dev # Development server
40+
bun run build # Production build
41+
bun run preview # Preview production build
4342
```
4443

4544
### Full Stack (Docker)
4645
```bash
47-
docker-compose up --build
46+
docker compose up --build
4847
```
4948

5049
## Architecture
5150

5251
```
53-
backend/app/
54-
├── main.py # FastAPI entry, CORS, routers
55-
├── config.py # YAML config loader
56-
├── collectors/ # System-specific metric collectors
57-
│ ├── base.py # Abstract BaseCollector
58-
│ ├── linux.py # SSH-based Linux metrics
59-
│ ├── docker.py # Docker API collector
60-
│ ├── qbittorrent.py # qBit Web API
61-
│ ├── unifi.py # UniFi Controller API
62-
│ └── unas.py # NAS storage collector
52+
backend/src/
53+
├── main.rs # Tokio runtime, Axum router, graceful shutdown
54+
├── lib.rs # Module re-exports
55+
├── error.rs # CollectorError, AppError with IntoResponse
56+
├── config/
57+
│ ├── settings.rs # Environment variable settings
58+
│ ├── app_config.rs # YAML config loading
59+
│ └── system_configs.rs # Per-type config structs
60+
├── models/
61+
│ ├── status.rs # SystemStatus, SystemType enums
62+
│ ├── metrics.rs # All metric structs (Linux, Docker, etc.)
63+
│ ├── system_metrics.rs # SystemMetrics + MetricsPayload union
64+
│ └── messages.rs # MetricsUpdate WebSocket message
65+
├── collectors/
66+
│ ├── mod.rs # Collector trait + factory function
67+
│ ├── ssh_connection.rs # Shared SSH abstraction (Linux + UNAS)
68+
│ ├── linux.rs # SSH-based Linux metrics
69+
│ ├── docker.rs # Docker API via bollard
70+
│ ├── qbittorrent.rs # qBit Web API via reqwest
71+
│ ├── unifi.rs # UniFi API via reqwest
72+
│ ├── unas.rs # NAS storage via SSH
73+
│ └── mock.rs # Mock collectors for testing
6374
├── services/
64-
│ ├── collector_manager.py # Orchestrates all collectors
65-
│ └── metrics_store.py # In-memory metrics cache
75+
│ ├── metrics_store.rs # RwLock<HashMap> in-memory cache
76+
│ └── collector_manager.rs # Collection loop orchestration
6677
└── api/
67-
├── routes.py # REST endpoints
68-
└── websocket.py # Real-time streaming
78+
├── routes.rs # REST handlers + AppState
79+
└── websocket.rs # WebSocket broadcast via tokio::sync::broadcast
6980
7081
frontend/src/
7182
├── components/
@@ -78,23 +89,32 @@ frontend/src/
7889

7990
## Key Patterns
8091

81-
- **Collectors:** Each system type extends `BaseCollector` with `collect()` and `check_health()` methods
82-
- **Real-time:** Backend pushes metrics via WebSocket every 5-10 seconds
92+
- **Collectors:** Each system type implements the `Collector` trait with `collect()` and `close()` methods
93+
- **SSH Sharing:** `SshConnection` abstracts SSH for both Linux and UNAS collectors
94+
- **State:** `Arc<AppState>` passed through Axum `State` extractor (replaces Python module-level singletons)
95+
- **Real-time:** `tokio::sync::broadcast` for WebSocket fan-out (serialize once, not per-connection)
8396
- **Status:** Systems are `healthy`, `warning`, `critical`, or `offline`
8497
- **Theming:** Dark/Light mode via TailwindCSS `class` strategy
8598

8699
## Configuration
87100

88-
Systems are defined in `config.yaml`:
101+
Systems are defined in `backend/config.yaml`:
89102
```yaml
90103
poll_interval: 5
91104
systems:
92105
- id: linux-server-1
106+
name: "My Server"
93107
type: linux
94-
host: 192.168.1.100
95-
ssh_user: monitor
108+
config:
109+
host: 192.168.1.100
110+
username: monitor
96111
```
97112
113+
## CI/CD
114+
115+
- **CI:** `.github/workflows/ci.yml` -- cargo check, clippy, test, build on push/PR
116+
- **Docker Publish:** `.github/workflows/docker-publish.yml` -- multi-arch image to ghcr.io on version tags
117+
98118
## Planning Files
99119

100120
- `requirements.md` - Full requirements specification

0 commit comments

Comments
 (0)