Messy JS + config from .env
This commit is contained in:
parent
71d4eb6cc3
commit
498aba835f
|
@ -0,0 +1,3 @@
|
||||||
|
DISK_FETCH_FREQUENCY=5
|
||||||
|
MEMORY_DUMP_FREQUENCY=60
|
||||||
|
MAX_HISTORY_AGE=2592000
|
|
@ -1 +1,2 @@
|
||||||
snapshots.dat
|
snapshots.dat
|
||||||
|
.env
|
||||||
|
|
1
go.mod
1
go.mod
|
@ -14,6 +14,7 @@ require (
|
||||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
github.com/go-playground/validator/v10 v10.17.0 // indirect
|
github.com/go-playground/validator/v10 v10.17.0 // indirect
|
||||||
github.com/goccy/go-json v0.10.2 // indirect
|
github.com/goccy/go-json v0.10.2 // indirect
|
||||||
|
github.com/joho/godotenv v1.5.1 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
|
||||||
github.com/leodido/go-urn v1.2.4 // indirect
|
github.com/leodido/go-urn v1.2.4 // indirect
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -30,6 +30,8 @@ github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MG
|
||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||||
|
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
|
|
|
@ -1,19 +1,48 @@
|
||||||
package config
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/joho/godotenv"
|
||||||
|
)
|
||||||
|
|
||||||
type DHConfig struct {
|
type DHConfig struct {
|
||||||
DiskFetchFrequency int `json:"diskFetchFrequency" comment:"How often should a snapshot be taken of the current state of the disks"`
|
DiskFetchFrequency int `json:"diskFetchFrequency"`
|
||||||
MemoryDumpFrequency int `json:"memoryDumpFrequency" comment:"How often should we save the snapshots from memory to disk"`
|
MemoryDumpFrequency int `json:"memoryDumpFrequency"`
|
||||||
MaxHistoryAge int
|
MaxHistoryAge int `json:"maxHistoryAge"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetConfiguration() DHConfig {
|
func GetConfiguration() DHConfig {
|
||||||
|
// Load .env file if it exists
|
||||||
// TODO: Read from os.environment or simply load the defaults
|
if err := godotenv.Load(); err != nil {
|
||||||
|
log.Println("No .env file found")
|
||||||
return DHConfig{
|
|
||||||
DiskFetchFrequency: 5,
|
|
||||||
MemoryDumpFrequency: 16,
|
|
||||||
MaxHistoryAge: 2592000,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config := DHConfig{
|
||||||
|
DiskFetchFrequency: 5, // default value
|
||||||
|
MemoryDumpFrequency: 60, // default value
|
||||||
|
MaxHistoryAge: 2592000, // default value
|
||||||
|
}
|
||||||
|
|
||||||
|
if val, exists := os.LookupEnv("DISK_FETCH_FREQUENCY"); exists {
|
||||||
|
if intValue, err := strconv.Atoi(val); err == nil {
|
||||||
|
config.DiskFetchFrequency = intValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if val, exists := os.LookupEnv("MEMORY_DUMP_FREQUENCY"); exists {
|
||||||
|
if intValue, err := strconv.Atoi(val); err == nil {
|
||||||
|
config.MemoryDumpFrequency = intValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if val, exists := os.LookupEnv("MAX_HISTORY_AGE"); exists {
|
||||||
|
if intValue, err := strconv.Atoi(val); err == nil {
|
||||||
|
config.MaxHistoryAge = intValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return config
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,16 +104,12 @@ func RunService() {
|
||||||
|
|
||||||
// Snapshot taking routine
|
// Snapshot taking routine
|
||||||
go func() {
|
go func() {
|
||||||
|
waitTime := time.Duration(config.GetConfiguration().DiskFetchFrequency) * time.Second
|
||||||
for {
|
for {
|
||||||
time.Sleep(time.Duration(config.GetConfiguration().DiskFetchFrequency) * time.Second)
|
time.Sleep(waitTime)
|
||||||
data, err := TakeHardwareSnapshot()
|
_, err := TakeHardwareSnapshot()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Hardware Fetch Error: %s", err)
|
fmt.Printf("Hardware Fetch Error: %s", err)
|
||||||
} else {
|
|
||||||
fmt.Println("Got Snapshot for " + data.TimeStamp.Format("02/01/2006"))
|
|
||||||
for _, hdd := range data.HDD {
|
|
||||||
fmt.Printf("%s[%s]: %v\n", hdd.Model, hdd.Size, hdd.Temperature)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
@ -121,13 +117,12 @@ func RunService() {
|
||||||
// Periodic saving routine
|
// Periodic saving routine
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
time.Sleep(time.Duration(config.GetConfiguration().MemoryDumpFrequency) * time.Second)
|
waitTime := time.Duration(config.GetConfiguration().MemoryDumpFrequency) * time.Second
|
||||||
|
time.Sleep(waitTime)
|
||||||
err := SaveSnapshotsToFile()
|
err := SaveSnapshotsToFile()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Memory Dump Error: %s", err)
|
fmt.Printf("Memory Dump Error: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("Saved Snapshots to file")
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,134 @@
|
||||||
|
function stringToColor(str) {
|
||||||
|
let hash = 0;
|
||||||
|
for (let i = 0; i < str.length; i++) {
|
||||||
|
hash = str.charCodeAt(i) + ((hash << 5) - hash);
|
||||||
|
}
|
||||||
|
let color = '#';
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
const value = (hash >> (i * 8)) & 0xFF;
|
||||||
|
color += ('00' + value.toString(16)).substr(-2);
|
||||||
|
}
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
const diskTableBody = document.getElementById('disk-table-body');
|
||||||
|
const ctx = document.getElementById('temperatureChart').getContext('2d');
|
||||||
|
let temperatureChart = new Chart(ctx, {
|
||||||
|
type: 'line',
|
||||||
|
data: {
|
||||||
|
datasets: []
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
scales: {
|
||||||
|
x: {
|
||||||
|
type: 'time',
|
||||||
|
time: {
|
||||||
|
unit: 'second',
|
||||||
|
displayFormats: {
|
||||||
|
second: 'HH:mm:ss'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: 'Time'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
beginAtZero: true,
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: 'Temperature (°C)'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function fetchAndUpdateDisks() {
|
||||||
|
fetch('/v1/api/disks?temp=true')
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
updateDiskTable(data.disks);
|
||||||
|
})
|
||||||
|
.catch(error => console.error('Error fetching disk data:', error));
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateDiskTable(disks) {
|
||||||
|
let tableHTML = '';
|
||||||
|
disks.forEach(disk => {
|
||||||
|
tableHTML += `
|
||||||
|
<tr>
|
||||||
|
<td>${disk.Name}</td>
|
||||||
|
<td>${disk.Transport}</td>
|
||||||
|
<td>${disk.Size}</td>
|
||||||
|
<td>${disk.Model}</td>
|
||||||
|
<td>${disk.Serial}</td>
|
||||||
|
<td>${disk.Type}</td>
|
||||||
|
<td>${disk.Temperature}</td>
|
||||||
|
</tr>
|
||||||
|
`;
|
||||||
|
});
|
||||||
|
diskTableBody.innerHTML = tableHTML;
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetchAndUpdateTemperatureChart() {
|
||||||
|
fetch('/v1/api/snapshots')
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(snapshots => {
|
||||||
|
updateTemperatureChart(snapshots);
|
||||||
|
})
|
||||||
|
.catch(error => console.error('Error fetching temperature data:', error));
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateTemperatureChart(snapshots) {
|
||||||
|
// Clear existing datasets
|
||||||
|
temperatureChart.data.datasets = [];
|
||||||
|
|
||||||
|
snapshots.forEach(snapshot => {
|
||||||
|
const time = new Date(snapshot.TimeStamp);
|
||||||
|
snapshot.HDD.forEach(disk => {
|
||||||
|
let dataset = temperatureChart.data.datasets.find(d => d.label === disk.Name);
|
||||||
|
if (!dataset) {
|
||||||
|
dataset = {
|
||||||
|
label: disk.Name,
|
||||||
|
data: [],
|
||||||
|
fill: false,
|
||||||
|
borderColor: stringToColor(disk.Name),
|
||||||
|
borderWidth: 1
|
||||||
|
};
|
||||||
|
temperatureChart.data.datasets.push(dataset);
|
||||||
|
}
|
||||||
|
|
||||||
|
dataset.data.push({
|
||||||
|
x: time,
|
||||||
|
y: disk.Temperature
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
temperatureChart.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Chart.js zoom and pan configuration
|
||||||
|
temperatureChart.options.plugins.zoom = {
|
||||||
|
zoom: {
|
||||||
|
wheel: {
|
||||||
|
enabled: true,
|
||||||
|
},
|
||||||
|
pinch: {
|
||||||
|
enabled: true
|
||||||
|
},
|
||||||
|
mode: 'x',
|
||||||
|
},
|
||||||
|
pan: {
|
||||||
|
enabled: true,
|
||||||
|
mode: 'x',
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchAndUpdateDisks();
|
||||||
|
fetchAndUpdateTemperatureChart();
|
||||||
|
setInterval(fetchAndUpdateDisks, 5000);
|
||||||
|
setInterval(fetchAndUpdateTemperatureChart, 5000);
|
||||||
|
});
|
|
@ -11,30 +11,26 @@
|
||||||
<h1>Drive Health Dashboard</h1>
|
<h1>Drive Health Dashboard</h1>
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<!-- ... table headers ... -->
|
||||||
<th>Name</th>
|
|
||||||
<th>Transport</th>
|
|
||||||
<th>Size</th>
|
|
||||||
<th>Model</th>
|
|
||||||
<th>Serial</th>
|
|
||||||
<th>Type</th>
|
|
||||||
<th>Temperature</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody id="disk-table-body">
|
||||||
{{range .drives}}
|
<!-- Data will be populated here by JavaScript -->
|
||||||
<tr>
|
|
||||||
<td>{{.Name}}</td>
|
|
||||||
<td>{{.Transport}}</td>
|
|
||||||
<td>{{.Size}}</td>
|
|
||||||
<td>{{.Model}}</td>
|
|
||||||
<td>{{.Serial}}</td>
|
|
||||||
<td>{{.Type}}</td>
|
|
||||||
<td>{{.Temperature}}</td>
|
|
||||||
</tr>
|
|
||||||
{{end}}
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<div class="chart-container" style="position: relative; height:40vh; width:80vw; overflow-x: scroll;">
|
||||||
|
<canvas id="temperatureChart"></canvas>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/chart.js@latest"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns@latest"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-zoom@latest"></script>
|
||||||
|
|
||||||
|
<script src="/static/main.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
Loading…
Reference in New Issue