Initial commit: Nerd Monitor - Cross-platform system monitoring application
Features: - Multi-platform agents (Linux, macOS, Windows - AMD64 & ARM64) - Real-time CPU, RAM, and disk usage monitoring - Responsive web dashboard with live status indicators - Session-based authentication with secure credentials - Stale agent detection and removal (6+ months inactive) - Auto-refresh dashboard (5 second intervals) - 15-second agent reporting intervals - Auto-generated agent IDs from hostnames - In-memory storage (zero database setup) - Minimal dependencies (Chi router + Templ templating) Project Structure: - cmd/: Agent and Server executables - internal/: API, Auth, Stats, Storage, and UI packages - views/: Templ templates for dashboard UI - Makefile: Build automation for all platforms Ready for deployment with comprehensive documentation: - README.md: Full project documentation - QUICKSTART.md: Getting started guide - AGENTS.md: Development guidelines
This commit is contained in:
81
views/dashboard.templ
Normal file
81
views/dashboard.templ
Normal file
@@ -0,0 +1,81 @@
|
||||
package views
|
||||
|
||||
import (
|
||||
"nerd-monitor/internal/store"
|
||||
)
|
||||
|
||||
templ Dashboard(agents []*store.AgentStats, staleAgents []*store.AgentStats) {
|
||||
@BaseLayout("Dashboard", dashboardContent(agents, staleAgents))
|
||||
}
|
||||
|
||||
templ dashboardContent(agents []*store.AgentStats, staleAgents []*store.AgentStats) {
|
||||
<h2 style="margin-bottom: 1rem;">Agent Status Overview</h2>
|
||||
|
||||
@StaleAgentAlert(len(staleAgents), nil)
|
||||
|
||||
if len(agents) == 0 {
|
||||
<div class="alert alert-info">
|
||||
<strong>ℹ️ No agents connected</strong>
|
||||
<p style="margin-top: 0.5rem; font-size: 0.875rem;">
|
||||
Start an agent to see its statistics appear here.
|
||||
</p>
|
||||
</div>
|
||||
} else {
|
||||
<div class="card">
|
||||
<div class="card-title">Active Agents</div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Hostname</th>
|
||||
<th>CPU Usage</th>
|
||||
<th>Memory</th>
|
||||
<th>Disk</th>
|
||||
<th>Last Seen</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
for _, agent := range agents {
|
||||
@AgentRow(agent.ID, agent.Hostname, agent.CPUUsage, agent.RAMUsage, agent.RAMTotal, agent.DiskUsage, agent.DiskTotal, agent.LastSeen)
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
|
||||
if len(staleAgents) > 0 {
|
||||
<div class="card" style="margin-top: 2rem; border-color: #d97706;">
|
||||
<div class="card-title" style="color: #f59e0b;">
|
||||
Stale Agents ({ len(staleAgents) })
|
||||
</div>
|
||||
<form method="POST" action="/api/agents/remove-stale" style="margin-top: 1rem;">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 30px;"><input type="checkbox" id="select-all" onchange="toggleAll(this)" /></th>
|
||||
<th>Hostname</th>
|
||||
<th>Last Seen</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
for _, agent := range staleAgents {
|
||||
<tr>
|
||||
<td><input type="checkbox" name="agent_ids" value={ agent.ID } class="agent-checkbox" /></td>
|
||||
<td>{ agent.Hostname }</td>
|
||||
<td class="timestamp">{ FormatTime(agent.LastSeen) }</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="btn-group">
|
||||
<button type="submit" class="btn btn-danger" style="margin-top: 1rem;">Remove Selected</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<script>
|
||||
function toggleAll(checkbox) {
|
||||
const checkboxes = document.querySelectorAll('.agent-checkbox');
|
||||
checkboxes.forEach(cb => cb.checked = checkbox.checked);
|
||||
}
|
||||
</script>
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user