diff --git a/internal/hub/probes.go b/internal/hub/probes.go index bea0b2e1..a7d2e0d4 100644 --- a/internal/hub/probes.go +++ b/internal/hub/probes.go @@ -1,9 +1,12 @@ package hub import ( + "time" + "github.com/henrygd/beszel/internal/entities/probe" "github.com/henrygd/beszel/internal/hub/systems" "github.com/pocketbase/pocketbase/core" + "github.com/pocketbase/pocketbase/tools/types" ) // generateProbeID creates a stable hash ID for a probe based on its configuration and the system it belongs to. @@ -107,11 +110,14 @@ func probeConfigFromRecord(record *core.Record) *probe.Config { // setProbeResultFields stores the latest probe result values on the record. func setProbeResultFields(record *core.Record, result probe.Result) { + now := time.Now().UTC() + nowString := now.Format(types.DefaultDateLayout) record.Set("res", result.Get(0)) record.Set("resAvg1h", result.Get(1)) record.Set("resMin1h", result.Get(2)) record.Set("resMax1h", result.Get(3)) record.Set("loss1h", result.Get(4)) + record.Set("updated", nowString) } // copyProbeToNewRecord creates a new record with the same field values as the old one. diff --git a/internal/hub/systems/system.go b/internal/hub/systems/system.go index f09da46a..d378addb 100644 --- a/internal/hub/systems/system.go +++ b/internal/hub/systems/system.go @@ -327,12 +327,13 @@ func updateNetworkProbesRecords(app core.App, data map[string]probe.Result, syst return !strings.Contains(filterQuery, "system") || strings.Contains(filterQuery, systemId) }) + now := time.Now().UTC() + nowMilli := now.UnixMilli() + nowString := now.Format(types.DefaultDateLayout) var db dbx.Builder - var nowString string var updateQuery *dbx.Query if !realtimeActive { db = app.DB() - nowString = time.Now().UTC().Format(types.DefaultDateLayout) sql := fmt.Sprintf("UPDATE %s SET res={:res}, resMin1h={:resMin1h}, resMax1h={:resMax1h}, resAvg1h={:resAvg1h}, loss1h={:loss1h}, updated={:updated} WHERE id={:id}", collectionName) updateQuery = db.NewQuery(sql) } @@ -349,6 +350,7 @@ func updateNetworkProbesRecords(app core.App, data map[string]probe.Result, syst record.Set("resMin1h", values.Get(2)) record.Set("resMax1h", values.Get(3)) record.Set("loss1h", values.Get(4)) + record.Set("updated", nowString) err = app.SaveNoValidate(record) } default: @@ -375,6 +377,7 @@ func updateNetworkProbesRecords(app core.App, data map[string]probe.Result, syst record.Set("system", systemId) record.Set("stats", data) record.Set("type", "1m") + record.Set("created", nowMilli) err = app.SaveNoValidate(record) default: var statsJson types.JSONRaw @@ -384,7 +387,7 @@ func updateNetworkProbesRecords(app core.App, data map[string]probe.Result, syst "system": systemId, "stats": statsJson, "type": "1m", - "created": nowString, + "created": nowMilli, }).Execute() } } diff --git a/internal/site/src/components/network-probes-table/network-probes-columns.tsx b/internal/site/src/components/network-probes-table/network-probes-columns.tsx index f5c5a5e0..cec104a8 100644 --- a/internal/site/src/components/network-probes-table/network-probes-columns.tsx +++ b/internal/site/src/components/network-probes-table/network-probes-columns.tsx @@ -226,7 +226,10 @@ export function getProbeColumns( header: ({ column }) => , cell: ({ getValue }) => { const timestamp = getValue() as number - return {hourWithSeconds(new Date(timestamp).toISOString())} + if (!timestamp) { + return - + } + return {hourWithSeconds(timestamp)} }, }, { diff --git a/internal/site/src/components/routes/system/chart-data.ts b/internal/site/src/components/routes/system/chart-data.ts index 041aaa94..d8691b1c 100644 --- a/internal/site/src/components/routes/system/chart-data.ts +++ b/internal/site/src/components/routes/system/chart-data.ts @@ -48,13 +48,14 @@ export async function getStats { const lastCached = cachedStats?.at(-1)?.created as number return await pb.collection(collection).getFullList({ filter: pb.filter("system={:id} && created > {:created} && type={:type}", { id: systemId, - created: getPbTimestamp(chartTime, lastCached ? new Date(lastCached + 1000) : undefined), + created: getPbTimestamp(chartTime, lastCached ? new Date(lastCached + 1000) : undefined, createdIsNumber), type: chartTimeData[chartTime].type, }), fields: "created,stats", diff --git a/internal/site/src/lib/api.ts b/internal/site/src/lib/api.ts index 79f4b127..edc8beb6 100644 --- a/internal/site/src/lib/api.ts +++ b/internal/site/src/lib/api.ts @@ -54,8 +54,11 @@ export async function updateUserSettings() { } } -export function getPbTimestamp(timeString: ChartTimes, d?: Date) { +export function getPbTimestamp(timeString: ChartTimes, d?: Date, createdIsNumber?: boolean) { d ||= chartTimeData[timeString].getOffset(new Date()) + if (createdIsNumber) { + return d.getTime() + } const year = d.getUTCFullYear() const month = String(d.getUTCMonth() + 1).padStart(2, "0") const day = String(d.getUTCDate()).padStart(2, "0") diff --git a/internal/site/src/lib/use-network-probes.ts b/internal/site/src/lib/use-network-probes.ts index 99ed95fa..06406a5b 100644 --- a/internal/site/src/lib/use-network-probes.ts +++ b/internal/site/src/lib/use-network-probes.ts @@ -196,7 +196,7 @@ export function useNetworkProbesData(props: UseNetworkProbesProps) { } } - getStats("network_probe_stats", systemId, chartTime, cachedProbeStats).then( + getStats("network_probe_stats", systemId, chartTime, cachedProbeStats, true).then( (probeStats) => { // If another request has been made since this one, ignore the results if (requestId !== statsRequestId.current) { diff --git a/internal/site/src/lib/utils.ts b/internal/site/src/lib/utils.ts index d7900bcc..ee6ef525 100644 --- a/internal/site/src/lib/utils.ts +++ b/internal/site/src/lib/utils.ts @@ -72,7 +72,7 @@ export const formatShortDate = (timestamp: string) => { return shortDateFormatter.format(new Date(timestamp)) } -export const hourWithSeconds = (timestamp: string) => { +export const hourWithSeconds = (timestamp: string | number) => { return hourWithSecondsFormatter.format(new Date(timestamp)) } @@ -111,17 +111,18 @@ export const updateFavicon = (() => { - ${downCount > 0 && - ` + ${ + downCount > 0 && + ` ${downCount} ` - } + } ` const blob = new Blob([svg], { type: "image/svg+xml" }) const url = URL.createObjectURL(blob) - ; (document.querySelector("link[rel='icon']") as HTMLLinkElement).href = url + ;(document.querySelector("link[rel='icon']") as HTMLLinkElement).href = url } })() @@ -365,12 +366,12 @@ export function formatDuration( .join(" ") } -/** Parse semver string into major, minor, and patch numbers +/** Parse semver string into major, minor, and patch numbers * @example * const semVer = "1.2.3" * const { major, minor, patch } = parseSemVer(semVer) * console.log(major, minor, patch) // 1, 2, 3 -*/ + */ export const parseSemVer = (semVer = ""): SemVer => { // if (semVer.startsWith("v")) { // semVer = semVer.slice(1) @@ -452,7 +453,12 @@ export function secondsToString(seconds: number, unit: "hour" | "minute" | "day" const countString = count.toLocaleString() switch (unit) { case "minute": - return plural(count, { one: `${countString} minute`, few: `${countString} minutes`, many: `${countString} minutes`, other: `${countString} minutes` }) + return plural(count, { + one: `${countString} minute`, + few: `${countString} minutes`, + many: `${countString} minutes`, + other: `${countString} minutes`, + }) case "hour": return plural(count, { one: `${countString} hour`, other: `${countString} hours` }) case "day": @@ -469,4 +475,4 @@ export function secondsToUptimeString(seconds: number): string { } else { return secondsToString(seconds, "day") } -} \ No newline at end of file +}