mirror of
https://github.com/fankes/beszel.git
synced 2025-10-19 17:59:28 +08:00
longer record creation
This commit is contained in:
59
main.go
59
main.go
@@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
_ "beszel/migrations"
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/ed25519"
|
"crypto/ed25519"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@@ -8,7 +9,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
_ "monitor-site/migrations"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httputil"
|
"net/http/httputil"
|
||||||
"net/mail"
|
"net/mail"
|
||||||
@@ -82,39 +82,21 @@ func main() {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
// set up cron job to delete records older than 30 days
|
// set up cron jobs
|
||||||
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||||
scheduler := cron.New()
|
scheduler := cron.New()
|
||||||
scheduler.MustAdd("delete old records", "* 2 * * *", func() {
|
// delete records that are older than the display period
|
||||||
// log.Println("Deleting old records...")
|
scheduler.MustAdd("delete old records", "0 */2 * * *", func() {
|
||||||
// Get the current time
|
deleteOldRecords("system_stats", "1m", time.Hour)
|
||||||
now := time.Now().UTC()
|
deleteOldRecords("container_stats", "1m", time.Hour)
|
||||||
// Subtract one month
|
deleteOldRecords("system_stats", "10m", 12*time.Hour)
|
||||||
oneMonthAgo := now.AddDate(0, 0, -30)
|
deleteOldRecords("container_stats", "10m", 12*time.Hour)
|
||||||
// Format the time as a string
|
deleteOldRecords("system_stats", "20m", 24*time.Hour)
|
||||||
timeString := oneMonthAgo.Format("2006-01-02 15:04:05")
|
deleteOldRecords("container_stats", "20m", 24*time.Hour)
|
||||||
// collections to be cleaned
|
deleteOldRecords("system_stats", "120m", 7*24*time.Hour)
|
||||||
collections := []string{"system_stats", "container_stats"}
|
deleteOldRecords("container_stats", "120m", 7*24*time.Hour)
|
||||||
|
deleteOldRecords("system_stats", "480m", 30*24*time.Hour)
|
||||||
for _, collection := range collections {
|
deleteOldRecords("container_stats", "480m", 30*24*time.Hour)
|
||||||
records, err := app.Dao().FindRecordsByFilter(
|
|
||||||
collection,
|
|
||||||
fmt.Sprintf("created <= \"%s\"", timeString), // filter
|
|
||||||
"", // sort
|
|
||||||
-1, // limit
|
|
||||||
0, // offset
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// delete records
|
|
||||||
for _, record := range records {
|
|
||||||
if err := app.Dao().DeleteRecord(record); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
scheduler.Start()
|
scheduler.Start()
|
||||||
return nil
|
return nil
|
||||||
@@ -176,10 +158,9 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if server is set to pending (unpause), try to connect immediately
|
// if server is set to pending (unpause), try to connect immediately
|
||||||
// commenting out because we don't want to get off of the one min schedule
|
if newStatus == "pending" {
|
||||||
// if newStatus == "pending" {
|
go updateSystem(newRecord)
|
||||||
// go updateSystem(newRecord)
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
// alerts
|
// alerts
|
||||||
handleStatusAlerts(newStatus, oldRecord)
|
handleStatusAlerts(newStatus, oldRecord)
|
||||||
@@ -193,6 +174,12 @@ func main() {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
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))
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
if err := app.Start(); err != nil {
|
if err := app.Start(); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@@ -44,13 +44,13 @@ The hub and agent are distributed as single binary files, as well as docker imag
|
|||||||
|
|
||||||
## Environment Variables
|
## Environment Variables
|
||||||
|
|
||||||
## Hub
|
### Hub
|
||||||
|
|
||||||
| Name | Default | Description |
|
| Name | Default | Description |
|
||||||
| ----------------------- | ------- | -------------------------------- |
|
| ----------------------- | ------- | -------------------------------- |
|
||||||
| `DISABLE_PASSWORD_AUTH` | false | Disables password authentication |
|
| `DISABLE_PASSWORD_AUTH` | false | Disables password authentication |
|
||||||
|
|
||||||
## Agent
|
### Agent
|
||||||
|
|
||||||
| Name | Default | Description |
|
| Name | Default | Description |
|
||||||
| ------------ | ------- | ------------------------------------------------ |
|
| ------------ | ------- | ------------------------------------------------ |
|
||||||
|
158
records.go
Normal file
158
records.go
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"math"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/pocketbase/dbx"
|
||||||
|
"github.com/pocketbase/pocketbase/models"
|
||||||
|
"github.com/pocketbase/pocketbase/tools/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func createLongerRecords(shorterRecord *models.Record) {
|
||||||
|
shorterRecordType := shorterRecord.Get("type").(string)
|
||||||
|
systemId := shorterRecord.Get("system").(string)
|
||||||
|
// fmt.Println("create longer records", "recordType", shorterRecordType, "systemId", systemId)
|
||||||
|
var longerRecordType string
|
||||||
|
var timeAgo time.Duration
|
||||||
|
var expectedShorterRecords int
|
||||||
|
switch shorterRecordType {
|
||||||
|
case "1m":
|
||||||
|
longerRecordType = "10m"
|
||||||
|
timeAgo = -10 * time.Minute
|
||||||
|
expectedShorterRecords = 10
|
||||||
|
case "10m":
|
||||||
|
longerRecordType = "20m"
|
||||||
|
timeAgo = -20 * time.Minute
|
||||||
|
expectedShorterRecords = 2
|
||||||
|
case "20m":
|
||||||
|
longerRecordType = "120m"
|
||||||
|
timeAgo = -120 * time.Minute
|
||||||
|
expectedShorterRecords = 6
|
||||||
|
default:
|
||||||
|
longerRecordType = "480m"
|
||||||
|
timeAgo = -480 * time.Minute
|
||||||
|
expectedShorterRecords = 4
|
||||||
|
}
|
||||||
|
|
||||||
|
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",
|
||||||
|
"type = {:type} && system = {:system} && created > {:created}",
|
||||||
|
dbx.Params{"type": longerRecordType, "system": systemId, "created": longerRecordPeriod},
|
||||||
|
)
|
||||||
|
// return if longer record exists
|
||||||
|
if err == nil || lastLongerRecord != nil {
|
||||||
|
// log.Println("longer record found. returning")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 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",
|
||||||
|
"type = {:type} && system = {:system} && created > {:created}",
|
||||||
|
"-created",
|
||||||
|
-1,
|
||||||
|
0,
|
||||||
|
dbx.Params{"type": shorterRecordType, "system": systemId, "created": longerRecordPeriod},
|
||||||
|
)
|
||||||
|
// return if not enough shorter records
|
||||||
|
if err != nil || len(allShorterRecords) < expectedShorterRecords {
|
||||||
|
// log.Println("not enough shorter records. returning")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// average the shorter records and create 10m record
|
||||||
|
averagedStats := averageSystemStats(allShorterRecords)
|
||||||
|
collection, _ := app.Dao().FindCollectionByNameOrId("system_stats")
|
||||||
|
tenMinRecord := models.NewRecord(collection)
|
||||||
|
tenMinRecord.Set("system", systemId)
|
||||||
|
tenMinRecord.Set("stats", averagedStats)
|
||||||
|
tenMinRecord.Set("type", longerRecordType)
|
||||||
|
if err := app.Dao().SaveRecord(tenMinRecord); err != nil {
|
||||||
|
fmt.Println("failed to save longer record", "err", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
// }
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Delete records of specified collection and type that are older than timeLimit */
|
||||||
|
func deleteOldRecords(collection string, recordType string, timeLimit time.Duration) {
|
||||||
|
log.Println("Deleting old", recordType, "records...")
|
||||||
|
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}),
|
||||||
|
)
|
||||||
|
for _, record := range records {
|
||||||
|
if err := app.Dao().DeleteRecord(record); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -5,6 +5,8 @@ import {
|
|||||||
DatabaseBackupIcon,
|
DatabaseBackupIcon,
|
||||||
Github,
|
Github,
|
||||||
LayoutDashboard,
|
LayoutDashboard,
|
||||||
|
LockKeyholeIcon,
|
||||||
|
LogsIcon,
|
||||||
MailIcon,
|
MailIcon,
|
||||||
Server,
|
Server,
|
||||||
} from 'lucide-react'
|
} from 'lucide-react'
|
||||||
@@ -97,6 +99,15 @@ export default function CommandPalette() {
|
|||||||
<span>PocketBase</span>
|
<span>PocketBase</span>
|
||||||
<CommandShortcut>Admin</CommandShortcut>
|
<CommandShortcut>Admin</CommandShortcut>
|
||||||
</CommandItem>
|
</CommandItem>
|
||||||
|
<CommandItem
|
||||||
|
onSelect={() => {
|
||||||
|
window.location.href = '/_/#/logs'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<LogsIcon className="mr-2 h-4 w-4" />
|
||||||
|
<span>Logs</span>
|
||||||
|
<CommandShortcut>Admin</CommandShortcut>
|
||||||
|
</CommandItem>
|
||||||
<CommandItem
|
<CommandItem
|
||||||
onSelect={() => {
|
onSelect={() => {
|
||||||
window.location.href = '/_/#/settings/backups'
|
window.location.href = '/_/#/settings/backups'
|
||||||
@@ -106,6 +117,16 @@ export default function CommandPalette() {
|
|||||||
<span>Database backups</span>
|
<span>Database backups</span>
|
||||||
<CommandShortcut>Admin</CommandShortcut>
|
<CommandShortcut>Admin</CommandShortcut>
|
||||||
</CommandItem>
|
</CommandItem>
|
||||||
|
<CommandItem
|
||||||
|
keywords={['oauth', 'oicd']}
|
||||||
|
onSelect={() => {
|
||||||
|
window.location.href = '/_/#/settings/auth-providers'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<LockKeyholeIcon className="mr-2 h-4 w-4" />
|
||||||
|
<span>Auth Providers</span>
|
||||||
|
<CommandShortcut>Admin</CommandShortcut>
|
||||||
|
</CommandItem>
|
||||||
<CommandItem
|
<CommandItem
|
||||||
keywords={['email']}
|
keywords={['email']}
|
||||||
onSelect={() => {
|
onSelect={() => {
|
||||||
|
Reference in New Issue
Block a user