display longer records in charts

This commit is contained in:
Henry Dollman
2024-07-20 13:58:55 -04:00
parent 1c3bf5bfe1
commit 926dcf14c8
6 changed files with 78 additions and 72 deletions

11
main.go
View File

@@ -83,12 +83,10 @@ func main() {
})
e.Router.GET("/icons/*", apis.StaticDirectoryHandler(os.DirFS("./site/public/icons"), false))
e.Router.Any("/*", echo.WrapHandler(proxy))
e.Router.Any("/", echo.WrapHandler(proxy))
// e.Router.Any("/", echo.WrapHandler(proxy))
default:
assets, _ := site.Assets()
icons, _ := site.Icons()
e.Router.GET("/icons/*", apis.StaticDirectoryHandler(icons, false))
e.Router.Any("/*", apis.StaticDirectoryHandler(assets, true))
e.Router.GET("/icons/*", apis.StaticDirectoryHandler(site.Icons, false))
e.Router.Any("/*", apis.StaticDirectoryHandler(site.Dist, true))
}
return nil
})
@@ -186,8 +184,7 @@ func main() {
})
app.OnModelAfterCreate("system_stats").Add(func(e *core.ModelEvent) error {
createLongerRecords(e.Model.(*models.Record))
// createLongerRecords(e.Model.(*models.Record).OriginalCopy(), e.Model.(*models.Record))
createLongerRecords("system_stats", e.Model.(*models.Record))
return nil
})

View File

@@ -97,11 +97,11 @@ Because Beszel is built on top of PocketBase, you can use the normal PocketBase
## Security
The hub and agent communicate over SSH, so they do not need to be exposed to the internet.
The hub and agent communicate over SSH, so they don't need to be exposed to the internet. And the connection won't break if you put your own auth gateway, such as Authelia, in front of the hub.
When the hub is started for the first time, it generates an ED25519 key pair.
The agent's SSH server is configured to accept connections only using this key. It also does not provide a pty or accept any input, so it is not possible to execute commands on the agent.
The agent's SSH server is configured to accept connections only using this key. It does not provide a pty or accept any input, so it is not possible to execute commands on the agent even if your private key is compromised.
## FAQ / Troubleshooting

View File

@@ -5,6 +5,7 @@ import (
"fmt"
"log"
"math"
"reflect"
"time"
"github.com/pocketbase/dbx"
@@ -12,7 +13,7 @@ import (
"github.com/pocketbase/pocketbase/tools/types"
)
func createLongerRecords(shorterRecord *models.Record) {
func createLongerRecords(collectionName string, shorterRecord *models.Record) {
shorterRecordType := shorterRecord.Get("type").(string)
systemId := shorterRecord.Get("system").(string)
// fmt.Println("create longer records", "recordType", shorterRecordType, "systemId", systemId)
@@ -41,7 +42,7 @@ func createLongerRecords(shorterRecord *models.Record) {
longerRecordPeriod := time.Now().UTC().Add(timeAgo + 10*time.Second).Format("2006-01-02 15:04:05")
// check creation time of last 10m record
lastLongerRecord, err := app.Dao().FindFirstRecordByFilter(
"system_stats",
collectionName,
"type = {:type} && system = {:system} && created > {:created}",
dbx.Params{"type": longerRecordType, "system": systemId, "created": longerRecordPeriod},
)
@@ -53,7 +54,7 @@ func createLongerRecords(shorterRecord *models.Record) {
// get shorter records from the past x minutes
// shorterRecordPeriod := time.Now().UTC().Add(timeAgo + time.Second).Format("2006-01-02 15:04:05")
allShorterRecords, err := app.Dao().FindRecordsByFilter(
"system_stats",
collectionName,
"type = {:type} && system = {:system} && created > {:created}",
"-created",
-1,
@@ -67,7 +68,7 @@ func createLongerRecords(shorterRecord *models.Record) {
}
// average the shorter records and create 10m record
averagedStats := averageSystemStats(allShorterRecords)
collection, _ := app.Dao().FindCollectionByNameOrId("system_stats")
collection, _ := app.Dao().FindCollectionByNameOrId(collectionName)
tenMinRecord := models.NewRecord(collection)
tenMinRecord.Set("system", systemId)
tenMinRecord.Set("stats", averagedStats)
@@ -78,65 +79,66 @@ func createLongerRecords(shorterRecord *models.Record) {
}
// func averageSystemStats(records []*models.Record) SystemStats {
// numStats := len(records)
// firstStats := records[0].Get("stats").(SystemStats)
// sum := reflect.New(reflect.TypeOf(firstStats)).Elem()
// for _, record := range records {
// stats := record.Get("stats").(SystemStats)
// statValue := reflect.ValueOf(stats)
// for i := 0; i < statValue.NumField(); i++ {
// field := sum.Field(i)
// field.SetFloat(field.Float() + statValue.Field(i).Float())
// }
// }
// average := reflect.New(reflect.TypeOf(firstStats)).Elem()
// for i := 0; i < sum.NumField(); i++ {
// average.Field(i).SetFloat(twoDecimals(sum.Field(i).Float() / float64(numStats)))
// }
// return average.Interface().(SystemStats)
// }
// calculate the average of a list of SystemStats using reflection
func averageSystemStats(records []*models.Record) SystemStats {
var sum SystemStats
count := float64(len(records))
sum := reflect.New(reflect.TypeOf(SystemStats{})).Elem()
for _, record := range records {
var stats SystemStats
json.Unmarshal([]byte(record.Get("stats").(types.JsonRaw)), &stats)
sum.Cpu += stats.Cpu
sum.Mem += stats.Mem
sum.MemUsed += stats.MemUsed
sum.MemPct += stats.MemPct
sum.MemBuffCache += stats.MemBuffCache
sum.Disk += stats.Disk
sum.DiskUsed += stats.DiskUsed
sum.DiskPct += stats.DiskPct
sum.DiskRead += stats.DiskRead
sum.DiskWrite += stats.DiskWrite
sum.NetworkSent += stats.NetworkSent
sum.NetworkRecv += stats.NetworkRecv
statValue := reflect.ValueOf(stats)
for i := 0; i < statValue.NumField(); i++ {
field := sum.Field(i)
field.SetFloat(field.Float() + statValue.Field(i).Float())
}
}
return SystemStats{
Cpu: twoDecimals(sum.Cpu / count),
Mem: twoDecimals(sum.Mem / count),
MemUsed: twoDecimals(sum.MemUsed / count),
MemPct: twoDecimals(sum.MemPct / count),
MemBuffCache: twoDecimals(sum.MemBuffCache / count),
Disk: twoDecimals(sum.Disk / count),
DiskUsed: twoDecimals(sum.DiskUsed / count),
DiskPct: twoDecimals(sum.DiskPct / count),
DiskRead: twoDecimals(sum.DiskRead / count),
DiskWrite: twoDecimals(sum.DiskWrite / count),
NetworkSent: twoDecimals(sum.NetworkSent / count),
NetworkRecv: twoDecimals(sum.NetworkRecv / count),
average := reflect.New(reflect.TypeOf(SystemStats{})).Elem()
for i := 0; i < sum.NumField(); i++ {
average.Field(i).SetFloat(twoDecimals(sum.Field(i).Float() / count))
}
return average.Interface().(SystemStats)
}
// func averageSystemStats(records []*models.Record) SystemStats {
// var sum SystemStats
// count := float64(len(records))
// for _, record := range records {
// var stats SystemStats
// json.Unmarshal([]byte(record.Get("stats").(types.JsonRaw)), &stats)
// sum.Cpu += stats.Cpu
// sum.Mem += stats.Mem
// sum.MemUsed += stats.MemUsed
// sum.MemPct += stats.MemPct
// sum.MemBuffCache += stats.MemBuffCache
// sum.Disk += stats.Disk
// sum.DiskUsed += stats.DiskUsed
// sum.DiskPct += stats.DiskPct
// sum.DiskRead += stats.DiskRead
// sum.DiskWrite += stats.DiskWrite
// sum.NetworkSent += stats.NetworkSent
// sum.NetworkRecv += stats.NetworkRecv
// }
// return SystemStats{
// Cpu: twoDecimals(sum.Cpu / count),
// Mem: twoDecimals(sum.Mem / count),
// MemUsed: twoDecimals(sum.MemUsed / count),
// MemPct: twoDecimals(sum.MemPct / count),
// MemBuffCache: twoDecimals(sum.MemBuffCache / count),
// Disk: twoDecimals(sum.Disk / count),
// DiskUsed: twoDecimals(sum.DiskUsed / count),
// DiskPct: twoDecimals(sum.DiskPct / count),
// DiskRead: twoDecimals(sum.DiskRead / count),
// DiskWrite: twoDecimals(sum.DiskWrite / count),
// NetworkSent: twoDecimals(sum.NetworkSent / count),
// NetworkRecv: twoDecimals(sum.NetworkRecv / count),
// }
// }
/* Round float to two decimals */
func twoDecimals(value float64) float64 {
return math.Round(value*100) / 100
@@ -148,7 +150,7 @@ func deleteOldRecords(collection string, recordType string, timeLimit time.Durat
timeLimitStamp := time.Now().UTC().Add(timeLimit).Format("2006-01-02 15:04:05")
records, _ := app.Dao().FindRecordsByExpr(collection,
dbx.NewExp("type = {:type}", dbx.Params{"type": recordType}),
dbx.NewExp("created < {:created}", dbx.Params{"created": timeLimitStamp}),
dbx.NewExp("created > {:created}", dbx.Params{"created": timeLimitStamp}),
)
for _, record := range records {
if err := app.Dao().DeleteRecord(record); err != nil {

View File

@@ -1,17 +1,15 @@
// Package site handles the Beszel frontend embedding.
package site
import (
"embed"
"io/fs"
"github.com/labstack/echo/v5"
)
//go:embed all:dist
var assets embed.FS
func Assets() (fs.FS, error) {
return fs.Sub(assets, "dist")
}
var Dist = echo.MustSubFS(assets, "dist")
func Icons() (fs.FS, error) {
return fs.Sub(assets, "dist/icons")
}
var Icons = echo.MustSubFS(assets, "dist/icons")

View File

@@ -73,12 +73,16 @@ export default function ServerDetail({ name }: { name: string }) {
// get stats
useEffect(() => {
if (!server.id) {
if (!server.id || !chartTime) {
return
}
pb.collection<SystemStatsRecord>('system_stats')
.getFullList({
filter: `system="${server.id}" && created > "${getPbTimestamp('1h')}"`,
filter: pb.filter('system={:id} && created > {:created} && type={:type}', {
id: server.id,
created: getPbTimestamp(chartTime),
type: chartTimeData[chartTime].type,
}),
fields: 'created,stats',
sort: 'created',
})
@@ -86,7 +90,7 @@ export default function ServerDetail({ name }: { name: string }) {
// console.log('sctats', records)
setServerStats(records)
})
}, [server])
}, [server, chartTime])
useEffect(() => {
if (updatedSystem.id === server.id) {

View File

@@ -123,26 +123,31 @@ export function getPbTimestamp(timeString: ChartTimes) {
export const chartTimeData = {
'1h': {
type: '1m',
label: '1 hour',
format: (timestamp: string) => hourWithMinutes(timestamp),
getOffset: (endTime: Date) => timeHour.offset(endTime, -1),
},
'12h': {
type: '10m',
label: '12 hours',
format: (timestamp: string) => hourWithMinutes(timestamp),
getOffset: (endTime: Date) => timeHour.offset(endTime, -12),
},
'24h': {
type: '20m',
label: '24 hours',
format: (timestamp: string) => hourWithMinutes(timestamp),
getOffset: (endTime: Date) => timeHour.offset(endTime, -24),
},
'1w': {
type: '120m',
label: '1 week',
format: (timestamp: string) => formatDay(timestamp),
getOffset: (endTime: Date) => timeDay.offset(endTime, -7),
},
'30d': {
type: '480m',
label: '30 days',
format: (timestamp: string) => formatDay(timestamp),
getOffset: (endTime: Date) => timeDay.offset(endTime, -30),