This commit is contained in:
2025-04-23 04:18:28 +02:00
parent 10a7d9bb6b
commit a16ac8f627
276 changed files with 85166 additions and 1 deletions

View File

@@ -0,0 +1,19 @@
<!-- Hardware stats fragment for polling updates -->
<tbody>
<tr>
<th scope="row">CPU</th>
<td>{{ cpuInfo }} ({{ cpuUsage }})</td>
</tr>
<tr>
<th scope="row">Memory</th>
<td>{{ memoryInfo }} ({{ memUsage }})</td>
</tr>
<tr>
<th scope="row">Disk</th>
<td>{{ diskInfo }} ({{ diskUsage }})</td>
</tr>
<tr>
<th scope="row">Network</th>
<td style="white-space: pre-line;">{{ networkInfo }}</td>
</tr>
</tbody>

View File

@@ -0,0 +1,79 @@
{{ extends "../layout" }}
{{ block documentBody() }}
<article class="system-info">
<header>
<h2 class="title">System Information</h2>
<p class="description text-muted">Overview of system resources and configuration</p>
</header>
<div class="grid" style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 1rem;">
<div>
<article class="hardware-info">
<header>
<h3 id="hardware-title">Hardware</h3>
</header>
<table class="table table-striped" up:poll="/admin/system/hardware-stats" up:target=".hardware-stats" up:poll-interval="1000">
<tbody>
<tr>
<th scope="row">CPU</th>
<td>{{ cpuInfo }}</td>
</tr>
<tr>
<th scope="row">Memory</th>
<td>{{ memoryInfo }}</td>
</tr>
<tr>
<th scope="row">Disk</th>
<td>{{ diskInfo }}</td>
</tr>
<tr>
<th scope="row">Network</th>
<td style="white-space: pre-line;">{{ networkInfo }}</td>
</tr>
</tbody>
</table>
{{ include "partials/network_chart" }}
</article>
</div>
<div>
<article class="software-info">
<header>
<h3 id="software-title">Software</h3>
</header>
<table class="table table-bordered" data:type="software-info">
<tbody>
<tr>
<th scope="row">OS</th>
<td>{{ osInfo }}</td>
</tr>
<tr>
<th scope="row">HeroLauncher</th>
<td>HeroLauncher</td>
</tr>
<tr>
<th scope="row">Uptime</th>
<td>{{ uptimeInfo }}</td>
</tr>
</tbody>
</table>
{{ include "partials/__cpu_chart" }}
{{ include "partials/__memory_chart" }}
</article>
</div>
</div>
</article>
{{ end }}
{{ block scripts() }}
<script src="/js/echarts/echarts.min.js"></script>
<script src="/js/charts/cpu-chart.js"></script>
<script src="/js/charts/memory-chart.js"></script>
<script src="/js/charts/network-chart.js"></script>
<script src="/js/charts/stats-fetcher.js"></script>
{{ end }}

View File

@@ -0,0 +1,58 @@
{{ extends "../layout" }}
<header>
<h2 class="title">System Jobs</h2>
<p class="description text-muted">Overview of scheduled jobs</p>
</header>
<article class="jobs-info">
<div class="grid" style="display: grid; grid-template-columns: 1fr; gap: 1rem;">
<div>
<article class="jobs-table">
<header>
<h3 id="jobs-title">Scheduled Jobs</h3>
<p class="refresh-status">
<button class="btn btn-sm" onclick="refreshJobs()">
Refresh
<span class="loading-indicator" id="refresh-loading" style="display: none;">&nbsp;Loading...</span>
</button>
</p>
</header>
<div class="jobs-table-content" up-poll="/admin/system/jobs-data" up-hungry="true" up-interval="10000" style="display: block; width: 100%;" id="jobs-content">
{{ if isset(., "error") }}
<div class="alert alert-danger">{{ .error }}</div>
{{ end }}
<table class="table table-striped" id="jobs-stats">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">Name</th>
<th scope="col">Status</th>
<th scope="col">Next Run</th>
<th scope="col">Last Run</th>
</tr>
</thead>
<tbody>
{{ if isset(., "jobs") && len(.jobs) > 0 }}
{{ range .jobs }}
<tr class="{{ if .is_current }}table-primary{{ end }}">
<td>{{ .id }}</td>
<td>{{ .name }}</td>
<td>{{ .status }}</td>
<td>{{ .next_run }}</td>
<td>{{ .last_run }}</td>
</tr>
{{ end }}
{{ else }}
<tr>
<td colspan="5">No job data available.</td>
</tr>
{{ end }}
</tbody>
</table>
</div>
</article>
</div>
</div>
</article>

View File

@@ -0,0 +1,135 @@
{{ extends "../layout" }}
{{ block documentBody() }}
<article>
<header class="flex-container">
<div>
<h2>{{title}}</h2>
<p>View and filter logs from different sources</p>
</div>
<div>
<a href="/api/logs/export" role="button" class="outline">Export Logs</a>
</div>
</header>
<article class="filter-controls">
<form class="log-controls" id="log-filter-form" action="/admin/system/logs" method="get" up-target="#logs-table-container" up-submit>
<div class="grid filter-grid">
<div class="filter-item">
<label for="log-type">Log Type</label>
<select id="log-type" name="log_type">
{{range logTypes}}
<option value="{{.}}" {{if selectedLogType == '.'}}selected{{end}}>{{if . == "all"}}All Logs{{else if . == "system"}}System Logs{{else if . == "service"}}Service Logs{{else if . == "job"}}Job Logs{{else if . == "process"}}Process Logs{{end}}</option>
{{end}}
</select>
</div>
<div class="filter-item">
<label for="log-level">Log Level</label>
<select id="log-level" name="type">
<option value="all" {{if typeParam == "all" || typeParam == ""}}selected{{end}}>All Levels</option>
<option value="info" {{if typeParam == "info"}}selected{{end}}>Info</option>
<option value="error" {{if typeParam == "error"}}selected{{end}}>Error</option>
</select>
</div>
<div class="filter-item">
<label for="log-source">Log Source</label>
<select id="log-source" name="category">
<option value="" {{if categoryParam == ""}}selected{{end}}>All Sources</option>
<option value="system" {{if categoryParam == "system"}}selected{{end}}>System</option>
<option value="redis" {{if categoryParam == "redis"}}selected{{end}}>Redis</option>
<option value="executor" {{if categoryParam == "executor"}}selected{{end}}>Executor</option>
<option value="package" {{if categoryParam == "package"}}selected{{end}}>Package Manager</option>
</select>
</div>
<div class="filter-item">
<label for="log-from-date">From Date</label>
<input type="datetime-local" id="log-from-date" name="from">
</div>
<div class="filter-item">
<label for="log-to-date">To Date</label>
<input type="datetime-local" id="log-to-date" name="to">
</div>
<div class="filter-button">
<button type="submit" class="filter-apply" up-target="#logs-table-container">Apply Filters</button>
</div>
</div>
</form>
</article>
<article class="log-container">
<header>
<h3>Log Output</h3>
</header>
<div id="logs-table-container">
<!-- Log content is loaded directly -->
{{ if isset(., "error") }}
<div class="alert alert-danger">{{ .error }}</div>
{{ end }}
<!-- Include logs table -->
<div class="log-table">
<table>
<thead>
<tr>
<th>Timestamp</th>
<th>Level</th>
<th>Source</th>
<th>Message</th>
</tr>
</thead>
<tbody>
{{if isset(., "logs")}}
{{range logs}}
<tr>
<td>{{.timestamp}}</td>
<td class="log-{{.type | lower}}">{{.type}}</td>
<td>{{.category}}</td>
<td>{{.message}}</td>
</tr>
{{else}}
<tr>
<td colspan="4" class="text-center">No logs found matching your criteria</td>
</tr>
{{end}}
{{else}}
<tr>
<td colspan="4" class="text-center">Loading logs...</td>
</tr>
{{end}}
</tbody>
</table>
</div>
<div class="pagination">
<div class="pagination-info">
{{if isset(., "logs")}}
{{if len(logs) > 0}}
<span>Showing {{showing}} of {{total}} logs</span>
{{else}}
<span>No logs found</span>
{{end}}
{{else}}
<span>Loading logs...</span>
{{end}}
</div>
<div class="pagination-controls">
{{if isset(., "page") && isset(., "totalPages")}}
{{if page > 1}}
<a href="/admin/system/logs?page={{page - 1}}{{if isset(., "categoryParam")}}&category={{categoryParam}}{{end}}{{if isset(., "typeParam")}}&type={{typeParam}}{{end}}{{if isset(., "fromParam")}}&from={{fromParam}}{{end}}{{if isset(., "toParam")}}&to={{toParam}}{{end}}" role="button" class="outline secondary" up-target="#logs-table-container">← Previous</a>
{{end}}
{{if page < totalPages}}
<a href="/admin/system/logs?page={{page + 1}}{{if isset(., "categoryParam")}}&category={{categoryParam}}{{end}}{{if isset(., "typeParam")}}&type={{typeParam}}{{end}}{{if isset(., "fromParam")}}&from={{fromParam}}{{end}}{{if isset(., "toParam")}}&to={{toParam}}{{end}}" role="button" class="outline secondary" up-target="#logs-table-container">Next →</a>
{{end}}
{{end}}
</div>
</div>
</div>
</article>
</article>
{{ end }}

View File

@@ -0,0 +1,49 @@
<!-- This template contains just the logs table content for Unpoly updates -->
{{ if isset(., "error") }}
<div class="alert alert-danger">{{ .error }}</div>
{{ end }}
<div class="log-table">
<table>
<thead>
<tr>
<th>Timestamp</th>
<th>Level</th>
<th>Source</th>
<th>Message</th>
</tr>
</thead>
<tbody>
{{range logs}}
<tr>
<td>{{.timestamp}}</td>
<td class="log-{{.type | lower}}">{{.type}}</td>
<td>{{.category}}</td>
<td>{{.message}}</td>
</tr>
{{else}}
<tr>
<td colspan="4" class="text-center">No logs found matching your criteria</td>
</tr>
{{end}}
</tbody>
</table>
</div>
<div class="pagination">
<div class="pagination-info">
{{if len(logs) > 0}}
<span>Showing {{len(logs)}} of {{total}} logs</span>
{{else}}
<span>No logs found</span>
{{end}}
</div>
<div class="pagination-controls">
{{if page > 1}}
<a href="/admin/system/logs?page={{page - 1}}{{if isset(., "selectedLogType")}}&log_type={{selectedLogType}}{{end}}{{if isset(., "categoryParam")}}&category={{categoryParam}}{{end}}{{if isset(., "typeParam")}}&type={{typeParam}}{{end}}{{if isset(., "fromParam")}}&from={{fromParam}}{{end}}{{if isset(., "toParam")}}&to={{toParam}}{{end}}" role="button" class="outline secondary" up-target="#logs-table-container">← Previous</a>
{{end}}
{{if page < totalPages}}
<a href="/admin/system/logs?page={{page + 1}}{{if isset(., "selectedLogType")}}&log_type={{selectedLogType}}{{end}}{{if isset(., "categoryParam")}}&category={{categoryParam}}{{end}}{{if isset(., "typeParam")}}&type={{typeParam}}{{end}}{{if isset(., "fromParam")}}&from={{fromParam}}{{end}}{{if isset(., "toParam")}}&to={{toParam}}{{end}}" role="button" class="outline secondary" up-target="#logs-table-container">Next →</a>
{{end}}
</div>
</div>

View File

@@ -0,0 +1,6 @@
{{ extends "admin/layout" }}
{{ block documentBody() }}
<h1>Test Logs Page</h1>
<p>This is a simple test template</p>
{{ end }}

View File

@@ -0,0 +1,2 @@
<h4 style="margin-bottom: 10px;">Process CPU Usage</h4>
<div id="cpu-chart" style="width: 100%; height: 300px; margin-bottom: 30px;"></div>

View File

@@ -0,0 +1,2 @@
<h4 style="margin-bottom: 10px;">Process Memory Usage</h4>
<div id="memory-chart" style="width: 100%; height: 300px;"></div>

View File

@@ -0,0 +1 @@
<!-- Stats fetcher removed - now loaded from external JS file -->

View File

@@ -0,0 +1,2 @@
<h4 style="margin-bottom: 10px;">Network Traffic</h4>
<div id="network-chart" style="width: 100%; height: 300px; margin-top: 10px;"></div>

View File

@@ -0,0 +1,77 @@
{{ extends "../layout" }}
{{ block documentBody() }}
<article class="processes-info">
<header>
<h2 class="title">System Processes</h2>
<p class="description text-muted">Overview of running processes with CPU and memory usage</p>
</header>
<div class="grid" style="display: grid; grid-template-columns: 1fr; gap: 1rem;">
<div>
<article class="processes-table">
<header>
<h3 id="processes-title">Running Processes</h3>
<p class="refresh-status">
<button class="btn btn-sm" onclick="refreshProcesses()">
Refresh
<span class="loading-indicator" id="refresh-loading" style="display: none;">&nbsp;Loading...</span>
</button>
</p>
</header>
<div class="processes-table-content" up-poll="/admin/system/processes-data" up-hungry="true" up-interval="10000" style="display: block; width: 100%;" id="processes-content">
{{ if isset(., "error") }}
<div class="alert alert-danger">{{ .error }}</div>
{{ end }}
<table class="table table-striped" id="processes-stats">
<thead>
<tr>
<th scope="col">PID</th>
<th scope="col">Name</th>
<th scope="col">Status</th>
<th scope="col">CPU (%)</th>
<th scope="col">Memory (MB)</th>
<th scope="col">Created</th>
</tr>
</thead>
<tbody>
{{ if isset(., "processes") && len(.processes) > 0 }}
{{ range .processes }}
<tr class="{{ if .is_current }}table-primary{{ end }}">
<td>{{ .pid }}</td>
<td>{{ .name }}</td>
<td>{{ .status }}</td>
<td>{{ .cpu_percent_str }}</td>
<td>{{ .memory_mb_str }}</td>
<td>{{ .create_time_str }}</td>
</tr>
{{ end }}
{{ else }}
<tr>
<td colspan="6">No process data available.</td>
</tr>
{{ end }}
</tbody>
</table>
</div>
</article>
</div>
</div>
</article>
<script>
// Ensure processes data is loaded on page load
document.addEventListener('DOMContentLoaded', function() {
// Check if the processes content is empty or shows 'No process data available'
const processesContent = document.getElementById('processes-content');
const tableBody = processesContent ? processesContent.querySelector('tbody') : null;
if (tableBody && (tableBody.innerText.includes('No process data available') || tableBody.children.length <= 1)) {
console.log('Triggering initial process data load');
refreshProcesses();
}
});
</script>
{{ end }}

View File

@@ -0,0 +1,48 @@
<!-- This template contains just the process table content for AJAX updates -->
<div class="processes-table-content">
{{ if isset(., "error") }}
<div class="alert alert-danger">{{ .error }}</div>
{{ end }}
<!-- Debug info -->
<div class="alert alert-info">
{{ if isset(., "debug") }}
Debug: {{ debug }}
{{ end }}
<!-- Direct debug output to help troubleshoot -->
Has processes: {{ hasProcesses ? "Yes" : "No" }}
Process count: {{ processCount }}
</div>
<table class="table table-striped" id="processes-stats">
<thead>
<tr>
<th scope="col">PID</th>
<th scope="col">Name</th>
<th scope="col">Status</th>
<th scope="col">CPU (%)</th>
<th scope="col">Memory (MB)</th>
<th scope="col">Created</th>
</tr>
</thead>
<tbody>
{{ if hasProcesses }}
{{ range processStats }}
<tr{{ if .is_current == true }} class="table-primary"{{ end }}>
<td>{{ .pid }}</td>
<td>{{ .name }}</td>
<td>{{ .status }}</td>
<td>{{ .cpu_percent_str }}</td>
<td>{{ .memory_mb_str }}</td>
<td>{{ .create_time_str }}</td>
</tr>
{{ end }}
{{ else }}
<tr>
<td colspan="6">No process data available.</td>
</tr>
{{ end }}
</tbody>
</table>
</div>

View File

@@ -0,0 +1,36 @@
<!-- This template contains just the process table content for AJAX updates -->
{{ if .error }}
<div class="alert alert-danger">{{ .error }}</div>
{{ end }}
<!-- Process data table - regenerated on each refresh -->
<table class="table table-striped" id="processes-table">
<thead>
<tr>
<th scope='col'>PID</th>
<th scope='col'>Name</th>
<th scope='col'>Status</th>
<th scope='col'>CPU (%)</th>
<th scope='col'>Memory (MB)</th>
<th scope='col'>Created</th>
</tr>
</thead>
<tbody>
{{ if .processes }}
{{ range .processes }}
<tr{{ if .is_current }} class="table-primary"{{ end }}>
<td>{{ .pid }}</td>
<td>{{ .name }}</td>
<td>{{ .status }}</td>
<td>{{ .cpu_percent | printf("%.1f%%") }}</td>
<td>{{ .memory_mb | printf("%.1f MB") }}</td>
<td>{{ .create_time_str }}</td>
</tr>
{{ end }}
{{ else }}
<tr>
<td colspan="6">No process data available.</td>
</tr>
{{ end }}
</tbody>
</table>

View File

@@ -0,0 +1,40 @@
{{ if isset(., "error") }}
<div class="alert alert-danger">{{ .error }}</div>
{{ end }}
<table class="table table-striped" id="processes-stats">
<thead>
<tr>
<th scope="col">PID</th>
<th scope="col">Name</th>
<th scope="col">Status</th>
<th scope="col">CPU (%)</th>
<th scope="col">Memory (MB)</th>
<th scope="col">Created</th>
</tr>
</thead>
<tbody>
{{ if isset(., "processes") }}
{{ if .processes }}
{{ range .processes }}
<tr class="{{ if .is_current }}table-primary{{ end }}">
<td>{{.pid}}</td>
<td>{{.name}}</td>
<td>{{.status}}</td>
<td>{{ printf("%.1f%%", .cpu_percent) }}</td>
<td>{{ printf("%.1f MB", .memory_mb) }}</td>
<td>{{.create_time_str}}</td>
</tr>
{{ end }}
{{ else }}
<tr>
<td colspan="6">No process data available.</td>
</tr>
{{ end }}
{{ else }}
<tr>
<td colspan="6">Loading process data...</td>
</tr>
{{ end }}
</tbody>
</table>

View File

@@ -0,0 +1,77 @@
{{ extends "../../layout" }}
{{ block documentBody() }}
<article>
<header>
<h2>System Settings</h2>
<p>Configure system parameters and preferences</p>
</header>
<form>
<div class="grid">
<div>
<article>
<header>
<h3>Server Settings</h3>
</header>
<label for="server-port">Server Port</label>
<input id="server-port" type="number" value="9001">
<label for="log-level">Default Log Level</label>
<select id="log-level">
<option value="info">Info</option>
<option value="warning">Warning</option>
<option value="error">Error</option>
<option value="debug">Debug</option>
</select>
<label for="max-connections">Max Connections</label>
<input id="max-connections" type="number" value="100">
</article>
</div>
<div>
<article>
<header>
<h3>Security Settings</h3>
</header>
<label for="enable-auth">Enable Authentication</label>
<input id="enable-auth" type="checkbox" checked>
<label for="session-timeout">Session Timeout (minutes)</label>
<input id="session-timeout" type="number" value="30">
<label for="allowed-origins">Allowed Origins (CORS)</label>
<input id="allowed-origins" type="text" value="*">
</article>
</div>
</div>
<div class="grid">
<div>
<article>
<header>
<h3>Redis Settings</h3>
</header>
<label for="redis-port">Redis Port</label>
<input id="redis-port" type="number" value="6378">
<label for="redis-max-memory">Max Memory (MB)</label>
<input id="redis-max-memory" type="number" value="512">
</article>
</div>
<div>
<article>
<header>
<h3>Executor Settings</h3>
</header>
<label for="executor-timeout">Command Timeout (seconds)</label>
<input id="executor-timeout" type="number" value="60">
<label for="executor-max-processes">Max Concurrent Processes</label>
<input id="executor-max-processes" type="number" value="10">
</article>
</div>
</div>
<button type="submit">Save Settings</button>
<button class="secondary" type="reset">Reset</button>
</form>
</article>
{{ end }}