mirror of
https://github.com/andrey271192/vps_monitoring.git
synced 2026-09-20 11:55:34 +00:00
feat: keenetic integration, VPN alerts, docs
Wire keenetic monitor loop and router into main app. Add deduplicated Telegram alerts for router offline, internet, CPU/RAM, and VPN down. Update README with KeenDNS edit, import format, troubleshooting. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
# VPS Monitor — Windows Agent Installer
|
||||
# Usage: powershell -ExecutionPolicy Bypass -File install_agent.ps1 -ServerUrl "http://IP:7272" -AgentName "MyPC"
|
||||
# VPS Monitor - Windows Agent Installer
|
||||
# Usage (PowerShell Admin): .\install_agent.ps1 -ServerUrl "http://IP" -AgentName "MyPC"
|
||||
|
||||
param(
|
||||
[Parameter(Mandatory=$true)]
|
||||
@@ -14,7 +14,7 @@ $InstallDir = "C:\VPS-Monitor"
|
||||
$TaskName = "VPS-Monitor-Agent"
|
||||
|
||||
Write-Host "================================================" -ForegroundColor Cyan
|
||||
Write-Host " VPS Monitor — Agent Installer" -ForegroundColor Cyan
|
||||
Write-Host " VPS Monitor - Agent Installer" -ForegroundColor Cyan
|
||||
Write-Host "================================================" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
Write-Host " Server: $ServerUrl" -ForegroundColor White
|
||||
@@ -32,15 +32,17 @@ if (-not (Test-Path $InstallDir)) {
|
||||
# Download agent script
|
||||
Write-Host "[*] Downloading agent..." -ForegroundColor Yellow
|
||||
$AgentUrl = "$ServerUrl/static/downloads/vps_monitor_agent.ps1"
|
||||
$AgentDest = "$InstallDir\vps_monitor_agent.ps1"
|
||||
try {
|
||||
Invoke-WebRequest -Uri $AgentUrl -OutFile "$InstallDir\vps_monitor_agent.ps1" -UseBasicParsing
|
||||
$wc = New-Object System.Net.WebClient
|
||||
$wc.Encoding = [System.Text.Encoding]::UTF8
|
||||
$wc.DownloadFile($AgentUrl, $AgentDest)
|
||||
Write-Host "[+] Agent downloaded" -ForegroundColor Green
|
||||
} catch {
|
||||
Write-Host "[!] Download failed, using local copy..." -ForegroundColor Yellow
|
||||
# Fallback: copy from same directory
|
||||
Write-Host "[!] Download failed: $($_.Exception.Message)" -ForegroundColor Yellow
|
||||
$localAgent = Join-Path $PSScriptRoot "vps_monitor_agent.ps1"
|
||||
if (Test-Path $localAgent) {
|
||||
Copy-Item $localAgent "$InstallDir\vps_monitor_agent.ps1"
|
||||
Copy-Item $localAgent $AgentDest
|
||||
Write-Host "[+] Local copy used" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "[-] No agent script found!" -ForegroundColor Red
|
||||
@@ -55,10 +57,10 @@ $config = @{
|
||||
interval = $Interval
|
||||
} | ConvertTo-Json
|
||||
|
||||
Set-Content -Path "$InstallDir\agent_config.json" -Value $config
|
||||
Set-Content -Path "$InstallDir\agent_config.json" -Value $config -Encoding UTF8
|
||||
Write-Host "[+] Config saved" -ForegroundColor Green
|
||||
|
||||
# Create scheduled task (run at startup + every 5 min check)
|
||||
# Create scheduled task
|
||||
$existingTask = Get-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue
|
||||
if ($existingTask) {
|
||||
Unregister-ScheduledTask -TaskName $TaskName -Confirm:$false
|
||||
@@ -71,7 +73,6 @@ $action = New-ScheduledTaskAction `
|
||||
-WorkingDirectory $InstallDir
|
||||
|
||||
$triggerStartup = New-ScheduledTaskTrigger -AtStartup
|
||||
$triggerNow = New-ScheduledTaskTrigger -Once -At (Get-Date) -RepetitionInterval (New-TimeSpan -Minutes 1)
|
||||
|
||||
$principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest
|
||||
|
||||
@@ -88,7 +89,7 @@ Register-ScheduledTask `
|
||||
-Trigger $triggerStartup `
|
||||
-Principal $principal `
|
||||
-Settings $settings `
|
||||
-Description "VPS Monitor PC Agent — sends metrics to $ServerUrl" | Out-Null
|
||||
-Description "VPS Monitor PC Agent" | Out-Null
|
||||
|
||||
Write-Host "[+] Scheduled task created" -ForegroundColor Green
|
||||
|
||||
@@ -106,9 +107,9 @@ Write-Host " Task: $TaskName" -ForegroundColor White
|
||||
Write-Host " Status: Running" -ForegroundColor White
|
||||
Write-Host ""
|
||||
Write-Host " Commands:" -ForegroundColor Gray
|
||||
Write-Host " Check: Get-ScheduledTask '$TaskName'" -ForegroundColor Gray
|
||||
Write-Host " Stop: Stop-ScheduledTask '$TaskName'" -ForegroundColor Gray
|
||||
Write-Host " Start: Start-ScheduledTask '$TaskName'" -ForegroundColor Gray
|
||||
Write-Host " Remove: Unregister-ScheduledTask '$TaskName'" -ForegroundColor Gray
|
||||
Write-Host " Logs: type $InstallDir\agent_config.json" -ForegroundColor Gray
|
||||
Write-Host " Check: Get-ScheduledTask $TaskName" -ForegroundColor Gray
|
||||
Write-Host " Stop: Stop-ScheduledTask $TaskName" -ForegroundColor Gray
|
||||
Write-Host " Start: Start-ScheduledTask $TaskName" -ForegroundColor Gray
|
||||
Write-Host " Remove: Unregister-ScheduledTask $TaskName" -ForegroundColor Gray
|
||||
Write-Host " Config: type $InstallDir\agent_config.json" -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
|
||||
166
windows/synology_tunnel.ps1
Normal file
166
windows/synology_tunnel.ps1
Normal file
@@ -0,0 +1,166 @@
|
||||
# VPS Monitor — Synology SSH Tunnel
|
||||
# Creates a reverse SSH tunnel so VPS can reach local Synology NAS
|
||||
#
|
||||
# Usage: .\synology_tunnel.ps1 -ServerUrl "http://IP" -LocalTarget "192.168.88.6:5000" -VpsPort 15000
|
||||
|
||||
param(
|
||||
[string]$ServerUrl = "",
|
||||
[string]$LocalTarget = "192.168.88.6:5000",
|
||||
[int]$VpsPort = 15000,
|
||||
[switch]$Install
|
||||
)
|
||||
|
||||
$InstallDir = "C:\VPS-Monitor"
|
||||
$TaskName = "VPS-Monitor-SynologyTunnel"
|
||||
$KeyFile = "$InstallDir\synology_tunnel_key"
|
||||
$ConfigFile = "$InstallDir\tunnel_config.json"
|
||||
|
||||
# Load config if exists
|
||||
if (Test-Path $ConfigFile) {
|
||||
$config = Get-Content $ConfigFile | ConvertFrom-Json
|
||||
if (-not $ServerUrl) { $ServerUrl = $config.server_url }
|
||||
if ($config.local_target) { $LocalTarget = $config.local_target }
|
||||
if ($config.vps_port) { $VpsPort = $config.vps_port }
|
||||
}
|
||||
|
||||
if (-not $ServerUrl) {
|
||||
Write-Host "ERROR: -ServerUrl required" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Extract VPS IP from ServerUrl
|
||||
$VpsHost = ($ServerUrl -replace "https?://", "") -replace ":[0-9]+$", ""
|
||||
|
||||
Write-Host "================================================" -ForegroundColor Cyan
|
||||
Write-Host " VPS Monitor — Synology Tunnel" -ForegroundColor Cyan
|
||||
Write-Host "================================================" -ForegroundColor Cyan
|
||||
Write-Host " VPS: $VpsHost" -ForegroundColor White
|
||||
Write-Host " Target: $LocalTarget" -ForegroundColor White
|
||||
Write-Host " Port: $VpsPort (on VPS)" -ForegroundColor White
|
||||
Write-Host ""
|
||||
|
||||
# Ensure install directory
|
||||
if (-not (Test-Path $InstallDir)) {
|
||||
New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null
|
||||
}
|
||||
|
||||
# Download SSH key from VPS
|
||||
if (-not (Test-Path $KeyFile)) {
|
||||
Write-Host "[*] Downloading tunnel SSH key..." -ForegroundColor Yellow
|
||||
try {
|
||||
$wc = New-Object System.Net.WebClient
|
||||
$wc.DownloadFile("$ServerUrl/static/downloads/synology_tunnel_key", $KeyFile)
|
||||
Write-Host "[+] Key downloaded" -ForegroundColor Green
|
||||
} catch {
|
||||
Write-Host "[!] Key download failed: $($_.Exception.Message)" -ForegroundColor Red
|
||||
Write-Host " Make sure you're logged into the dashboard first" -ForegroundColor Yellow
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Fix key permissions (Windows — icacls)
|
||||
icacls $KeyFile /inheritance:r /grant:r "${env:USERNAME}:(R)" 2>$null | Out-Null
|
||||
}
|
||||
|
||||
# Save config
|
||||
$tunnelConfig = @{
|
||||
server_url = $ServerUrl
|
||||
local_target = $LocalTarget
|
||||
vps_port = $VpsPort
|
||||
vps_host = $VpsHost
|
||||
} | ConvertTo-Json
|
||||
Set-Content -Path $ConfigFile -Value $tunnelConfig
|
||||
|
||||
# Install mode — create scheduled task
|
||||
if ($Install) {
|
||||
Write-Host "[*] Creating scheduled task..." -ForegroundColor Yellow
|
||||
|
||||
$existingTask = Get-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue
|
||||
if ($existingTask) {
|
||||
Unregister-ScheduledTask -TaskName $TaskName -Confirm:$false
|
||||
}
|
||||
|
||||
$action = New-ScheduledTaskAction `
|
||||
-Execute "powershell.exe" `
|
||||
-Argument "-ExecutionPolicy Bypass -WindowStyle Hidden -File `"$InstallDir\synology_tunnel.ps1`"" `
|
||||
-WorkingDirectory $InstallDir
|
||||
|
||||
$trigger = New-ScheduledTaskTrigger -AtStartup
|
||||
$principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest
|
||||
|
||||
$settings = New-ScheduledTaskSettingsSet `
|
||||
-AllowStartIfOnBatteries `
|
||||
-DontStopIfGoingOnBatteries `
|
||||
-RestartCount 999 `
|
||||
-RestartInterval (New-TimeSpan -Minutes 1) `
|
||||
-ExecutionTimeLimit (New-TimeSpan -Days 365)
|
||||
|
||||
Register-ScheduledTask `
|
||||
-TaskName $TaskName `
|
||||
-Action $action `
|
||||
-Trigger $trigger `
|
||||
-Principal $principal `
|
||||
-Settings $settings `
|
||||
-Description "VPS Monitor — SSH tunnel to Synology NAS via $VpsHost" | Out-Null
|
||||
|
||||
Write-Host "[+] Scheduled task created: $TaskName" -ForegroundColor Green
|
||||
Start-ScheduledTask -TaskName $TaskName
|
||||
Write-Host "[+] Tunnel started!" -ForegroundColor Green
|
||||
Write-Host ""
|
||||
Write-Host " Commands:" -ForegroundColor Gray
|
||||
Write-Host " Check: Get-ScheduledTask '$TaskName'" -ForegroundColor Gray
|
||||
Write-Host " Stop: Stop-ScheduledTask '$TaskName'" -ForegroundColor Gray
|
||||
Write-Host " Remove: Unregister-ScheduledTask '$TaskName'" -ForegroundColor Gray
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Main loop — maintain SSH tunnel
|
||||
Write-Host "[*] Starting tunnel loop..." -ForegroundColor Green
|
||||
Write-Host " VPS:$VpsPort -> Local:$LocalTarget" -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
|
||||
$LocalIP = $LocalTarget.Split(":")[0]
|
||||
$LocalPort = $LocalTarget.Split(":")[1]
|
||||
|
||||
while ($true) {
|
||||
$ts = Get-Date -Format "HH:mm:ss"
|
||||
|
||||
# Check if ssh.exe available (Windows 10+)
|
||||
$sshExe = Get-Command ssh.exe -ErrorAction SilentlyContinue
|
||||
|
||||
if ($sshExe) {
|
||||
Write-Host "[$ts] Connecting via ssh.exe..." -ForegroundColor Cyan
|
||||
# ssh -N = no command, -R = reverse tunnel, -o = options for stability
|
||||
& ssh.exe -N `
|
||||
-o "StrictHostKeyChecking=no" `
|
||||
-o "ServerAliveInterval=30" `
|
||||
-o "ServerAliveCountMax=3" `
|
||||
-o "ExitOnForwardFailure=yes" `
|
||||
-o "BatchMode=yes" `
|
||||
-i $KeyFile `
|
||||
-R "127.0.0.1:${VpsPort}:${LocalIP}:${LocalPort}" `
|
||||
"root@${VpsHost}" 2>&1
|
||||
|
||||
$exitCode = $LASTEXITCODE
|
||||
Write-Host "[$ts] SSH disconnected (exit: $exitCode)" -ForegroundColor Yellow
|
||||
} else {
|
||||
# Fallback to plink
|
||||
$plinkPath = "$InstallDir\plink.exe"
|
||||
if (-not (Test-Path $plinkPath)) {
|
||||
Write-Host "[$ts] No ssh.exe or plink.exe found. Install OpenSSH or download plink." -ForegroundColor Red
|
||||
Start-Sleep -Seconds 60
|
||||
continue
|
||||
}
|
||||
|
||||
Write-Host "[$ts] Connecting via plink..." -ForegroundColor Cyan
|
||||
echo y | & $plinkPath -N -batch `
|
||||
-i $KeyFile `
|
||||
-R "127.0.0.1:${VpsPort}:${LocalIP}:${LocalPort}" `
|
||||
"root@${VpsHost}" 2>&1
|
||||
|
||||
Write-Host "[$ts] Plink disconnected" -ForegroundColor Yellow
|
||||
}
|
||||
|
||||
# Wait before reconnect
|
||||
Write-Host "[$ts] Reconnecting in 10s..." -ForegroundColor Gray
|
||||
Start-Sleep -Seconds 10
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
# VPS Monitor — Windows PC Agent
|
||||
# VPS Monitor - Windows PC Agent
|
||||
# Sends system metrics to VPS Monitoring server
|
||||
#
|
||||
# Install: Run install_agent.ps1
|
||||
@@ -13,7 +13,7 @@ param(
|
||||
# Load config
|
||||
$ConfigPath = Join-Path $PSScriptRoot "agent_config.json"
|
||||
if (Test-Path $ConfigPath) {
|
||||
$config = Get-Content $ConfigPath | ConvertFrom-Json
|
||||
$config = Get-Content $ConfigPath -Raw | ConvertFrom-Json
|
||||
if (-not $ServerUrl) { $ServerUrl = $config.server_url }
|
||||
if (-not $AgentName) { $AgentName = $config.agent_name }
|
||||
if ($config.interval) { $Interval = $config.interval }
|
||||
@@ -26,7 +26,7 @@ if (-not $ServerUrl -or -not $AgentName) {
|
||||
}
|
||||
|
||||
Write-Host "================================================" -ForegroundColor Cyan
|
||||
Write-Host " VPS Monitor — PC Agent" -ForegroundColor Cyan
|
||||
Write-Host " VPS Monitor - PC Agent" -ForegroundColor Cyan
|
||||
Write-Host " Server: $ServerUrl" -ForegroundColor Gray
|
||||
Write-Host " Name: $AgentName" -ForegroundColor Gray
|
||||
Write-Host " Interval: ${Interval}s" -ForegroundColor Gray
|
||||
@@ -44,9 +44,19 @@ function Get-SystemMetrics {
|
||||
$metrics["os"] = "$($os.Caption) $($os.Version)"
|
||||
|
||||
# CPU
|
||||
$cpu = Get-CimInstance Win32_Processor
|
||||
$metrics["cpu_name"] = $cpu.Name
|
||||
$metrics["cpu_percent"] = [math]::Round((Get-Counter '\Processor(_Total)\% Processor Time' -ErrorAction SilentlyContinue).CounterSamples[0].CookedValue, 1)
|
||||
try {
|
||||
$cpu = Get-CimInstance Win32_Processor | Select-Object -First 1
|
||||
$metrics["cpu_name"] = $cpu.Name
|
||||
$counter = Get-Counter '\Processor(_Total)\% Processor Time' -ErrorAction SilentlyContinue
|
||||
if ($counter) {
|
||||
$metrics["cpu_percent"] = [math]::Round($counter.CounterSamples[0].CookedValue, 1)
|
||||
} else {
|
||||
$metrics["cpu_percent"] = 0
|
||||
}
|
||||
} catch {
|
||||
$metrics["cpu_percent"] = 0
|
||||
$metrics["cpu_name"] = "Unknown"
|
||||
}
|
||||
|
||||
# RAM
|
||||
$totalRam = [math]::Round($os.TotalVisibleMemorySize / 1024, 0)
|
||||
@@ -82,18 +92,24 @@ function Get-SystemMetrics {
|
||||
}
|
||||
|
||||
# Network
|
||||
$adapters = Get-NetAdapter -Physical -ErrorAction SilentlyContinue | Where-Object { $_.Status -eq "Up" }
|
||||
$netInfo = @()
|
||||
foreach ($a in $adapters) {
|
||||
$stats = Get-NetAdapterStatistics -Name $a.Name -ErrorAction SilentlyContinue
|
||||
$netInfo += @{
|
||||
name = $a.Name
|
||||
speed_mbps = [math]::Round($a.LinkSpeed.Replace(" Gbps","000").Replace(" Mbps","") -as [double], 0)
|
||||
sent_mb = if ($stats) { [math]::Round($stats.SentBytes / 1MB, 0) } else { 0 }
|
||||
received_mb = if ($stats) { [math]::Round($stats.ReceivedBytes / 1MB, 0) } else { 0 }
|
||||
try {
|
||||
$adapters = Get-NetAdapter -Physical -ErrorAction SilentlyContinue | Where-Object { $_.Status -eq "Up" }
|
||||
$netInfo = @()
|
||||
foreach ($a in $adapters) {
|
||||
$stats = Get-NetAdapterStatistics -Name $a.Name -ErrorAction SilentlyContinue
|
||||
$speed = 0
|
||||
try { $speed = [math]::Round($a.LinkSpeed.Replace(" Gbps","000").Replace(" Mbps","") -as [double], 0) } catch {}
|
||||
$netInfo += @{
|
||||
name = $a.Name
|
||||
speed_mbps = $speed
|
||||
sent_mb = if ($stats) { [math]::Round($stats.SentBytes / 1MB, 0) } else { 0 }
|
||||
received_mb = if ($stats) { [math]::Round($stats.ReceivedBytes / 1MB, 0) } else { 0 }
|
||||
}
|
||||
}
|
||||
$metrics["network"] = $netInfo
|
||||
} catch {
|
||||
$metrics["network"] = @()
|
||||
}
|
||||
$metrics["network"] = $netInfo
|
||||
|
||||
# Uptime
|
||||
$uptime = (Get-Date) - $os.LastBootUpTime
|
||||
@@ -110,9 +126,13 @@ function Get-SystemMetrics {
|
||||
} catch {}
|
||||
|
||||
# Top processes by CPU
|
||||
$topProcs = Get-Process | Sort-Object CPU -Descending | Select-Object -First 5 |
|
||||
ForEach-Object { @{ name = $_.ProcessName; cpu = [math]::Round($_.CPU, 1); ram_mb = [math]::Round($_.WorkingSet64 / 1MB, 0) } }
|
||||
$metrics["top_processes"] = $topProcs
|
||||
try {
|
||||
$topProcs = Get-Process | Sort-Object CPU -Descending | Select-Object -First 5 |
|
||||
ForEach-Object { @{ name = $_.ProcessName; cpu = [math]::Round($_.CPU, 1); ram_mb = [math]::Round($_.WorkingSet64 / 1MB, 0) } }
|
||||
$metrics["top_processes"] = $topProcs
|
||||
} catch {
|
||||
$metrics["top_processes"] = @()
|
||||
}
|
||||
|
||||
return $metrics
|
||||
}
|
||||
@@ -125,12 +145,10 @@ function Send-Metrics($metrics) {
|
||||
} | ConvertTo-Json -Depth 5
|
||||
|
||||
try {
|
||||
$response = Invoke-RestMethod -Uri "$ServerUrl/api/pc/heartbeat" `
|
||||
-Method POST `
|
||||
-Body $body `
|
||||
-ContentType "application/json" `
|
||||
-TimeoutSec 15
|
||||
|
||||
$wc = New-Object System.Net.WebClient
|
||||
$wc.Headers.Add("Content-Type", "application/json")
|
||||
$wc.Encoding = [System.Text.Encoding]::UTF8
|
||||
$null = $wc.UploadString("$ServerUrl/api/pc/heartbeat", "POST", $body)
|
||||
return $true
|
||||
} catch {
|
||||
Write-Host " [!] Send failed: $($_.Exception.Message)" -ForegroundColor Yellow
|
||||
@@ -150,7 +168,7 @@ while ($true) {
|
||||
$ok = Send-Metrics $metrics
|
||||
|
||||
if ($ok) {
|
||||
Write-Host "[$ts] OK — CPU: $($metrics.cpu_percent)% RAM: $($metrics.ram_percent)% Disk: $($metrics.disk_percent)%" -ForegroundColor Green
|
||||
Write-Host "[$ts] OK - CPU: $($metrics.cpu_percent)% RAM: $($metrics.ram_percent)% Disk: $($metrics.disk_percent)%" -ForegroundColor Green
|
||||
}
|
||||
} catch {
|
||||
Write-Host "[$ts] ERROR: $($_.Exception.Message)" -ForegroundColor Red
|
||||
|
||||
Reference in New Issue
Block a user