feat: init pc-monitor project

- Client: Go-based Windows hardware monitoring (CPU, GPU, memory, disk, network, power)
- Server: Go + Gin + SQLite backend with REST API
- Frontend: Vue 3 + Element Plus dashboard
- Docker deployment support
- Windows service installation script
This commit is contained in:
672
2026-05-17 01:29:44 +08:00
commit 0e8c9f7bff
49 changed files with 3291 additions and 0 deletions

123
client/main.go Normal file
View File

@@ -0,0 +1,123 @@
package main
import (
"fmt"
"log"
"net"
"os"
"os/signal"
"runtime"
"syscall"
"time"
"pc-monitor-client/collector"
"pc-monitor-client/reporter"
)
func main() {
log.SetFlags(log.LstdFlags | log.Lshortfile)
log.Println("PC Monitor Client starting...")
// Load config
cfg, err := LoadConfig("config.yaml")
if err != nil {
log.Fatalf("Failed to load config: %v", err)
}
// Get hostname and IP
hostname, _ := os.Hostname()
ip := getLocalIP()
// Create reporter
r := reporter.NewReporter(cfg.Server.URL, cfg.Server.Token)
// Register device
log.Printf("Registering device: %s (%s)", hostname, ip)
deviceID, err := r.Register(hostname, runtime.GOOS, ip)
if err != nil {
log.Printf("Warning: Failed to register: %v", err)
log.Println("Will retry on next report...")
} else {
log.Printf("Device registered with ID: %s", deviceID)
}
// Create ticker for collection
collectTicker := time.NewTicker(cfg.Collect.Interval)
defer collectTicker.Stop()
reportTicker := time.NewTicker(cfg.Report.Interval)
defer reportTicker.Stop()
// Create channel for graceful shutdown
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
// Last collected metrics
var lastMetrics *collector.Metrics
log.Printf("Collecting metrics every %v", cfg.Collect.Interval)
log.Printf("Reporting metrics every %v", cfg.Report.Interval)
for {
select {
case <-collectTicker.C:
metrics, err := collector.CollectAll()
if err != nil {
log.Printf("Error collecting metrics: %v", err)
continue
}
metrics.DeviceID = deviceID
lastMetrics = metrics
log.Printf("Metrics collected: CPU=%.1f%%, Memory=%.1f%%",
metrics.CPU.Usage, metrics.Memory.Usage)
case <-reportTicker.C:
if lastMetrics == nil {
log.Println("No metrics to report yet")
continue
}
if deviceID == "" {
// Try to register again
deviceID, err = r.Register(hostname, runtime.GOOS, ip)
if err != nil {
log.Printf("Failed to register: %v", err)
continue
}
log.Printf("Device registered with ID: %s", deviceID)
}
if err := r.Report(lastMetrics); err != nil {
log.Printf("Error reporting metrics: %v", err)
} else {
log.Println("Metrics reported successfully")
}
// Send heartbeat
if err := r.Heartbeat(deviceID); err != nil {
log.Printf("Error sending heartbeat: %v", err)
}
case <-sigChan:
log.Println("Shutting down...")
return
}
}
}
func getLocalIP() string {
addrs, err := net.InterfaceAddrs()
if err != nil {
return "unknown"
}
for _, addr := range addrs {
if ipNet, ok := addr.(*net.IPNet); ok && !ipNet.IP.IsLoopback() {
if ipNet.IP.To4() != nil {
return ipNet.IP.String()
}
}
}
return "unknown"
}