mirror of
https://github.com/henrygd/beszel.git
synced 2026-04-05 12:31:49 +02:00
Compare commits
33 Commits
283fa9d5c2
...
1750-raid-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
02c1a0c13d | ||
|
|
69fdcb36ab | ||
|
|
b91eb6de40 | ||
|
|
ec69f6c6e0 | ||
|
|
a86cb91e07 | ||
|
|
004841717a | ||
|
|
096296ba7b | ||
|
|
b012df5669 | ||
|
|
12545b4b6d | ||
|
|
9e2296452b | ||
|
|
ac79860d4a | ||
|
|
e13a99fdac | ||
|
|
4cfb2a86ad | ||
|
|
191f25f6e0 | ||
|
|
aa8b3711d7 | ||
|
|
1fb0b25988 | ||
|
|
04600d83cc | ||
|
|
5d8906c9b2 | ||
|
|
daac287b9d | ||
|
|
d526ea61a9 | ||
|
|
79616e1662 | ||
|
|
01e8bdf040 | ||
|
|
1e3a44e05d | ||
|
|
311095cfdd | ||
|
|
4869c834bb | ||
|
|
e1c1e97f0a | ||
|
|
f6b2824ccc | ||
|
|
f17ffc21b8 | ||
|
|
f792f9b102 | ||
|
|
1def7d8d3a | ||
|
|
ef92b254bf | ||
|
|
10d853c004 | ||
|
|
cdfd116da0 |
1
Makefile
1
Makefile
@@ -51,7 +51,6 @@ clean:
|
||||
lint:
|
||||
golangci-lint run
|
||||
|
||||
test: export GOEXPERIMENT=synctest
|
||||
test:
|
||||
go test -tags=testing ./...
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build testing
|
||||
// +build testing
|
||||
|
||||
package agent
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build testing
|
||||
// +build testing
|
||||
|
||||
package agent
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build testing
|
||||
// +build testing
|
||||
|
||||
package agent
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"log/slog"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
@@ -91,8 +91,8 @@ func (c *ConnectionManager) Start(serverOptions ServerOptions) error {
|
||||
c.eventChan = make(chan ConnectionEvent, 1)
|
||||
|
||||
// signal handling for shutdown
|
||||
sigChan := make(chan os.Signal, 1)
|
||||
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
|
||||
sigCtx, stopSignals := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
||||
defer stopSignals()
|
||||
|
||||
c.startWsTicker()
|
||||
c.connect()
|
||||
@@ -109,8 +109,8 @@ func (c *ConnectionManager) Start(serverOptions ServerOptions) error {
|
||||
_ = c.startWebSocketConnection()
|
||||
case <-healthTicker:
|
||||
_ = health.Update()
|
||||
case <-sigChan:
|
||||
slog.Info("Shutting down")
|
||||
case <-sigCtx.Done():
|
||||
slog.Info("Shutting down", "cause", context.Cause(sigCtx))
|
||||
_ = c.agent.StopServer()
|
||||
c.closeWebSocket()
|
||||
return health.CleanUp()
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build testing
|
||||
// +build testing
|
||||
|
||||
package agent
|
||||
|
||||
|
||||
@@ -14,10 +14,10 @@ var lastPerCoreCpuTimes = make(map[uint16][]cpu.TimesStat)
|
||||
// init initializes the CPU monitoring by storing the initial CPU times
|
||||
// for the default 60-second cache interval.
|
||||
func init() {
|
||||
if times, err := cpu.Times(false); err == nil {
|
||||
if times, err := cpu.Times(false); err == nil && len(times) > 0 {
|
||||
lastCpuTimes[60000] = times[0]
|
||||
}
|
||||
if perCoreTimes, err := cpu.Times(true); err == nil {
|
||||
if perCoreTimes, err := cpu.Times(true); err == nil && len(perCoreTimes) > 0 {
|
||||
lastPerCoreCpuTimes[60000] = perCoreTimes
|
||||
}
|
||||
}
|
||||
@@ -89,10 +89,7 @@ func getPerCoreCpuUsage(cacheTimeMs uint16) (system.Uint8Slice, error) {
|
||||
lastTimes := lastPerCoreCpuTimes[cacheTimeMs]
|
||||
|
||||
// Limit to the number of cores available in both samples
|
||||
length := len(perCoreTimes)
|
||||
if len(lastTimes) < length {
|
||||
length = len(lastTimes)
|
||||
}
|
||||
length := min(len(lastTimes), len(perCoreTimes))
|
||||
|
||||
usage := make([]uint8, length)
|
||||
for i := 0; i < length; i++ {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build testing
|
||||
// +build testing
|
||||
|
||||
package agent
|
||||
|
||||
|
||||
207
agent/disk.go
207
agent/disk.go
@@ -78,14 +78,21 @@ func (a *Agent) initializeDiskInfo() {
|
||||
if _, exists := a.fsStats[key]; !exists {
|
||||
if root {
|
||||
slog.Info("Detected root device", "name", key)
|
||||
// Check if root device is in /proc/diskstats. Do not guess a
|
||||
// fallback device for root: that can misattribute root I/O to a
|
||||
// different disk while usage remains tied to root mountpoint.
|
||||
// Try to map root device to a diskIoCounters entry. First
|
||||
// checks for an exact key match, then uses findIoDevice for
|
||||
// normalized / prefix-based matching (e.g. nda0p2 → nda0),
|
||||
// and finally falls back to the FILESYSTEM env var.
|
||||
if _, ioMatch = diskIoCounters[key]; !ioMatch {
|
||||
if matchedKey, match := findIoDevice(filesystem, diskIoCounters); match {
|
||||
if matchedKey, match := findIoDevice(key, diskIoCounters); match {
|
||||
key = matchedKey
|
||||
ioMatch = true
|
||||
} else {
|
||||
} else if filesystem != "" {
|
||||
if matchedKey, match := findIoDevice(filesystem, diskIoCounters); match {
|
||||
key = matchedKey
|
||||
ioMatch = true
|
||||
}
|
||||
}
|
||||
if !ioMatch {
|
||||
slog.Warn("Root I/O unmapped; set FILESYSTEM", "device", device, "mountpoint", mountpoint)
|
||||
}
|
||||
}
|
||||
@@ -114,20 +121,28 @@ func (a *Agent) initializeDiskInfo() {
|
||||
// Use FILESYSTEM env var to find root filesystem
|
||||
if filesystem != "" {
|
||||
for _, p := range partitions {
|
||||
if strings.HasSuffix(p.Device, filesystem) || p.Mountpoint == filesystem {
|
||||
if filesystemMatchesPartitionSetting(filesystem, p) {
|
||||
addFsStat(p.Device, p.Mountpoint, true)
|
||||
hasRoot = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !hasRoot {
|
||||
slog.Warn("Partition details not found", "filesystem", filesystem)
|
||||
// FILESYSTEM may name a physical disk absent from partitions (e.g.
|
||||
// ZFS lists dataset paths like zroot/ROOT/default, not block devices).
|
||||
// Try matching directly against diskIoCounters.
|
||||
if ioKey, match := findIoDevice(filesystem, diskIoCounters); match {
|
||||
a.fsStats[ioKey] = &system.FsStats{Root: true, Mountpoint: rootMountPoint}
|
||||
hasRoot = true
|
||||
} else {
|
||||
slog.Warn("Partition details not found", "filesystem", filesystem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add EXTRA_FILESYSTEMS env var values to fsStats
|
||||
if extraFilesystems, exists := GetEnv("EXTRA_FILESYSTEMS"); exists {
|
||||
for _, fsEntry := range strings.Split(extraFilesystems, ",") {
|
||||
for fsEntry := range strings.SplitSeq(extraFilesystems, ",") {
|
||||
// Parse custom name from format: device__customname
|
||||
fs, customName := parseFilesystemEntry(fsEntry)
|
||||
|
||||
@@ -187,28 +202,180 @@ func (a *Agent) initializeDiskInfo() {
|
||||
}
|
||||
}
|
||||
|
||||
// If no root filesystem set, use fallback
|
||||
// If no root filesystem set, try the most active I/O device as a last
|
||||
// resort (e.g. ZFS where dataset names are unrelated to disk names).
|
||||
if !hasRoot {
|
||||
rootKey := filepath.Base(rootMountPoint)
|
||||
if _, exists := a.fsStats[rootKey]; exists {
|
||||
rootKey = "root"
|
||||
rootKey := mostActiveIoDevice(diskIoCounters)
|
||||
if rootKey != "" {
|
||||
slog.Warn("Using most active device for root I/O; set FILESYSTEM to override", "device", rootKey)
|
||||
} else {
|
||||
rootKey = filepath.Base(rootMountPoint)
|
||||
if _, exists := a.fsStats[rootKey]; exists {
|
||||
rootKey = "root"
|
||||
}
|
||||
slog.Warn("Root I/O device not detected; set FILESYSTEM to override")
|
||||
}
|
||||
slog.Warn("Root device not detected; root I/O disabled", "mountpoint", rootMountPoint)
|
||||
a.fsStats[rootKey] = &system.FsStats{Root: true, Mountpoint: rootMountPoint}
|
||||
}
|
||||
|
||||
a.pruneDuplicateRootExtraFilesystems()
|
||||
a.initializeDiskIoStats(diskIoCounters)
|
||||
}
|
||||
|
||||
// Returns matching device from /proc/diskstats.
|
||||
// bool is true if a match was found.
|
||||
func findIoDevice(filesystem string, diskIoCounters map[string]disk.IOCountersStat) (string, bool) {
|
||||
for _, d := range diskIoCounters {
|
||||
if d.Name == filesystem || (d.Label != "" && d.Label == filesystem) {
|
||||
return d.Name, true
|
||||
// Removes extra filesystems that mirror root usage (https://github.com/henrygd/beszel/issues/1428).
|
||||
func (a *Agent) pruneDuplicateRootExtraFilesystems() {
|
||||
var rootMountpoint string
|
||||
for _, stats := range a.fsStats {
|
||||
if stats != nil && stats.Root {
|
||||
rootMountpoint = stats.Mountpoint
|
||||
break
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
if rootMountpoint == "" {
|
||||
return
|
||||
}
|
||||
rootUsage, err := disk.Usage(rootMountpoint)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for name, stats := range a.fsStats {
|
||||
if stats == nil || stats.Root {
|
||||
continue
|
||||
}
|
||||
extraUsage, err := disk.Usage(stats.Mountpoint)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if hasSameDiskUsage(rootUsage, extraUsage) {
|
||||
slog.Info("Ignoring duplicate FS", "name", name, "mount", stats.Mountpoint)
|
||||
delete(a.fsStats, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// hasSameDiskUsage compares root/extra usage with a small byte tolerance.
|
||||
func hasSameDiskUsage(a, b *disk.UsageStat) bool {
|
||||
if a == nil || b == nil || a.Total == 0 || b.Total == 0 {
|
||||
return false
|
||||
}
|
||||
// Allow minor drift between sequential disk usage calls.
|
||||
const toleranceBytes uint64 = 16 * 1024 * 1024
|
||||
return withinUsageTolerance(a.Total, b.Total, toleranceBytes) &&
|
||||
withinUsageTolerance(a.Used, b.Used, toleranceBytes)
|
||||
}
|
||||
|
||||
// withinUsageTolerance reports whether two byte values differ by at most tolerance.
|
||||
func withinUsageTolerance(a, b, tolerance uint64) bool {
|
||||
if a >= b {
|
||||
return a-b <= tolerance
|
||||
}
|
||||
return b-a <= tolerance
|
||||
}
|
||||
|
||||
type ioMatchCandidate struct {
|
||||
name string
|
||||
bytes uint64
|
||||
ops uint64
|
||||
}
|
||||
|
||||
// findIoDevice prefers exact device/label matches, then falls back to a
|
||||
// prefix-related candidate with the highest recent activity.
|
||||
func findIoDevice(filesystem string, diskIoCounters map[string]disk.IOCountersStat) (string, bool) {
|
||||
filesystem = normalizeDeviceName(filesystem)
|
||||
if filesystem == "" {
|
||||
return "", false
|
||||
}
|
||||
|
||||
candidates := []ioMatchCandidate{}
|
||||
|
||||
for _, d := range diskIoCounters {
|
||||
if normalizeDeviceName(d.Name) == filesystem || (d.Label != "" && normalizeDeviceName(d.Label) == filesystem) {
|
||||
return d.Name, true
|
||||
}
|
||||
if prefixRelated(normalizeDeviceName(d.Name), filesystem) ||
|
||||
(d.Label != "" && prefixRelated(normalizeDeviceName(d.Label), filesystem)) {
|
||||
candidates = append(candidates, ioMatchCandidate{
|
||||
name: d.Name,
|
||||
bytes: d.ReadBytes + d.WriteBytes,
|
||||
ops: d.ReadCount + d.WriteCount,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if len(candidates) == 0 {
|
||||
return "", false
|
||||
}
|
||||
|
||||
best := candidates[0]
|
||||
for _, c := range candidates[1:] {
|
||||
if c.bytes > best.bytes ||
|
||||
(c.bytes == best.bytes && c.ops > best.ops) ||
|
||||
(c.bytes == best.bytes && c.ops == best.ops && c.name < best.name) {
|
||||
best = c
|
||||
}
|
||||
}
|
||||
|
||||
slog.Info("Using disk I/O fallback", "requested", filesystem, "selected", best.name)
|
||||
return best.name, true
|
||||
}
|
||||
|
||||
// mostActiveIoDevice returns the device with the highest I/O activity,
|
||||
// or "" if diskIoCounters is empty.
|
||||
func mostActiveIoDevice(diskIoCounters map[string]disk.IOCountersStat) string {
|
||||
var best ioMatchCandidate
|
||||
for _, d := range diskIoCounters {
|
||||
c := ioMatchCandidate{
|
||||
name: d.Name,
|
||||
bytes: d.ReadBytes + d.WriteBytes,
|
||||
ops: d.ReadCount + d.WriteCount,
|
||||
}
|
||||
if best.name == "" || c.bytes > best.bytes ||
|
||||
(c.bytes == best.bytes && c.ops > best.ops) ||
|
||||
(c.bytes == best.bytes && c.ops == best.ops && c.name < best.name) {
|
||||
best = c
|
||||
}
|
||||
}
|
||||
return best.name
|
||||
}
|
||||
|
||||
// prefixRelated reports whether either identifier is a prefix of the other.
|
||||
func prefixRelated(a, b string) bool {
|
||||
if a == "" || b == "" || a == b {
|
||||
return false
|
||||
}
|
||||
return strings.HasPrefix(a, b) || strings.HasPrefix(b, a)
|
||||
}
|
||||
|
||||
// filesystemMatchesPartitionSetting checks whether a FILESYSTEM env var value
|
||||
// matches a partition by mountpoint, exact device name, or prefix relationship
|
||||
// (e.g. FILESYSTEM=ada0 matches partition /dev/ada0p2).
|
||||
func filesystemMatchesPartitionSetting(filesystem string, p disk.PartitionStat) bool {
|
||||
filesystem = strings.TrimSpace(filesystem)
|
||||
if filesystem == "" {
|
||||
return false
|
||||
}
|
||||
if p.Mountpoint == filesystem {
|
||||
return true
|
||||
}
|
||||
|
||||
fsName := normalizeDeviceName(filesystem)
|
||||
partName := normalizeDeviceName(p.Device)
|
||||
if fsName == "" || partName == "" {
|
||||
return false
|
||||
}
|
||||
if fsName == partName {
|
||||
return true
|
||||
}
|
||||
return prefixRelated(partName, fsName)
|
||||
}
|
||||
|
||||
// normalizeDeviceName canonicalizes device strings for comparisons.
|
||||
func normalizeDeviceName(value string) string {
|
||||
name := filepath.Base(strings.TrimSpace(value))
|
||||
if name == "." {
|
||||
return ""
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
// Sets start values for disk I/O stats.
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build testing
|
||||
// +build testing
|
||||
|
||||
package agent
|
||||
|
||||
@@ -117,7 +116,7 @@ func TestFindIoDevice(t *testing.T) {
|
||||
assert.Equal(t, "sda", device)
|
||||
})
|
||||
|
||||
t.Run("returns no fallback when not found", func(t *testing.T) {
|
||||
t.Run("returns no match when not found", func(t *testing.T) {
|
||||
ioCounters := map[string]disk.IOCountersStat{
|
||||
"sda": {Name: "sda"},
|
||||
"sdb": {Name: "sdb"},
|
||||
@@ -127,6 +126,106 @@ func TestFindIoDevice(t *testing.T) {
|
||||
assert.False(t, ok)
|
||||
assert.Equal(t, "", device)
|
||||
})
|
||||
|
||||
t.Run("uses uncertain unique prefix fallback", func(t *testing.T) {
|
||||
ioCounters := map[string]disk.IOCountersStat{
|
||||
"nvme0n1": {Name: "nvme0n1"},
|
||||
"sda": {Name: "sda"},
|
||||
}
|
||||
|
||||
device, ok := findIoDevice("nvme0n1p2", ioCounters)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, "nvme0n1", device)
|
||||
})
|
||||
|
||||
t.Run("uses dominant activity when prefix matches are ambiguous", func(t *testing.T) {
|
||||
ioCounters := map[string]disk.IOCountersStat{
|
||||
"sda": {Name: "sda", ReadBytes: 5000, WriteBytes: 5000, ReadCount: 100, WriteCount: 100},
|
||||
"sdb": {Name: "sdb", ReadBytes: 1000, WriteBytes: 1000, ReadCount: 50, WriteCount: 50},
|
||||
}
|
||||
|
||||
device, ok := findIoDevice("sd", ioCounters)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, "sda", device)
|
||||
})
|
||||
|
||||
t.Run("uses highest activity when ambiguous without dominance", func(t *testing.T) {
|
||||
ioCounters := map[string]disk.IOCountersStat{
|
||||
"sda": {Name: "sda", ReadBytes: 3000, WriteBytes: 3000, ReadCount: 50, WriteCount: 50},
|
||||
"sdb": {Name: "sdb", ReadBytes: 2500, WriteBytes: 2500, ReadCount: 40, WriteCount: 40},
|
||||
}
|
||||
|
||||
device, ok := findIoDevice("sd", ioCounters)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, "sda", device)
|
||||
})
|
||||
|
||||
t.Run("matches /dev/-prefixed partition to parent disk", func(t *testing.T) {
|
||||
ioCounters := map[string]disk.IOCountersStat{
|
||||
"nda0": {Name: "nda0", ReadBytes: 1000, WriteBytes: 1000},
|
||||
}
|
||||
|
||||
device, ok := findIoDevice("/dev/nda0p2", ioCounters)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, "nda0", device)
|
||||
})
|
||||
|
||||
t.Run("uses deterministic name tie-breaker", func(t *testing.T) {
|
||||
ioCounters := map[string]disk.IOCountersStat{
|
||||
"sdb": {Name: "sdb", ReadBytes: 2000, WriteBytes: 2000, ReadCount: 10, WriteCount: 10},
|
||||
"sda": {Name: "sda", ReadBytes: 2000, WriteBytes: 2000, ReadCount: 10, WriteCount: 10},
|
||||
}
|
||||
|
||||
device, ok := findIoDevice("sd", ioCounters)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, "sda", device)
|
||||
})
|
||||
}
|
||||
|
||||
func TestFilesystemMatchesPartitionSetting(t *testing.T) {
|
||||
p := disk.PartitionStat{Device: "/dev/ada0p2", Mountpoint: "/"}
|
||||
|
||||
t.Run("matches mountpoint setting", func(t *testing.T) {
|
||||
assert.True(t, filesystemMatchesPartitionSetting("/", p))
|
||||
})
|
||||
|
||||
t.Run("matches exact partition setting", func(t *testing.T) {
|
||||
assert.True(t, filesystemMatchesPartitionSetting("ada0p2", p))
|
||||
assert.True(t, filesystemMatchesPartitionSetting("/dev/ada0p2", p))
|
||||
})
|
||||
|
||||
t.Run("matches prefix-style parent setting", func(t *testing.T) {
|
||||
assert.True(t, filesystemMatchesPartitionSetting("ada0", p))
|
||||
assert.True(t, filesystemMatchesPartitionSetting("/dev/ada0", p))
|
||||
})
|
||||
|
||||
t.Run("does not match unrelated device", func(t *testing.T) {
|
||||
assert.False(t, filesystemMatchesPartitionSetting("sda", p))
|
||||
assert.False(t, filesystemMatchesPartitionSetting("nvme0n1", p))
|
||||
assert.False(t, filesystemMatchesPartitionSetting("", p))
|
||||
})
|
||||
}
|
||||
|
||||
func TestMostActiveIoDevice(t *testing.T) {
|
||||
t.Run("returns most active device", func(t *testing.T) {
|
||||
ioCounters := map[string]disk.IOCountersStat{
|
||||
"nda0": {Name: "nda0", ReadBytes: 5000, WriteBytes: 5000, ReadCount: 100, WriteCount: 100},
|
||||
"nda1": {Name: "nda1", ReadBytes: 1000, WriteBytes: 1000, ReadCount: 50, WriteCount: 50},
|
||||
}
|
||||
assert.Equal(t, "nda0", mostActiveIoDevice(ioCounters))
|
||||
})
|
||||
|
||||
t.Run("uses deterministic tie-breaker", func(t *testing.T) {
|
||||
ioCounters := map[string]disk.IOCountersStat{
|
||||
"sdb": {Name: "sdb", ReadBytes: 1000, WriteBytes: 1000, ReadCount: 10, WriteCount: 10},
|
||||
"sda": {Name: "sda", ReadBytes: 1000, WriteBytes: 1000, ReadCount: 10, WriteCount: 10},
|
||||
}
|
||||
assert.Equal(t, "sda", mostActiveIoDevice(ioCounters))
|
||||
})
|
||||
|
||||
t.Run("returns empty for empty map", func(t *testing.T) {
|
||||
assert.Equal(t, "", mostActiveIoDevice(map[string]disk.IOCountersStat{}))
|
||||
})
|
||||
}
|
||||
|
||||
func TestIsDockerSpecialMountpoint(t *testing.T) {
|
||||
@@ -373,3 +472,37 @@ func TestDiskUsageCaching(t *testing.T) {
|
||||
"lastDiskUsageUpdate should be refreshed when cache expires")
|
||||
})
|
||||
}
|
||||
|
||||
func TestHasSameDiskUsage(t *testing.T) {
|
||||
const toleranceBytes uint64 = 16 * 1024 * 1024
|
||||
|
||||
t.Run("returns true when totals and usage are equal", func(t *testing.T) {
|
||||
a := &disk.UsageStat{Total: 100 * 1024 * 1024 * 1024, Used: 42 * 1024 * 1024 * 1024}
|
||||
b := &disk.UsageStat{Total: 100 * 1024 * 1024 * 1024, Used: 42 * 1024 * 1024 * 1024}
|
||||
assert.True(t, hasSameDiskUsage(a, b))
|
||||
})
|
||||
|
||||
t.Run("returns true within tolerance", func(t *testing.T) {
|
||||
a := &disk.UsageStat{Total: 100 * 1024 * 1024 * 1024, Used: 42 * 1024 * 1024 * 1024}
|
||||
b := &disk.UsageStat{
|
||||
Total: a.Total + toleranceBytes - 1,
|
||||
Used: a.Used - toleranceBytes + 1,
|
||||
}
|
||||
assert.True(t, hasSameDiskUsage(a, b))
|
||||
})
|
||||
|
||||
t.Run("returns false when total exceeds tolerance", func(t *testing.T) {
|
||||
a := &disk.UsageStat{Total: 100 * 1024 * 1024 * 1024, Used: 42 * 1024 * 1024 * 1024}
|
||||
b := &disk.UsageStat{
|
||||
Total: a.Total + toleranceBytes + 1,
|
||||
Used: a.Used,
|
||||
}
|
||||
assert.False(t, hasSameDiskUsage(a, b))
|
||||
})
|
||||
|
||||
t.Run("returns false for nil or zero total", func(t *testing.T) {
|
||||
assert.False(t, hasSameDiskUsage(nil, &disk.UsageStat{Total: 1, Used: 1}))
|
||||
assert.False(t, hasSameDiskUsage(&disk.UsageStat{Total: 1, Used: 1}, nil))
|
||||
assert.False(t, hasSameDiskUsage(&disk.UsageStat{Total: 0, Used: 0}, &disk.UsageStat{Total: 1, Used: 1}))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
@@ -28,6 +29,7 @@ import (
|
||||
// ansiEscapePattern matches ANSI escape sequences (colors, cursor movement, etc.)
|
||||
// This includes CSI sequences like \x1b[...m and simple escapes like \x1b[K
|
||||
var ansiEscapePattern = regexp.MustCompile(`\x1b\[[0-9;]*[a-zA-Z]|\x1b\][^\x07]*\x07|\x1b[@-Z\\-_]`)
|
||||
var dockerContainerIDPattern = regexp.MustCompile(`^[a-fA-F0-9]{12,64}$`)
|
||||
|
||||
const (
|
||||
// Docker API timeout in milliseconds
|
||||
@@ -72,6 +74,7 @@ type dockerManager struct {
|
||||
// cacheTimeMs -> DeltaTracker for network bytes sent/received
|
||||
networkSentTrackers map[uint16]*deltatracker.DeltaTracker[string, uint64]
|
||||
networkRecvTrackers map[uint16]*deltatracker.DeltaTracker[string, uint64]
|
||||
retrySleep func(time.Duration)
|
||||
}
|
||||
|
||||
// userAgentRoundTripper is a custom http.RoundTripper that adds a User-Agent header to all requests
|
||||
@@ -565,6 +568,7 @@ func newDockerManager() *dockerManager {
|
||||
lastCpuReadTime: make(map[uint16]map[string]time.Time),
|
||||
networkSentTrackers: make(map[uint16]*deltatracker.DeltaTracker[string, uint64]),
|
||||
networkRecvTrackers: make(map[uint16]*deltatracker.DeltaTracker[string, uint64]),
|
||||
retrySleep: time.Sleep,
|
||||
}
|
||||
|
||||
// If using podman, return client
|
||||
@@ -574,7 +578,7 @@ func newDockerManager() *dockerManager {
|
||||
return manager
|
||||
}
|
||||
|
||||
// this can take up to 5 seconds with retry, so run in goroutine
|
||||
// run version check in goroutine to avoid blocking (server may not be ready and requires retries)
|
||||
go manager.checkDockerVersion()
|
||||
|
||||
// give version check a chance to complete before returning
|
||||
@@ -594,18 +598,18 @@ func (dm *dockerManager) checkDockerVersion() {
|
||||
const versionMaxTries = 2
|
||||
for i := 1; i <= versionMaxTries; i++ {
|
||||
resp, err = dm.client.Get("http://localhost/version")
|
||||
if err == nil {
|
||||
if err == nil && resp.StatusCode == http.StatusOK {
|
||||
break
|
||||
}
|
||||
if resp != nil {
|
||||
resp.Body.Close()
|
||||
}
|
||||
if i < versionMaxTries {
|
||||
slog.Debug("Failed to get Docker version; retrying", "attempt", i, "error", err)
|
||||
time.Sleep(5 * time.Second)
|
||||
slog.Debug("Failed to get Docker version; retrying", "attempt", i, "err", err, "response", resp)
|
||||
dm.retrySleep(5 * time.Second)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
if err != nil || resp.StatusCode != http.StatusOK {
|
||||
return
|
||||
}
|
||||
if err := dm.decode(resp, &versionInfo); err != nil {
|
||||
@@ -647,9 +651,34 @@ func getDockerHost() string {
|
||||
return scheme + socks[0]
|
||||
}
|
||||
|
||||
func validateContainerID(containerID string) error {
|
||||
if !dockerContainerIDPattern.MatchString(containerID) {
|
||||
return fmt.Errorf("invalid container id")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildDockerContainerEndpoint(containerID, action string, query url.Values) (string, error) {
|
||||
if err := validateContainerID(containerID); err != nil {
|
||||
return "", err
|
||||
}
|
||||
u := &url.URL{
|
||||
Scheme: "http",
|
||||
Host: "localhost",
|
||||
Path: fmt.Sprintf("/containers/%s/%s", url.PathEscape(containerID), action),
|
||||
}
|
||||
if len(query) > 0 {
|
||||
u.RawQuery = query.Encode()
|
||||
}
|
||||
return u.String(), nil
|
||||
}
|
||||
|
||||
// getContainerInfo fetches the inspection data for a container
|
||||
func (dm *dockerManager) getContainerInfo(ctx context.Context, containerID string) ([]byte, error) {
|
||||
endpoint := fmt.Sprintf("http://localhost/containers/%s/json", containerID)
|
||||
endpoint, err := buildDockerContainerEndpoint(containerID, "json", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -680,7 +709,15 @@ func (dm *dockerManager) getContainerInfo(ctx context.Context, containerID strin
|
||||
|
||||
// getLogs fetches the logs for a container
|
||||
func (dm *dockerManager) getLogs(ctx context.Context, containerID string) (string, error) {
|
||||
endpoint := fmt.Sprintf("http://localhost/containers/%s/logs?stdout=1&stderr=1&tail=%d", containerID, dockerLogsTail)
|
||||
query := url.Values{
|
||||
"stdout": []string{"1"},
|
||||
"stderr": []string{"1"},
|
||||
"tail": []string{fmt.Sprintf("%d", dockerLogsTail)},
|
||||
}
|
||||
endpoint, err := buildDockerContainerEndpoint(containerID, "logs", query)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -698,8 +735,17 @@ func (dm *dockerManager) getLogs(ctx context.Context, containerID string) (strin
|
||||
}
|
||||
|
||||
var builder strings.Builder
|
||||
multiplexed := resp.Header.Get("Content-Type") == "application/vnd.docker.multiplexed-stream"
|
||||
if err := decodeDockerLogStream(resp.Body, &builder, multiplexed); err != nil {
|
||||
contentType := resp.Header.Get("Content-Type")
|
||||
multiplexed := strings.HasSuffix(contentType, "multiplexed-stream")
|
||||
logReader := io.Reader(resp.Body)
|
||||
if !multiplexed {
|
||||
// Podman may return multiplexed logs without Content-Type. Sniff the first frame header
|
||||
// with a small buffered reader only when the header check fails.
|
||||
bufferedReader := bufio.NewReaderSize(resp.Body, 8)
|
||||
multiplexed = detectDockerMultiplexedStream(bufferedReader)
|
||||
logReader = bufferedReader
|
||||
}
|
||||
if err := decodeDockerLogStream(logReader, &builder, multiplexed); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
@@ -711,6 +757,23 @@ func (dm *dockerManager) getLogs(ctx context.Context, containerID string) (strin
|
||||
return logs, nil
|
||||
}
|
||||
|
||||
func detectDockerMultiplexedStream(reader *bufio.Reader) bool {
|
||||
const headerSize = 8
|
||||
header, err := reader.Peek(headerSize)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if header[0] != 0x01 && header[0] != 0x02 {
|
||||
return false
|
||||
}
|
||||
// Docker's stream framing header reserves bytes 1-3 as zero.
|
||||
if header[1] != 0 || header[2] != 0 || header[3] != 0 {
|
||||
return false
|
||||
}
|
||||
frameLen := binary.BigEndian.Uint32(header[4:])
|
||||
return frameLen <= maxLogFrameSize
|
||||
}
|
||||
|
||||
func decodeDockerLogStream(reader io.Reader, builder *strings.Builder, multiplexed bool) error {
|
||||
if !multiplexed {
|
||||
_, err := io.Copy(builder, io.LimitReader(reader, maxTotalLogSize))
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
//go:build testing
|
||||
// +build testing
|
||||
|
||||
package agent
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -19,6 +25,37 @@ import (
|
||||
|
||||
var defaultCacheTimeMs = uint16(60_000)
|
||||
|
||||
type recordingRoundTripper struct {
|
||||
statusCode int
|
||||
body string
|
||||
contentType string
|
||||
called bool
|
||||
lastPath string
|
||||
lastQuery map[string]string
|
||||
}
|
||||
|
||||
func (rt *recordingRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
rt.called = true
|
||||
rt.lastPath = req.URL.EscapedPath()
|
||||
rt.lastQuery = map[string]string{}
|
||||
for key, values := range req.URL.Query() {
|
||||
if len(values) > 0 {
|
||||
rt.lastQuery[key] = values[0]
|
||||
}
|
||||
}
|
||||
resp := &http.Response{
|
||||
StatusCode: rt.statusCode,
|
||||
Status: "200 OK",
|
||||
Header: make(http.Header),
|
||||
Body: io.NopCloser(strings.NewReader(rt.body)),
|
||||
Request: req,
|
||||
}
|
||||
if rt.contentType != "" {
|
||||
resp.Header.Set("Content-Type", rt.contentType)
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// cycleCpuDeltas cycles the CPU tracking data for a specific cache time interval
|
||||
func (dm *dockerManager) cycleCpuDeltas(cacheTimeMs uint16) {
|
||||
// Clear the CPU tracking maps for this cache time interval
|
||||
@@ -110,6 +147,72 @@ func TestCalculateMemoryUsage(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildDockerContainerEndpoint(t *testing.T) {
|
||||
t.Run("valid container ID builds escaped endpoint", func(t *testing.T) {
|
||||
endpoint, err := buildDockerContainerEndpoint("0123456789ab", "json", nil)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "http://localhost/containers/0123456789ab/json", endpoint)
|
||||
})
|
||||
|
||||
t.Run("invalid container ID is rejected", func(t *testing.T) {
|
||||
_, err := buildDockerContainerEndpoint("../../version", "json", nil)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "invalid container id")
|
||||
})
|
||||
}
|
||||
|
||||
func TestContainerDetailsRequestsValidateContainerID(t *testing.T) {
|
||||
rt := &recordingRoundTripper{
|
||||
statusCode: 200,
|
||||
body: `{"Config":{"Env":["SECRET=1"]}}`,
|
||||
}
|
||||
dm := &dockerManager{
|
||||
client: &http.Client{Transport: rt},
|
||||
}
|
||||
|
||||
_, err := dm.getContainerInfo(context.Background(), "../version")
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "invalid container id")
|
||||
assert.False(t, rt.called, "request should be rejected before dispatching to Docker API")
|
||||
}
|
||||
|
||||
func TestContainerDetailsRequestsUseExpectedDockerPaths(t *testing.T) {
|
||||
t.Run("container info uses container json endpoint", func(t *testing.T) {
|
||||
rt := &recordingRoundTripper{
|
||||
statusCode: 200,
|
||||
body: `{"Config":{"Env":["SECRET=1"]},"Name":"demo"}`,
|
||||
}
|
||||
dm := &dockerManager{
|
||||
client: &http.Client{Transport: rt},
|
||||
}
|
||||
|
||||
body, err := dm.getContainerInfo(context.Background(), "0123456789ab")
|
||||
require.NoError(t, err)
|
||||
assert.True(t, rt.called)
|
||||
assert.Equal(t, "/containers/0123456789ab/json", rt.lastPath)
|
||||
assert.NotContains(t, string(body), "SECRET=1", "sensitive env vars should be removed")
|
||||
})
|
||||
|
||||
t.Run("container logs uses expected endpoint and query params", func(t *testing.T) {
|
||||
rt := &recordingRoundTripper{
|
||||
statusCode: 200,
|
||||
body: "line1\nline2\n",
|
||||
}
|
||||
dm := &dockerManager{
|
||||
client: &http.Client{Transport: rt},
|
||||
}
|
||||
|
||||
logs, err := dm.getLogs(context.Background(), "abcdef123456")
|
||||
require.NoError(t, err)
|
||||
assert.True(t, rt.called)
|
||||
assert.Equal(t, "/containers/abcdef123456/logs", rt.lastPath)
|
||||
assert.Equal(t, "1", rt.lastQuery["stdout"])
|
||||
assert.Equal(t, "1", rt.lastQuery["stderr"])
|
||||
assert.Equal(t, "200", rt.lastQuery["tail"])
|
||||
assert.Equal(t, "line1\nline2\n", logs)
|
||||
})
|
||||
}
|
||||
|
||||
func TestValidateCpuPercentage(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -379,6 +482,117 @@ func TestDockerManagerCreation(t *testing.T) {
|
||||
assert.NotNil(t, dm.networkRecvTrackers)
|
||||
}
|
||||
|
||||
func TestCheckDockerVersion(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
responses []struct {
|
||||
statusCode int
|
||||
body string
|
||||
}
|
||||
expectedGood bool
|
||||
expectedRequests int
|
||||
}{
|
||||
{
|
||||
name: "200 with good version on first try",
|
||||
responses: []struct {
|
||||
statusCode int
|
||||
body string
|
||||
}{
|
||||
{http.StatusOK, `{"Version":"25.0.1"}`},
|
||||
},
|
||||
expectedGood: true,
|
||||
expectedRequests: 1,
|
||||
},
|
||||
{
|
||||
name: "200 with old version on first try",
|
||||
responses: []struct {
|
||||
statusCode int
|
||||
body string
|
||||
}{
|
||||
{http.StatusOK, `{"Version":"24.0.7"}`},
|
||||
},
|
||||
expectedGood: false,
|
||||
expectedRequests: 1,
|
||||
},
|
||||
{
|
||||
name: "non-200 then 200 with good version",
|
||||
responses: []struct {
|
||||
statusCode int
|
||||
body string
|
||||
}{
|
||||
{http.StatusServiceUnavailable, `"not ready"`},
|
||||
{http.StatusOK, `{"Version":"25.1.0"}`},
|
||||
},
|
||||
expectedGood: true,
|
||||
expectedRequests: 2,
|
||||
},
|
||||
{
|
||||
name: "non-200 on all retries",
|
||||
responses: []struct {
|
||||
statusCode int
|
||||
body string
|
||||
}{
|
||||
{http.StatusInternalServerError, `"error"`},
|
||||
{http.StatusUnauthorized, `"error"`},
|
||||
},
|
||||
expectedGood: false,
|
||||
expectedRequests: 2,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
requestCount := 0
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
idx := requestCount
|
||||
requestCount++
|
||||
if idx >= len(tt.responses) {
|
||||
idx = len(tt.responses) - 1
|
||||
}
|
||||
w.WriteHeader(tt.responses[idx].statusCode)
|
||||
fmt.Fprint(w, tt.responses[idx].body)
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
dm := &dockerManager{
|
||||
client: &http.Client{
|
||||
Transport: &http.Transport{
|
||||
DialContext: func(_ context.Context, network, _ string) (net.Conn, error) {
|
||||
return net.Dial(network, server.Listener.Addr().String())
|
||||
},
|
||||
},
|
||||
},
|
||||
retrySleep: func(time.Duration) {},
|
||||
}
|
||||
|
||||
dm.checkDockerVersion()
|
||||
|
||||
assert.Equal(t, tt.expectedGood, dm.goodDockerVersion)
|
||||
assert.Equal(t, tt.expectedRequests, requestCount)
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("request error on all retries", func(t *testing.T) {
|
||||
requestCount := 0
|
||||
dm := &dockerManager{
|
||||
client: &http.Client{
|
||||
Transport: &http.Transport{
|
||||
DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
|
||||
requestCount++
|
||||
return nil, errors.New("connection refused")
|
||||
},
|
||||
},
|
||||
},
|
||||
retrySleep: func(time.Duration) {},
|
||||
}
|
||||
|
||||
dm.checkDockerVersion()
|
||||
|
||||
assert.False(t, dm.goodDockerVersion)
|
||||
assert.Equal(t, 2, requestCount)
|
||||
})
|
||||
}
|
||||
|
||||
func TestCycleCpuDeltas(t *testing.T) {
|
||||
dm := &dockerManager{
|
||||
lastCpuContainer: map[uint16]map[string]uint64{
|
||||
@@ -699,6 +913,42 @@ func TestContainerStatsEndToEndWithRealData(t *testing.T) {
|
||||
assert.Equal(t, testTime, testStats.PrevReadTime)
|
||||
}
|
||||
|
||||
func TestGetLogsDetectsMultiplexedWithoutContentType(t *testing.T) {
|
||||
// Docker multiplexed frame: [stream][0,0,0][len(4 bytes BE)][payload]
|
||||
frame := []byte{
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05,
|
||||
'H', 'e', 'l', 'l', 'o',
|
||||
}
|
||||
rt := &recordingRoundTripper{
|
||||
statusCode: 200,
|
||||
body: string(frame),
|
||||
// Intentionally omit content type to simulate Podman behavior.
|
||||
}
|
||||
dm := &dockerManager{
|
||||
client: &http.Client{Transport: rt},
|
||||
}
|
||||
|
||||
logs, err := dm.getLogs(context.Background(), "abcdef123456")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "Hello", logs)
|
||||
}
|
||||
|
||||
func TestGetLogsDoesNotMisclassifyRawStreamAsMultiplexed(t *testing.T) {
|
||||
// Starts with 0x01, but doesn't match Docker frame signature (reserved bytes aren't all zero).
|
||||
raw := []byte{0x01, 0x02, 0x03, 0x04, 'r', 'a', 'w'}
|
||||
rt := &recordingRoundTripper{
|
||||
statusCode: 200,
|
||||
body: string(raw),
|
||||
}
|
||||
dm := &dockerManager{
|
||||
client: &http.Client{Transport: rt},
|
||||
}
|
||||
|
||||
logs, err := dm.getLogs(context.Background(), "abcdef123456")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, raw, []byte(logs))
|
||||
}
|
||||
|
||||
func TestEdgeCasesWithRealData(t *testing.T) {
|
||||
// Test with minimal container stats
|
||||
minimalStats := &container.ApiStats{
|
||||
|
||||
@@ -199,19 +199,6 @@ func readHexByteFile(path string) (uint8, bool) {
|
||||
return b, ok
|
||||
}
|
||||
|
||||
func readStringFile(path string) string {
|
||||
content, _ := readStringFileOK(path)
|
||||
return content
|
||||
}
|
||||
|
||||
func readStringFileOK(path string) (string, bool) {
|
||||
b, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return "", false
|
||||
}
|
||||
return strings.TrimSpace(string(b)), true
|
||||
}
|
||||
|
||||
func hasEmmcHealthFiles(deviceDir string) bool {
|
||||
entries, err := os.ReadDir(deviceDir)
|
||||
if err != nil {
|
||||
|
||||
24
agent/file_utils.go
Normal file
24
agent/file_utils.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func readStringFile(path string) string {
|
||||
content, _ := readStringFileOK(path)
|
||||
return content
|
||||
}
|
||||
|
||||
func readStringFileOK(path string) (string, bool) {
|
||||
b, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return "", false
|
||||
}
|
||||
return strings.TrimSpace(string(b)), true
|
||||
}
|
||||
|
||||
func fileExists(path string) bool {
|
||||
_, err := os.Stat(path)
|
||||
return err == nil
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build testing
|
||||
// +build testing
|
||||
|
||||
package agent
|
||||
|
||||
|
||||
91
agent/gpu.go
91
agent/gpu.go
@@ -9,6 +9,7 @@ import (
|
||||
"maps"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -19,11 +20,13 @@ import (
|
||||
|
||||
const (
|
||||
// Commands
|
||||
nvidiaSmiCmd string = "nvidia-smi"
|
||||
rocmSmiCmd string = "rocm-smi"
|
||||
tegraStatsCmd string = "tegrastats"
|
||||
nvtopCmd string = "nvtop"
|
||||
noGPUFoundMsg string = "no GPU found - see https://beszel.dev/guide/gpu"
|
||||
nvidiaSmiCmd string = "nvidia-smi"
|
||||
rocmSmiCmd string = "rocm-smi"
|
||||
tegraStatsCmd string = "tegrastats"
|
||||
nvtopCmd string = "nvtop"
|
||||
powermetricsCmd string = "powermetrics"
|
||||
macmonCmd string = "macmon"
|
||||
noGPUFoundMsg string = "no GPU found - see https://beszel.dev/guide/gpu"
|
||||
|
||||
// Command retry and timeout constants
|
||||
retryWaitTime time.Duration = 5 * time.Second
|
||||
@@ -82,15 +85,18 @@ var errNoValidData = fmt.Errorf("no valid GPU data found") // Error for missing
|
||||
type collectorSource string
|
||||
|
||||
const (
|
||||
collectorSourceNVTop collectorSource = collectorSource(nvtopCmd)
|
||||
collectorSourceNVML collectorSource = "nvml"
|
||||
collectorSourceNvidiaSMI collectorSource = collectorSource(nvidiaSmiCmd)
|
||||
collectorSourceIntelGpuTop collectorSource = collectorSource(intelGpuStatsCmd)
|
||||
collectorSourceAmdSysfs collectorSource = "amd_sysfs"
|
||||
collectorSourceRocmSMI collectorSource = collectorSource(rocmSmiCmd)
|
||||
collectorGroupNvidia string = "nvidia"
|
||||
collectorGroupIntel string = "intel"
|
||||
collectorGroupAmd string = "amd"
|
||||
collectorSourceNVTop collectorSource = collectorSource(nvtopCmd)
|
||||
collectorSourceNVML collectorSource = "nvml"
|
||||
collectorSourceNvidiaSMI collectorSource = collectorSource(nvidiaSmiCmd)
|
||||
collectorSourceIntelGpuTop collectorSource = collectorSource(intelGpuStatsCmd)
|
||||
collectorSourceAmdSysfs collectorSource = "amd_sysfs"
|
||||
collectorSourceRocmSMI collectorSource = collectorSource(rocmSmiCmd)
|
||||
collectorSourceMacmon collectorSource = collectorSource(macmonCmd)
|
||||
collectorSourcePowermetrics collectorSource = collectorSource(powermetricsCmd)
|
||||
collectorGroupNvidia string = "nvidia"
|
||||
collectorGroupIntel string = "intel"
|
||||
collectorGroupAmd string = "amd"
|
||||
collectorGroupApple string = "apple"
|
||||
)
|
||||
|
||||
func isValidCollectorSource(source collectorSource) bool {
|
||||
@@ -100,7 +106,9 @@ func isValidCollectorSource(source collectorSource) bool {
|
||||
collectorSourceNvidiaSMI,
|
||||
collectorSourceIntelGpuTop,
|
||||
collectorSourceAmdSysfs,
|
||||
collectorSourceRocmSMI:
|
||||
collectorSourceRocmSMI,
|
||||
collectorSourceMacmon,
|
||||
collectorSourcePowermetrics:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@@ -108,12 +116,14 @@ func isValidCollectorSource(source collectorSource) bool {
|
||||
|
||||
// gpuCapabilities describes detected GPU tooling and sysfs support on the host.
|
||||
type gpuCapabilities struct {
|
||||
hasNvidiaSmi bool
|
||||
hasRocmSmi bool
|
||||
hasAmdSysfs bool
|
||||
hasTegrastats bool
|
||||
hasIntelGpuTop bool
|
||||
hasNvtop bool
|
||||
hasNvidiaSmi bool
|
||||
hasRocmSmi bool
|
||||
hasAmdSysfs bool
|
||||
hasTegrastats bool
|
||||
hasIntelGpuTop bool
|
||||
hasNvtop bool
|
||||
hasMacmon bool
|
||||
hasPowermetrics bool
|
||||
}
|
||||
|
||||
type collectorDefinition struct {
|
||||
@@ -449,11 +459,19 @@ func (gm *GPUManager) discoverGpuCapabilities() gpuCapabilities {
|
||||
if _, err := exec.LookPath(nvtopCmd); err == nil {
|
||||
caps.hasNvtop = true
|
||||
}
|
||||
if runtime.GOOS == "darwin" {
|
||||
if _, err := exec.LookPath(macmonCmd); err == nil {
|
||||
caps.hasMacmon = true
|
||||
}
|
||||
if _, err := exec.LookPath(powermetricsCmd); err == nil {
|
||||
caps.hasPowermetrics = true
|
||||
}
|
||||
}
|
||||
return caps
|
||||
}
|
||||
|
||||
func hasAnyGpuCollector(caps gpuCapabilities) bool {
|
||||
return caps.hasNvidiaSmi || caps.hasRocmSmi || caps.hasAmdSysfs || caps.hasTegrastats || caps.hasIntelGpuTop || caps.hasNvtop
|
||||
return caps.hasNvidiaSmi || caps.hasRocmSmi || caps.hasAmdSysfs || caps.hasTegrastats || caps.hasIntelGpuTop || caps.hasNvtop || caps.hasMacmon || caps.hasPowermetrics
|
||||
}
|
||||
|
||||
func (gm *GPUManager) startIntelCollector() {
|
||||
@@ -567,6 +585,22 @@ func (gm *GPUManager) collectorDefinitions(caps gpuCapabilities) map[collectorSo
|
||||
return true
|
||||
},
|
||||
},
|
||||
collectorSourceMacmon: {
|
||||
group: collectorGroupApple,
|
||||
available: caps.hasMacmon,
|
||||
start: func(_ func()) bool {
|
||||
gm.startMacmonCollector()
|
||||
return true
|
||||
},
|
||||
},
|
||||
collectorSourcePowermetrics: {
|
||||
group: collectorGroupApple,
|
||||
available: caps.hasPowermetrics,
|
||||
start: func(_ func()) bool {
|
||||
gm.startPowermetricsCollector()
|
||||
return true
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -674,7 +708,18 @@ func (gm *GPUManager) resolveLegacyCollectorPriority(caps gpuCapabilities) []col
|
||||
priorities = append(priorities, collectorSourceIntelGpuTop)
|
||||
}
|
||||
|
||||
// Keep nvtop as a legacy last resort only when no vendor collector exists.
|
||||
// Apple collectors are currently opt-in only for testing.
|
||||
// Enable them with GPU_COLLECTOR=macmon or GPU_COLLECTOR=powermetrics.
|
||||
// TODO: uncomment below when Apple collectors are confirmed to be working.
|
||||
//
|
||||
// Prefer macmon on macOS (no sudo). Fall back to powermetrics if present.
|
||||
// if caps.hasMacmon {
|
||||
// priorities = append(priorities, collectorSourceMacmon)
|
||||
// } else if caps.hasPowermetrics {
|
||||
// priorities = append(priorities, collectorSourcePowermetrics)
|
||||
// }
|
||||
|
||||
// Keep nvtop as a last resort only when no vendor collector exists.
|
||||
if len(priorities) == 0 && caps.hasNvtop {
|
||||
priorities = append(priorities, collectorSourceNVTop)
|
||||
}
|
||||
|
||||
@@ -103,10 +103,8 @@ func (gm *GPUManager) updateAmdGpuData(cardPath string) bool {
|
||||
|
||||
// Read all sysfs values first (no lock needed - these can be slow)
|
||||
usage, usageErr := readSysfsFloat(filepath.Join(devicePath, "gpu_busy_percent"))
|
||||
vramUsed, memUsedErr := readSysfsFloat(filepath.Join(devicePath, "mem_info_vram_used"))
|
||||
vramTotal, _ := readSysfsFloat(filepath.Join(devicePath, "mem_info_vram_total"))
|
||||
memUsed := vramUsed
|
||||
memTotal := vramTotal
|
||||
memUsed, memUsedErr := readSysfsFloat(filepath.Join(devicePath, "mem_info_vram_used"))
|
||||
memTotal, _ := readSysfsFloat(filepath.Join(devicePath, "mem_info_vram_total"))
|
||||
// if gtt is present, add it to the memory used and total (https://github.com/henrygd/beszel/issues/1569#issuecomment-3837640484)
|
||||
if gttUsed, err := readSysfsFloat(filepath.Join(devicePath, "mem_info_gtt_used")); err == nil && gttUsed > 0 {
|
||||
if gttTotal, err := readSysfsFloat(filepath.Join(devicePath, "mem_info_gtt_total")); err == nil {
|
||||
@@ -243,7 +241,10 @@ func getCachedAmdgpuName(deviceID, revisionID string) (name string, found bool,
|
||||
|
||||
// normalizeAmdgpuName trims standard suffixes from AMDGPU product names.
|
||||
func normalizeAmdgpuName(name string) string {
|
||||
return strings.TrimSuffix(strings.TrimSpace(name), " Graphics")
|
||||
for _, suffix := range []string{" Graphics", " Series"} {
|
||||
name = strings.TrimSuffix(name, suffix)
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
// cacheAmdgpuName stores a resolved AMDGPU name in the lookup cache.
|
||||
|
||||
252
agent/gpu_darwin.go
Normal file
252
agent/gpu_darwin.go
Normal file
@@ -0,0 +1,252 @@
|
||||
//go:build darwin
|
||||
|
||||
package agent
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/henrygd/beszel/internal/entities/system"
|
||||
)
|
||||
|
||||
const (
|
||||
// powermetricsSampleIntervalMs is the sampling interval passed to powermetrics (-i).
|
||||
powermetricsSampleIntervalMs = 500
|
||||
// powermetricsPollInterval is how often we run powermetrics to collect a new sample.
|
||||
powermetricsPollInterval = 2 * time.Second
|
||||
// macmonIntervalMs is the sampling interval passed to macmon pipe (-i), in milliseconds.
|
||||
macmonIntervalMs = 2500
|
||||
)
|
||||
|
||||
const appleGPUID = "0"
|
||||
|
||||
// startPowermetricsCollector runs powermetrics --samplers gpu_power in a loop and updates
|
||||
// GPU usage and power. Requires root (sudo) on macOS. A single logical GPU is reported as id "0".
|
||||
func (gm *GPUManager) startPowermetricsCollector() {
|
||||
// Ensure single GPU entry for Apple GPU
|
||||
if _, ok := gm.GpuDataMap[appleGPUID]; !ok {
|
||||
gm.GpuDataMap[appleGPUID] = &system.GPUData{Name: "Apple GPU"}
|
||||
}
|
||||
|
||||
go func() {
|
||||
failures := 0
|
||||
for {
|
||||
if err := gm.collectPowermetrics(); err != nil {
|
||||
failures++
|
||||
if failures > maxFailureRetries {
|
||||
slog.Warn("powermetrics GPU collector failed repeatedly, stopping", "err", err)
|
||||
break
|
||||
}
|
||||
slog.Warn("Error collecting macOS GPU data via powermetrics (may require sudo)", "err", err)
|
||||
time.Sleep(retryWaitTime)
|
||||
continue
|
||||
}
|
||||
failures = 0
|
||||
time.Sleep(powermetricsPollInterval)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// collectPowermetrics runs powermetrics once and parses GPU usage and power from its output.
|
||||
func (gm *GPUManager) collectPowermetrics() error {
|
||||
interval := strconv.Itoa(powermetricsSampleIntervalMs)
|
||||
cmd := exec.Command(powermetricsCmd, "--samplers", "gpu_power", "-i", interval, "-n", "1")
|
||||
cmd.Stderr = nil
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !gm.parsePowermetricsData(out) {
|
||||
return errNoValidData
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// parsePowermetricsData parses powermetrics gpu_power output and updates GpuDataMap["0"].
|
||||
// Example output:
|
||||
//
|
||||
// **** GPU usage ****
|
||||
// GPU HW active frequency: 444 MHz
|
||||
// GPU HW active residency: 0.97% (444 MHz: .97% ...
|
||||
// GPU idle residency: 99.03%
|
||||
// GPU Power: 4 mW
|
||||
func (gm *GPUManager) parsePowermetricsData(output []byte) bool {
|
||||
var idleResidency, powerMW float64
|
||||
var gotIdle, gotPower bool
|
||||
|
||||
scanner := bufio.NewScanner(bytes.NewReader(output))
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
if strings.HasPrefix(line, "GPU idle residency:") {
|
||||
// "GPU idle residency: 99.03%"
|
||||
fields := strings.Fields(strings.TrimPrefix(line, "GPU idle residency:"))
|
||||
if len(fields) >= 1 {
|
||||
pct := strings.TrimSuffix(fields[0], "%")
|
||||
if v, err := strconv.ParseFloat(pct, 64); err == nil {
|
||||
idleResidency = v
|
||||
gotIdle = true
|
||||
}
|
||||
}
|
||||
} else if strings.HasPrefix(line, "GPU Power:") {
|
||||
// "GPU Power: 4 mW"
|
||||
fields := strings.Fields(strings.TrimPrefix(line, "GPU Power:"))
|
||||
if len(fields) >= 1 {
|
||||
if v, err := strconv.ParseFloat(fields[0], 64); err == nil {
|
||||
powerMW = v
|
||||
gotPower = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
return false
|
||||
}
|
||||
if !gotIdle && !gotPower {
|
||||
return false
|
||||
}
|
||||
|
||||
gm.Lock()
|
||||
defer gm.Unlock()
|
||||
|
||||
if _, ok := gm.GpuDataMap[appleGPUID]; !ok {
|
||||
gm.GpuDataMap[appleGPUID] = &system.GPUData{Name: "Apple GPU"}
|
||||
}
|
||||
gpu := gm.GpuDataMap[appleGPUID]
|
||||
|
||||
if gotIdle {
|
||||
// Usage = 100 - idle residency (e.g. 100 - 99.03 = 0.97%)
|
||||
gpu.Usage += 100 - idleResidency
|
||||
}
|
||||
if gotPower {
|
||||
// mW -> W
|
||||
gpu.Power += powerMW / milliwattsInAWatt
|
||||
}
|
||||
gpu.Count++
|
||||
return true
|
||||
}
|
||||
|
||||
// startMacmonCollector runs `macmon pipe` in a loop and parses one JSON object per line.
|
||||
// This collector does not require sudo. A single logical GPU is reported as id "0".
|
||||
func (gm *GPUManager) startMacmonCollector() {
|
||||
if _, ok := gm.GpuDataMap[appleGPUID]; !ok {
|
||||
gm.GpuDataMap[appleGPUID] = &system.GPUData{Name: "Apple GPU"}
|
||||
}
|
||||
|
||||
go func() {
|
||||
failures := 0
|
||||
for {
|
||||
if err := gm.collectMacmonPipe(); err != nil {
|
||||
failures++
|
||||
if failures > maxFailureRetries {
|
||||
slog.Warn("macmon GPU collector failed repeatedly, stopping", "err", err)
|
||||
break
|
||||
}
|
||||
slog.Warn("Error collecting macOS GPU data via macmon", "err", err)
|
||||
time.Sleep(retryWaitTime)
|
||||
continue
|
||||
}
|
||||
failures = 0
|
||||
// `macmon pipe` is long-running; if it returns, wait a bit before restarting.
|
||||
time.Sleep(retryWaitTime)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
type macmonTemp struct {
|
||||
GPUTempAvg float64 `json:"gpu_temp_avg"`
|
||||
}
|
||||
|
||||
type macmonSample struct {
|
||||
GPUPower float64 `json:"gpu_power"` // watts (macmon reports fractional values)
|
||||
GPURAMPower float64 `json:"gpu_ram_power"` // watts
|
||||
GPUUsage []float64 `json:"gpu_usage"` // [freq_mhz, usage] where usage is typically 0..1
|
||||
Temp macmonTemp `json:"temp"`
|
||||
}
|
||||
|
||||
func (gm *GPUManager) collectMacmonPipe() (err error) {
|
||||
cmd := exec.Command(macmonCmd, "pipe", "-i", strconv.Itoa(macmonIntervalMs))
|
||||
// Avoid blocking if macmon writes to stderr.
|
||||
cmd.Stderr = io.Discard
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cmd.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Ensure we always reap the child to avoid zombies on any return path and
|
||||
// propagate a non-zero exit code if no other error was set.
|
||||
defer func() {
|
||||
_ = stdout.Close()
|
||||
if cmd.ProcessState == nil || !cmd.ProcessState.Exited() {
|
||||
_ = cmd.Process.Kill()
|
||||
}
|
||||
if waitErr := cmd.Wait(); err == nil && waitErr != nil {
|
||||
err = waitErr
|
||||
}
|
||||
}()
|
||||
|
||||
scanner := bufio.NewScanner(stdout)
|
||||
var hadSample bool
|
||||
for scanner.Scan() {
|
||||
line := bytes.TrimSpace(scanner.Bytes())
|
||||
if len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
if gm.parseMacmonLine(line) {
|
||||
hadSample = true
|
||||
}
|
||||
}
|
||||
if scanErr := scanner.Err(); scanErr != nil {
|
||||
return scanErr
|
||||
}
|
||||
if !hadSample {
|
||||
return errNoValidData
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseMacmonLine parses a single macmon JSON line and updates Apple GPU metrics.
|
||||
func (gm *GPUManager) parseMacmonLine(line []byte) bool {
|
||||
var sample macmonSample
|
||||
if err := json.Unmarshal(line, &sample); err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
usage := 0.0
|
||||
if len(sample.GPUUsage) >= 2 {
|
||||
usage = sample.GPUUsage[1]
|
||||
// Heuristic: macmon typically reports 0..1; convert to percentage.
|
||||
if usage <= 1.0 {
|
||||
usage *= 100
|
||||
}
|
||||
}
|
||||
|
||||
// Consider the line valid if it contains at least one GPU metric.
|
||||
if usage == 0 && sample.GPUPower == 0 && sample.Temp.GPUTempAvg == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
gm.Lock()
|
||||
defer gm.Unlock()
|
||||
|
||||
gpu, ok := gm.GpuDataMap[appleGPUID]
|
||||
if !ok {
|
||||
gpu = &system.GPUData{Name: "Apple GPU"}
|
||||
gm.GpuDataMap[appleGPUID] = gpu
|
||||
}
|
||||
gpu.Temperature = sample.Temp.GPUTempAvg
|
||||
gpu.Usage += usage
|
||||
// macmon reports power in watts; include VRAM power if present.
|
||||
gpu.Power += sample.GPUPower + sample.GPURAMPower
|
||||
gpu.Count++
|
||||
return true
|
||||
}
|
||||
81
agent/gpu_darwin_test.go
Normal file
81
agent/gpu_darwin_test.go
Normal file
@@ -0,0 +1,81 @@
|
||||
//go:build darwin
|
||||
|
||||
package agent
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/henrygd/beszel/internal/entities/system"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestParsePowermetricsData(t *testing.T) {
|
||||
input := `
|
||||
Machine model: Mac14,10
|
||||
OS version: 25D125
|
||||
|
||||
*** Sampled system activity (Sat Feb 14 00:42:06 2026 -0500) (503.05ms elapsed) ***
|
||||
|
||||
**** GPU usage ****
|
||||
|
||||
GPU HW active frequency: 444 MHz
|
||||
GPU HW active residency: 0.97% (444 MHz: .97% 612 MHz: 0% 808 MHz: 0% 968 MHz: 0% 1110 MHz: 0% 1236 MHz: 0% 1338 MHz: 0% 1398 MHz: 0%)
|
||||
GPU SW requested state: (P1 : 100% P2 : 0% P3 : 0% P4 : 0% P5 : 0% P6 : 0% P7 : 0% P8 : 0%)
|
||||
GPU idle residency: 99.03%
|
||||
GPU Power: 4 mW
|
||||
`
|
||||
gm := &GPUManager{
|
||||
GpuDataMap: make(map[string]*system.GPUData),
|
||||
}
|
||||
valid := gm.parsePowermetricsData([]byte(input))
|
||||
require.True(t, valid)
|
||||
|
||||
g0, ok := gm.GpuDataMap["0"]
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "Apple GPU", g0.Name)
|
||||
// Usage = 100 - 99.03 = 0.97
|
||||
assert.InDelta(t, 0.97, g0.Usage, 0.01)
|
||||
// 4 mW -> 0.004 W
|
||||
assert.InDelta(t, 0.004, g0.Power, 0.0001)
|
||||
assert.Equal(t, 1.0, g0.Count)
|
||||
}
|
||||
|
||||
func TestParsePowermetricsDataPartial(t *testing.T) {
|
||||
// Only power line (e.g. older macOS or different sampler output)
|
||||
input := `
|
||||
**** GPU usage ****
|
||||
GPU Power: 120 mW
|
||||
`
|
||||
gm := &GPUManager{
|
||||
GpuDataMap: make(map[string]*system.GPUData),
|
||||
}
|
||||
valid := gm.parsePowermetricsData([]byte(input))
|
||||
require.True(t, valid)
|
||||
|
||||
g0, ok := gm.GpuDataMap["0"]
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "Apple GPU", g0.Name)
|
||||
assert.InDelta(t, 0.12, g0.Power, 0.001)
|
||||
assert.Equal(t, 1.0, g0.Count)
|
||||
}
|
||||
|
||||
func TestParseMacmonLine(t *testing.T) {
|
||||
input := `{"all_power":0.6468324661254883,"ane_power":0.0,"cpu_power":0.6359732151031494,"ecpu_usage":[2061,0.1726151406764984],"gpu_power":0.010859241709113121,"gpu_ram_power":0.000965250947047025,"gpu_usage":[503,0.013633215799927711],"memory":{"ram_total":17179869184,"ram_usage":12322914304,"swap_total":0,"swap_usage":0},"pcpu_usage":[1248,0.11792058497667313],"ram_power":0.14885640144348145,"sys_power":10.4955415725708,"temp":{"cpu_temp_avg":23.041261672973633,"gpu_temp_avg":29.44516944885254},"timestamp":"2026-02-17T19:34:27.942556+00:00"}`
|
||||
|
||||
gm := &GPUManager{
|
||||
GpuDataMap: make(map[string]*system.GPUData),
|
||||
}
|
||||
valid := gm.parseMacmonLine([]byte(input))
|
||||
require.True(t, valid)
|
||||
|
||||
g0, ok := gm.GpuDataMap["0"]
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "Apple GPU", g0.Name)
|
||||
// macmon reports usage fraction 0..1; expect percent conversion.
|
||||
assert.InDelta(t, 1.3633, g0.Usage, 0.05)
|
||||
// power includes gpu_power + gpu_ram_power
|
||||
assert.InDelta(t, 0.011824, g0.Power, 0.0005)
|
||||
assert.InDelta(t, 29.445, g0.Temperature, 0.01)
|
||||
assert.Equal(t, 1.0, g0.Count)
|
||||
}
|
||||
9
agent/gpu_darwin_unsupported.go
Normal file
9
agent/gpu_darwin_unsupported.go
Normal file
@@ -0,0 +1,9 @@
|
||||
//go:build !darwin
|
||||
|
||||
package agent
|
||||
|
||||
// startPowermetricsCollector is a no-op on non-darwin platforms; the real implementation is in gpu_darwin.go.
|
||||
func (gm *GPUManager) startPowermetricsCollector() {}
|
||||
|
||||
// startMacmonCollector is a no-op on non-darwin platforms; the real implementation is in gpu_darwin.go.
|
||||
func (gm *GPUManager) startMacmonCollector() {}
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build testing
|
||||
// +build testing
|
||||
|
||||
package agent
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build testing
|
||||
// +build testing
|
||||
|
||||
package agent
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build testing
|
||||
// +build testing
|
||||
|
||||
package health
|
||||
|
||||
@@ -37,7 +36,6 @@ func TestHealth(t *testing.T) {
|
||||
})
|
||||
|
||||
// This test uses synctest to simulate time passing.
|
||||
// NOTE: This test requires GOEXPERIMENT=synctest to run.
|
||||
t.Run("check with simulated time", func(t *testing.T) {
|
||||
synctest.Test(t, func(t *testing.T) {
|
||||
// Update the file to set the initial timestamp.
|
||||
|
||||
239
agent/mdraid_linux.go
Normal file
239
agent/mdraid_linux.go
Normal file
@@ -0,0 +1,239 @@
|
||||
//go:build linux
|
||||
|
||||
package agent
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/henrygd/beszel/internal/entities/smart"
|
||||
)
|
||||
|
||||
// mdraidSysfsRoot is a test hook; production value is "/sys".
|
||||
var mdraidSysfsRoot = "/sys"
|
||||
|
||||
type mdraidHealth struct {
|
||||
name string
|
||||
level string
|
||||
arrayState string
|
||||
degraded uint64
|
||||
raidDisks uint64
|
||||
syncAction string
|
||||
syncCompleted string
|
||||
syncSpeed string
|
||||
mismatchCnt uint64
|
||||
capacity uint64
|
||||
}
|
||||
|
||||
func scanMdraidDevices() []*DeviceInfo {
|
||||
blockDir := filepath.Join(mdraidSysfsRoot, "block")
|
||||
entries, err := os.ReadDir(blockDir)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
devices := make([]*DeviceInfo, 0, 2)
|
||||
for _, ent := range entries {
|
||||
name := ent.Name()
|
||||
if !isMdraidBlockName(name) {
|
||||
continue
|
||||
}
|
||||
mdDir := filepath.Join(blockDir, name, "md")
|
||||
if !fileExists(filepath.Join(mdDir, "array_state")) {
|
||||
continue
|
||||
}
|
||||
|
||||
devPath := filepath.Join("/dev", name)
|
||||
devices = append(devices, &DeviceInfo{
|
||||
Name: devPath,
|
||||
Type: "mdraid",
|
||||
InfoName: devPath + " [mdraid]",
|
||||
Protocol: "MD",
|
||||
})
|
||||
}
|
||||
|
||||
return devices
|
||||
}
|
||||
|
||||
func (sm *SmartManager) collectMdraidHealth(deviceInfo *DeviceInfo) (bool, error) {
|
||||
if deviceInfo == nil || deviceInfo.Name == "" {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
base := filepath.Base(deviceInfo.Name)
|
||||
if !isMdraidBlockName(base) && !strings.EqualFold(deviceInfo.Type, "mdraid") {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
health, ok := readMdraidHealth(base)
|
||||
if !ok {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
deviceInfo.Type = "mdraid"
|
||||
key := fmt.Sprintf("mdraid:%s", base)
|
||||
status := mdraidSmartStatus(health)
|
||||
|
||||
attrs := make([]*smart.SmartAttribute, 0, 10)
|
||||
if health.arrayState != "" {
|
||||
attrs = append(attrs, &smart.SmartAttribute{Name: "ArrayState", RawString: health.arrayState})
|
||||
}
|
||||
if health.level != "" {
|
||||
attrs = append(attrs, &smart.SmartAttribute{Name: "RaidLevel", RawString: health.level})
|
||||
}
|
||||
if health.raidDisks > 0 {
|
||||
attrs = append(attrs, &smart.SmartAttribute{Name: "RaidDisks", RawValue: health.raidDisks})
|
||||
}
|
||||
if health.degraded > 0 {
|
||||
attrs = append(attrs, &smart.SmartAttribute{Name: "Degraded", RawValue: health.degraded})
|
||||
}
|
||||
if health.syncAction != "" {
|
||||
attrs = append(attrs, &smart.SmartAttribute{Name: "SyncAction", RawString: health.syncAction})
|
||||
}
|
||||
if health.syncCompleted != "" {
|
||||
attrs = append(attrs, &smart.SmartAttribute{Name: "SyncCompleted", RawString: health.syncCompleted})
|
||||
}
|
||||
if health.syncSpeed != "" {
|
||||
attrs = append(attrs, &smart.SmartAttribute{Name: "SyncSpeed", RawString: health.syncSpeed})
|
||||
}
|
||||
if health.mismatchCnt > 0 {
|
||||
attrs = append(attrs, &smart.SmartAttribute{Name: "MismatchCount", RawValue: health.mismatchCnt})
|
||||
}
|
||||
|
||||
sm.Lock()
|
||||
defer sm.Unlock()
|
||||
|
||||
if _, exists := sm.SmartDataMap[key]; !exists {
|
||||
sm.SmartDataMap[key] = &smart.SmartData{}
|
||||
}
|
||||
|
||||
data := sm.SmartDataMap[key]
|
||||
data.ModelName = "Linux MD RAID"
|
||||
if health.level != "" {
|
||||
data.ModelName = "Linux MD RAID (" + health.level + ")"
|
||||
}
|
||||
data.SerialNumber = ""
|
||||
data.FirmwareVersion = ""
|
||||
data.Capacity = health.capacity
|
||||
data.Temperature = 0
|
||||
data.SmartStatus = status
|
||||
data.DiskName = filepath.Join("/dev", base)
|
||||
data.DiskType = "mdraid"
|
||||
data.Attributes = attrs
|
||||
sm.SmartDataMap[key] = data
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func readMdraidHealth(blockName string) (mdraidHealth, bool) {
|
||||
var out mdraidHealth
|
||||
|
||||
if !isMdraidBlockName(blockName) {
|
||||
return out, false
|
||||
}
|
||||
|
||||
mdDir := filepath.Join(mdraidSysfsRoot, "block", blockName, "md")
|
||||
arrayState, okState := readStringFileOK(filepath.Join(mdDir, "array_state"))
|
||||
if !okState {
|
||||
return out, false
|
||||
}
|
||||
|
||||
out.name = blockName
|
||||
out.arrayState = arrayState
|
||||
out.level = readStringFile(filepath.Join(mdDir, "level"))
|
||||
out.syncAction = readStringFile(filepath.Join(mdDir, "sync_action"))
|
||||
out.syncCompleted = readStringFile(filepath.Join(mdDir, "sync_completed"))
|
||||
out.syncSpeed = readStringFile(filepath.Join(mdDir, "sync_speed"))
|
||||
|
||||
if val, ok := readUintFile(filepath.Join(mdDir, "raid_disks")); ok {
|
||||
out.raidDisks = val
|
||||
}
|
||||
if val, ok := readUintFile(filepath.Join(mdDir, "degraded")); ok {
|
||||
out.degraded = val
|
||||
}
|
||||
if val, ok := readUintFile(filepath.Join(mdDir, "mismatch_cnt")); ok {
|
||||
out.mismatchCnt = val
|
||||
}
|
||||
|
||||
if capBytes, ok := readMdraidBlockCapacityBytes(blockName, mdraidSysfsRoot); ok {
|
||||
out.capacity = capBytes
|
||||
}
|
||||
|
||||
return out, true
|
||||
}
|
||||
|
||||
func mdraidSmartStatus(health mdraidHealth) string {
|
||||
state := strings.ToLower(strings.TrimSpace(health.arrayState))
|
||||
switch state {
|
||||
case "inactive", "faulty", "broken", "stopped":
|
||||
return "FAILED"
|
||||
}
|
||||
if health.degraded > 0 {
|
||||
return "FAILED"
|
||||
}
|
||||
|
||||
switch strings.ToLower(strings.TrimSpace(health.syncAction)) {
|
||||
case "resync", "recover", "reshape", "check", "repair":
|
||||
return "WARNING"
|
||||
}
|
||||
|
||||
if state == "clean" || state == "active" || state == "readonly" {
|
||||
return "PASSED"
|
||||
}
|
||||
return "UNKNOWN"
|
||||
}
|
||||
|
||||
func isMdraidBlockName(name string) bool {
|
||||
if !strings.HasPrefix(name, "md") {
|
||||
return false
|
||||
}
|
||||
suffix := strings.TrimPrefix(name, "md")
|
||||
if suffix == "" {
|
||||
return false
|
||||
}
|
||||
for _, c := range suffix {
|
||||
if c < '0' || c > '9' {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func readUintFile(path string) (uint64, bool) {
|
||||
raw, ok := readStringFileOK(path)
|
||||
if !ok {
|
||||
return 0, false
|
||||
}
|
||||
parsed, err := strconv.ParseUint(strings.TrimSpace(raw), 10, 64)
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
return parsed, true
|
||||
}
|
||||
|
||||
func readMdraidBlockCapacityBytes(blockName, root string) (uint64, bool) {
|
||||
sizePath := filepath.Join(root, "block", blockName, "size")
|
||||
lbsPath := filepath.Join(root, "block", blockName, "queue", "logical_block_size")
|
||||
|
||||
sizeStr, ok := readStringFileOK(sizePath)
|
||||
if !ok {
|
||||
return 0, false
|
||||
}
|
||||
sectors, err := strconv.ParseUint(strings.TrimSpace(sizeStr), 10, 64)
|
||||
if err != nil || sectors == 0 {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
lbsStr, ok := readStringFileOK(lbsPath)
|
||||
logicalBlockSize := uint64(512)
|
||||
if ok {
|
||||
if parsed, err := strconv.ParseUint(strings.TrimSpace(lbsStr), 10, 64); err == nil && parsed > 0 {
|
||||
logicalBlockSize = parsed
|
||||
}
|
||||
}
|
||||
|
||||
return sectors * logicalBlockSize, true
|
||||
}
|
||||
100
agent/mdraid_linux_test.go
Normal file
100
agent/mdraid_linux_test.go
Normal file
@@ -0,0 +1,100 @@
|
||||
//go:build linux
|
||||
|
||||
package agent
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/henrygd/beszel/internal/entities/smart"
|
||||
)
|
||||
|
||||
func TestMdraidMockSysfsScanAndCollect(t *testing.T) {
|
||||
tmp := t.TempDir()
|
||||
prev := mdraidSysfsRoot
|
||||
mdraidSysfsRoot = tmp
|
||||
t.Cleanup(func() { mdraidSysfsRoot = prev })
|
||||
|
||||
mdDir := filepath.Join(tmp, "block", "md0", "md")
|
||||
queueDir := filepath.Join(tmp, "block", "md0", "queue")
|
||||
if err := os.MkdirAll(mdDir, 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.MkdirAll(queueDir, 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
write := func(path, content string) {
|
||||
t.Helper()
|
||||
if err := os.WriteFile(path, []byte(content), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
write(filepath.Join(mdDir, "array_state"), "active\n")
|
||||
write(filepath.Join(mdDir, "level"), "raid1\n")
|
||||
write(filepath.Join(mdDir, "raid_disks"), "2\n")
|
||||
write(filepath.Join(mdDir, "degraded"), "0\n")
|
||||
write(filepath.Join(mdDir, "sync_action"), "resync\n")
|
||||
write(filepath.Join(mdDir, "sync_completed"), "10%\n")
|
||||
write(filepath.Join(mdDir, "sync_speed"), "100M\n")
|
||||
write(filepath.Join(mdDir, "mismatch_cnt"), "0\n")
|
||||
write(filepath.Join(queueDir, "logical_block_size"), "512\n")
|
||||
write(filepath.Join(tmp, "block", "md0", "size"), "2048\n")
|
||||
|
||||
devs := scanMdraidDevices()
|
||||
if len(devs) != 1 {
|
||||
t.Fatalf("scanMdraidDevices() = %d devices, want 1", len(devs))
|
||||
}
|
||||
if devs[0].Name != "/dev/md0" || devs[0].Type != "mdraid" {
|
||||
t.Fatalf("scanMdraidDevices()[0] = %+v, want Name=/dev/md0 Type=mdraid", devs[0])
|
||||
}
|
||||
|
||||
sm := &SmartManager{SmartDataMap: map[string]*smart.SmartData{}}
|
||||
ok, err := sm.collectMdraidHealth(devs[0])
|
||||
if err != nil || !ok {
|
||||
t.Fatalf("collectMdraidHealth() = (ok=%v, err=%v), want (true,nil)", ok, err)
|
||||
}
|
||||
if len(sm.SmartDataMap) != 1 {
|
||||
t.Fatalf("SmartDataMap len=%d, want 1", len(sm.SmartDataMap))
|
||||
}
|
||||
var got *smart.SmartData
|
||||
for _, v := range sm.SmartDataMap {
|
||||
got = v
|
||||
break
|
||||
}
|
||||
if got == nil {
|
||||
t.Fatalf("SmartDataMap value nil")
|
||||
}
|
||||
if got.DiskType != "mdraid" || got.DiskName != "/dev/md0" {
|
||||
t.Fatalf("disk fields = (type=%q name=%q), want (mdraid,/dev/md0)", got.DiskType, got.DiskName)
|
||||
}
|
||||
if got.SmartStatus != "WARNING" {
|
||||
t.Fatalf("SmartStatus=%q, want WARNING", got.SmartStatus)
|
||||
}
|
||||
if got.ModelName == "" || got.Capacity == 0 {
|
||||
t.Fatalf("identity fields = (model=%q cap=%d), want non-empty model and cap>0", got.ModelName, got.Capacity)
|
||||
}
|
||||
if len(got.Attributes) < 5 {
|
||||
t.Fatalf("attributes len=%d, want >= 5", len(got.Attributes))
|
||||
}
|
||||
}
|
||||
|
||||
func TestMdraidSmartStatus(t *testing.T) {
|
||||
if got := mdraidSmartStatus(mdraidHealth{arrayState: "inactive"}); got != "FAILED" {
|
||||
t.Fatalf("mdraidSmartStatus(inactive) = %q, want FAILED", got)
|
||||
}
|
||||
if got := mdraidSmartStatus(mdraidHealth{arrayState: "active", degraded: 1}); got != "FAILED" {
|
||||
t.Fatalf("mdraidSmartStatus(degraded) = %q, want FAILED", got)
|
||||
}
|
||||
if got := mdraidSmartStatus(mdraidHealth{arrayState: "active", syncAction: "recover"}); got != "WARNING" {
|
||||
t.Fatalf("mdraidSmartStatus(recover) = %q, want WARNING", got)
|
||||
}
|
||||
if got := mdraidSmartStatus(mdraidHealth{arrayState: "clean"}); got != "PASSED" {
|
||||
t.Fatalf("mdraidSmartStatus(clean) = %q, want PASSED", got)
|
||||
}
|
||||
if got := mdraidSmartStatus(mdraidHealth{arrayState: "unknown"}); got != "UNKNOWN" {
|
||||
t.Fatalf("mdraidSmartStatus(unknown) = %q, want UNKNOWN", got)
|
||||
}
|
||||
}
|
||||
11
agent/mdraid_stub.go
Normal file
11
agent/mdraid_stub.go
Normal file
@@ -0,0 +1,11 @@
|
||||
//go:build !linux
|
||||
|
||||
package agent
|
||||
|
||||
func scanMdraidDevices() []*DeviceInfo {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sm *SmartManager) collectMdraidHealth(deviceInfo *DeviceInfo) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build testing
|
||||
// +build testing
|
||||
|
||||
package agent
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build testing
|
||||
// +build testing
|
||||
|
||||
package agent
|
||||
|
||||
|
||||
@@ -199,6 +199,13 @@ func (sm *SmartManager) ScanDevices(force bool) error {
|
||||
hasValidScan = true
|
||||
}
|
||||
|
||||
// Add Linux mdraid arrays by reading sysfs health fields. This does not
|
||||
// require smartctl and does not scan the whole device.
|
||||
if raidDevices := scanMdraidDevices(); len(raidDevices) > 0 {
|
||||
scannedDevices = append(scannedDevices, raidDevices...)
|
||||
hasValidScan = true
|
||||
}
|
||||
|
||||
finalDevices := mergeDeviceLists(currentDevices, scannedDevices, configuredDevices)
|
||||
finalDevices = sm.filterExcludedDevices(finalDevices)
|
||||
sm.updateSmartDevices(finalDevices)
|
||||
@@ -450,6 +457,12 @@ func (sm *SmartManager) CollectSmart(deviceInfo *DeviceInfo) error {
|
||||
return errNoValidSmartData
|
||||
}
|
||||
|
||||
// mdraid health is not exposed via SMART; Linux exposes array state in sysfs.
|
||||
if deviceInfo != nil {
|
||||
if ok, err := sm.collectMdraidHealth(deviceInfo); ok {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// eMMC health is not exposed via SMART on Linux, but the kernel provides
|
||||
// wear / EOL indicators via sysfs. Prefer that path when available.
|
||||
if deviceInfo != nil {
|
||||
@@ -476,7 +489,7 @@ func (sm *SmartManager) CollectSmart(deviceInfo *DeviceInfo) error {
|
||||
output, err := cmd.CombinedOutput()
|
||||
|
||||
// Check if device is in standby (exit status 2)
|
||||
if exitErr, ok := err.(*exec.ExitError); ok && exitErr.ExitCode() == 2 {
|
||||
if exitErr, ok := errors.AsType[*exec.ExitError](err); ok && exitErr.ExitCode() == 2 {
|
||||
if hasExistingData {
|
||||
// Device is in standby and we have cached data, keep using cache
|
||||
return nil
|
||||
@@ -1146,9 +1159,11 @@ func NewSmartManager() (*SmartManager, error) {
|
||||
slog.Debug("smartctl", "path", path, "err", err)
|
||||
if err != nil {
|
||||
// Keep the previous fail-fast behavior unless this Linux host exposes
|
||||
// eMMC health via sysfs, in which case smartctl is optional.
|
||||
if runtime.GOOS == "linux" && len(scanEmmcDevices()) > 0 {
|
||||
return sm, nil
|
||||
// eMMC or mdraid health via sysfs, in which case smartctl is optional.
|
||||
if runtime.GOOS == "linux" {
|
||||
if len(scanEmmcDevices()) > 0 || len(scanMdraidDevices()) > 0 {
|
||||
return sm, nil
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build testing
|
||||
// +build testing
|
||||
|
||||
package agent
|
||||
|
||||
|
||||
@@ -7,12 +7,12 @@ import (
|
||||
"log/slog"
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/henrygd/beszel"
|
||||
"github.com/henrygd/beszel/agent/battery"
|
||||
"github.com/henrygd/beszel/agent/zfs"
|
||||
"github.com/henrygd/beszel/internal/entities/container"
|
||||
"github.com/henrygd/beszel/internal/entities/system"
|
||||
|
||||
@@ -107,7 +107,7 @@ func (a *Agent) refreshSystemDetails() {
|
||||
}
|
||||
|
||||
// zfs
|
||||
if _, err := getARCSize(); err != nil {
|
||||
if _, err := zfs.ARCSize(); err != nil {
|
||||
slog.Debug("Not monitoring ZFS ARC", "err", err)
|
||||
} else {
|
||||
a.zfs = true
|
||||
@@ -178,7 +178,7 @@ func (a *Agent) getSystemStats(cacheTimeMs uint16) system.Stats {
|
||||
// }
|
||||
// subtract ZFS ARC size from used memory and add as its own category
|
||||
if a.zfs {
|
||||
if arcSize, _ := getARCSize(); arcSize > 0 && arcSize < v.Used {
|
||||
if arcSize, _ := zfs.ARCSize(); arcSize > 0 && arcSize < v.Used {
|
||||
v.Used = v.Used - arcSize
|
||||
v.UsedPercent = float64(v.Used) / float64(v.Total) * 100.0
|
||||
systemStats.MemZfsArc = bytesToGigabytes(arcSize)
|
||||
@@ -250,32 +250,6 @@ func (a *Agent) getSystemStats(cacheTimeMs uint16) system.Stats {
|
||||
return systemStats
|
||||
}
|
||||
|
||||
// Returns the size of the ZFS ARC memory cache in bytes
|
||||
func getARCSize() (uint64, error) {
|
||||
file, err := os.Open("/proc/spl/kstat/zfs/arcstats")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// Scan the lines
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if strings.HasPrefix(line, "size") {
|
||||
// Example line: size 4 15032385536
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) < 3 {
|
||||
return 0, err
|
||||
}
|
||||
// Return the size as uint64
|
||||
return strconv.ParseUint(fields[2], 10, 64)
|
||||
}
|
||||
}
|
||||
|
||||
return 0, fmt.Errorf("failed to parse size field")
|
||||
}
|
||||
|
||||
// getOsPrettyName attempts to get the pretty OS name from /etc/os-release on Linux systems
|
||||
func getOsPrettyName() (string, error) {
|
||||
file, err := os.Open("/etc/os-release")
|
||||
|
||||
11
agent/zfs/zfs_freebsd.go
Normal file
11
agent/zfs/zfs_freebsd.go
Normal file
@@ -0,0 +1,11 @@
|
||||
//go:build freebsd
|
||||
|
||||
package zfs
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func ARCSize() (uint64, error) {
|
||||
return unix.SysctlUint64("kstat.zfs.misc.arcstats.size")
|
||||
}
|
||||
34
agent/zfs/zfs_linux.go
Normal file
34
agent/zfs/zfs_linux.go
Normal file
@@ -0,0 +1,34 @@
|
||||
//go:build linux
|
||||
|
||||
// Package zfs provides functions to read ZFS statistics.
|
||||
package zfs
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func ARCSize() (uint64, error) {
|
||||
file, err := os.Open("/proc/spl/kstat/zfs/arcstats")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if strings.HasPrefix(line, "size") {
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) < 3 {
|
||||
return 0, fmt.Errorf("unexpected arcstats size format: %s", line)
|
||||
}
|
||||
return strconv.ParseUint(fields[2], 10, 64)
|
||||
}
|
||||
}
|
||||
|
||||
return 0, fmt.Errorf("size field not found in arcstats")
|
||||
}
|
||||
9
agent/zfs/zfs_unsupported.go
Normal file
9
agent/zfs/zfs_unsupported.go
Normal file
@@ -0,0 +1,9 @@
|
||||
//go:build !linux && !freebsd
|
||||
|
||||
package zfs
|
||||
|
||||
import "errors"
|
||||
|
||||
func ARCSize() (uint64, error) {
|
||||
return 0, errors.ErrUnsupported
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import "github.com/blang/semver"
|
||||
|
||||
const (
|
||||
// Version is the current version of the application.
|
||||
Version = "0.18.3"
|
||||
Version = "0.18.4"
|
||||
// AppName is the name of the application.
|
||||
AppName = "beszel"
|
||||
)
|
||||
|
||||
30
go.mod
30
go.mod
@@ -1,6 +1,6 @@
|
||||
module github.com/henrygd/beszel
|
||||
|
||||
go 1.25.7
|
||||
go 1.26.0
|
||||
|
||||
require (
|
||||
github.com/blang/semver v3.5.1+incompatible
|
||||
@@ -11,17 +11,17 @@ require (
|
||||
github.com/gliderlabs/ssh v0.3.8
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/lxzan/gws v1.8.9
|
||||
github.com/nicholas-fedor/shoutrrr v0.13.1
|
||||
github.com/pocketbase/dbx v1.11.0
|
||||
github.com/pocketbase/pocketbase v0.36.2
|
||||
github.com/nicholas-fedor/shoutrrr v0.13.2
|
||||
github.com/pocketbase/dbx v1.12.0
|
||||
github.com/pocketbase/pocketbase v0.36.4
|
||||
github.com/shirou/gopsutil/v4 v4.26.1
|
||||
github.com/spf13/cast v1.10.0
|
||||
github.com/spf13/cobra v1.10.2
|
||||
github.com/spf13/pflag v1.0.10
|
||||
github.com/stretchr/testify v1.11.1
|
||||
golang.org/x/crypto v0.47.0
|
||||
golang.org/x/exp v0.0.0-20260112195511-716be5621a96
|
||||
golang.org/x/sys v0.40.0
|
||||
golang.org/x/crypto v0.48.0
|
||||
golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa
|
||||
golang.org/x/sys v0.41.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
@@ -42,8 +42,8 @@ require (
|
||||
github.com/godbus/dbus/v5 v5.2.2 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/klauspost/compress v1.18.3 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 // indirect
|
||||
github.com/klauspost/compress v1.18.4 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20260216142805-b3301c5f2a88 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/ncruces/go-strftime v1.0.0 // indirect
|
||||
@@ -54,15 +54,15 @@ require (
|
||||
github.com/tklauser/numcpus v0.11.0 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
golang.org/x/image v0.35.0 // indirect
|
||||
golang.org/x/net v0.49.0 // indirect
|
||||
golang.org/x/oauth2 v0.34.0 // indirect
|
||||
golang.org/x/image v0.36.0 // indirect
|
||||
golang.org/x/net v0.50.0 // indirect
|
||||
golang.org/x/oauth2 v0.35.0 // indirect
|
||||
golang.org/x/sync v0.19.0 // indirect
|
||||
golang.org/x/term v0.39.0 // indirect
|
||||
golang.org/x/text v0.33.0 // indirect
|
||||
golang.org/x/term v0.40.0 // indirect
|
||||
golang.org/x/text v0.34.0 // indirect
|
||||
howett.net/plist v1.0.1 // indirect
|
||||
modernc.org/libc v1.67.6 // indirect
|
||||
modernc.org/mathutil v1.7.1 // indirect
|
||||
modernc.org/memory v1.11.0 // indirect
|
||||
modernc.org/sqlite v1.44.3 // indirect
|
||||
modernc.org/sqlite v1.45.0 // indirect
|
||||
)
|
||||
|
||||
76
go.sum
76
go.sum
@@ -69,14 +69,14 @@ github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLf
|
||||
github.com/jarcoal/httpmock v1.4.1 h1:0Ju+VCFuARfFlhVXFc2HxlcQkfB+Xq12/EotHko+x2A=
|
||||
github.com/jarcoal/httpmock v1.4.1/go.mod h1:ftW1xULwo+j0R0JJkJIIi7UKigZUXCLLanykgjwBXL0=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/klauspost/compress v1.18.3 h1:9PJRvfbmTabkOX8moIpXPbMMbYN60bWImDDU7L+/6zw=
|
||||
github.com/klauspost/compress v1.18.3/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
|
||||
github.com/klauspost/compress v1.18.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c=
|
||||
github.com/klauspost/compress v1.18.4/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 h1:PwQumkgq4/acIiZhtifTV5OUqqiP82UAl0h87xj/l9k=
|
||||
github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg=
|
||||
github.com/lufia/plan9stats v0.0.0-20260216142805-b3301c5f2a88 h1:PTw+yKnXcOFCR6+8hHTyWBeQ/P4Nb7dd4/0ohEcWQuM=
|
||||
github.com/lufia/plan9stats v0.0.0-20260216142805-b3301c5f2a88/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg=
|
||||
github.com/lxzan/gws v1.8.9 h1:VU3SGUeWlQrEwfUSfokcZep8mdg/BrUF+y73YYshdBM=
|
||||
github.com/lxzan/gws v1.8.9/go.mod h1:d9yHaR1eDTBHagQC6KY7ycUOaz5KWeqQtP3xu7aMK8Y=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
@@ -85,19 +85,19 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
|
||||
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/nicholas-fedor/shoutrrr v0.13.1 h1:llEoHNbnMM4GfQ9+2Ns3n6ssvNfi3NPWluM0AQiicoY=
|
||||
github.com/nicholas-fedor/shoutrrr v0.13.1/go.mod h1:kU4cFJpEAtTzl3iV0l+XUXmM90OlC5T01b7roM4/pYM=
|
||||
github.com/onsi/ginkgo/v2 v2.27.3 h1:ICsZJ8JoYafeXFFlFAG75a7CxMsJHwgKwtO+82SE9L8=
|
||||
github.com/onsi/ginkgo/v2 v2.27.3/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo=
|
||||
github.com/onsi/gomega v1.38.3 h1:eTX+W6dobAYfFeGC2PV6RwXRu/MyT+cQguijutvkpSM=
|
||||
github.com/onsi/gomega v1.38.3/go.mod h1:ZCU1pkQcXDO5Sl9/VVEGlDyp+zm0m1cmeG5TOzLgdh4=
|
||||
github.com/nicholas-fedor/shoutrrr v0.13.2 h1:hfsYBIqSFYGg92pZP5CXk/g7/OJIkLYmiUnRl+AD1IA=
|
||||
github.com/nicholas-fedor/shoutrrr v0.13.2/go.mod h1:ZqzV3gY/Wj6AvWs1etlO7+yKbh4iptSbeL8avBpMQbA=
|
||||
github.com/onsi/ginkgo/v2 v2.28.1 h1:S4hj+HbZp40fNKuLUQOYLDgZLwNUVn19N3Atb98NCyI=
|
||||
github.com/onsi/ginkgo/v2 v2.28.1/go.mod h1:CLtbVInNckU3/+gC8LzkGUb9oF+e8W8TdUsxPwvdOgE=
|
||||
github.com/onsi/gomega v1.39.1 h1:1IJLAad4zjPn2PsnhH70V4DKRFlrCzGBNrNaru+Vf28=
|
||||
github.com/onsi/gomega v1.39.1/go.mod h1:hL6yVALoTOxeWudERyfppUcZXjMwIMLnuSfruD2lcfg=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pocketbase/dbx v1.11.0 h1:LpZezioMfT3K4tLrqA55wWFw1EtH1pM4tzSVa7kgszU=
|
||||
github.com/pocketbase/dbx v1.11.0/go.mod h1:xXRCIAKTHMgUCyCKZm55pUOdvFziJjQfXaWKhu2vhMs=
|
||||
github.com/pocketbase/pocketbase v0.36.2 h1:mzrxnvXKc3yxKlvZdbwoYXkH8kfIETteD0hWdgj0VI4=
|
||||
github.com/pocketbase/pocketbase v0.36.2/go.mod h1:71vSF8whUDzC8mcLFE10+Qatf9JQdeOGIRWawOuLLKM=
|
||||
github.com/pocketbase/dbx v1.12.0 h1:/oLErM+A0b4xI0PWTGPqSDVjzix48PqI/bng2l0PzoA=
|
||||
github.com/pocketbase/dbx v1.12.0/go.mod h1:xXRCIAKTHMgUCyCKZm55pUOdvFziJjQfXaWKhu2vhMs=
|
||||
github.com/pocketbase/pocketbase v0.36.4 h1:zTjRZbp2WfTOJJfb+pFRWa200UaQwxZYt8RzkFMlAZ4=
|
||||
github.com/pocketbase/pocketbase v0.36.4/go.mod h1:9CiezhRudd9FZGa5xZa53QZBTNxc5vvw/FGG+diAECI=
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
@@ -129,20 +129,20 @@ github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQ
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
|
||||
golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
|
||||
golang.org/x/exp v0.0.0-20260112195511-716be5621a96 h1:Z/6YuSHTLOHfNFdb8zVZomZr7cqNgTJvA8+Qz75D8gU=
|
||||
golang.org/x/exp v0.0.0-20260112195511-716be5621a96/go.mod h1:nzimsREAkjBCIEFtHiYkrJyT+2uy9YZJB7H1k68CXZU=
|
||||
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
|
||||
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
|
||||
golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa h1:Zt3DZoOFFYkKhDT3v7Lm9FDMEV06GpzjG2jrqW+QTE0=
|
||||
golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa/go.mod h1:K79w1Vqn7PoiZn+TkNpx3BUWUQksGO3JcVX6qIjytmA=
|
||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.35.0 h1:LKjiHdgMtO8z7Fh18nGY6KDcoEtVfsgLDPeLyguqb7I=
|
||||
golang.org/x/image v0.35.0/go.mod h1:MwPLTVgvxSASsxdLzKrl8BRFuyqMyGhLwmC+TO1Sybk=
|
||||
golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c=
|
||||
golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU=
|
||||
golang.org/x/image v0.36.0 h1:Iknbfm1afbgtwPTmHnS2gTM/6PPZfH+z2EFuOkSbqwc=
|
||||
golang.org/x/image v0.36.0/go.mod h1:YsWD2TyyGKiIX1kZlu9QfKIsQ4nAAK9bdgdrIsE7xy4=
|
||||
golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
|
||||
golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
|
||||
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
|
||||
golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
|
||||
golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
|
||||
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
|
||||
golang.org/x/oauth2 v0.35.0 h1:Mv2mzuHuZuY2+bkyWXIHMfhNdJAdwW3FuWeCPYN5GVQ=
|
||||
golang.org/x/oauth2 v0.35.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -150,20 +150,20 @@ golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
||||
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY=
|
||||
golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww=
|
||||
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
|
||||
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg=
|
||||
golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
|
||||
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
|
||||
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
|
||||
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc=
|
||||
golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg=
|
||||
golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
|
||||
golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
@@ -195,8 +195,8 @@ modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
|
||||
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
||||
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
|
||||
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
|
||||
modernc.org/sqlite v1.44.3 h1:+39JvV/HWMcYslAwRxHb8067w+2zowvFOUrOWIy9PjY=
|
||||
modernc.org/sqlite v1.44.3/go.mod h1:CzbrU2lSB1DKUusvwGz7rqEKIq+NUd8GWuBBZDs9/nA=
|
||||
modernc.org/sqlite v1.45.0 h1:r51cSGzKpbptxnby+EIIz5fop4VuE4qFoVEjNvWoObs=
|
||||
modernc.org/sqlite v1.45.0/go.mod h1:CzbrU2lSB1DKUusvwGz7rqEKIq+NUd8GWuBBZDs9/nA=
|
||||
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
|
||||
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build testing
|
||||
// +build testing
|
||||
|
||||
package alerts_test
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build testing
|
||||
// +build testing
|
||||
|
||||
package alerts_test
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build testing
|
||||
// +build testing
|
||||
|
||||
package alerts_test
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ func (am *AlertManager) HandleSystemAlerts(systemRecord *core.Record, data *syst
|
||||
case "Memory":
|
||||
val = data.Info.MemPct
|
||||
case "Bandwidth":
|
||||
val = data.Info.Bandwidth
|
||||
val = float64(data.Info.BandwidthBytes) / (1024 * 1024)
|
||||
unit = " MB/s"
|
||||
case "Disk":
|
||||
maxUsedPct := data.Info.DiskPct
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build testing
|
||||
// +build testing
|
||||
|
||||
package alerts_test
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build testing
|
||||
// +build testing
|
||||
|
||||
package alerts
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ func ColorPrint(color, text string) {
|
||||
fmt.Println(color + text + colorReset)
|
||||
}
|
||||
|
||||
func ColorPrintf(color, format string, args ...interface{}) {
|
||||
func ColorPrintf(color, format string, args ...any) {
|
||||
fmt.Printf(color+format+colorReset+"\n", args...)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build testing
|
||||
// +build testing
|
||||
|
||||
package hub
|
||||
|
||||
@@ -10,6 +9,7 @@ import (
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -35,6 +35,26 @@ func createTestHub(t testing.TB) (*Hub, *pbtests.TestApp, error) {
|
||||
return NewHub(testApp), testApp, nil
|
||||
}
|
||||
|
||||
// cleanupTestHub stops background system goroutines before tearing down the app.
|
||||
func cleanupTestHub(hub *Hub, testApp *pbtests.TestApp) {
|
||||
if hub != nil {
|
||||
sm := hub.GetSystemManager()
|
||||
sm.RemoveAllSystems()
|
||||
// Give updater goroutines a brief window to observe cancellation before DB teardown.
|
||||
for range 20 {
|
||||
if sm.GetSystemCount() == 0 {
|
||||
break
|
||||
}
|
||||
runtime.Gosched()
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
}
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
}
|
||||
if testApp != nil {
|
||||
testApp.Cleanup()
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to create a test record
|
||||
func createTestRecord(app core.App, collection string, data map[string]any) (*core.Record, error) {
|
||||
col, err := app.FindCachedCollectionByNameOrId(collection)
|
||||
@@ -64,7 +84,7 @@ func TestValidateAgentHeaders(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer testApp.Cleanup()
|
||||
defer cleanupTestHub(hub, testApp)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
@@ -145,7 +165,7 @@ func TestGetAllFingerprintRecordsByToken(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer testApp.Cleanup()
|
||||
defer cleanupTestHub(hub, testApp)
|
||||
|
||||
// create test user
|
||||
userRecord, err := createTestUser(testApp)
|
||||
@@ -235,7 +255,7 @@ func TestSetFingerprint(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer testApp.Cleanup()
|
||||
defer cleanupTestHub(hub, testApp)
|
||||
|
||||
// Create test user
|
||||
userRecord, err := createTestUser(testApp)
|
||||
@@ -315,7 +335,7 @@ func TestCreateSystemFromAgentData(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer testApp.Cleanup()
|
||||
defer cleanupTestHub(hub, testApp)
|
||||
|
||||
// Create test user
|
||||
userRecord, err := createTestUser(testApp)
|
||||
@@ -425,7 +445,7 @@ func TestUniversalTokenFlow(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer testApp.Cleanup()
|
||||
defer cleanupTestHub(nil, testApp)
|
||||
|
||||
// Create test user
|
||||
userRecord, err := createTestUser(testApp)
|
||||
@@ -493,7 +513,7 @@ func TestAgentConnect(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer testApp.Cleanup()
|
||||
defer cleanupTestHub(hub, testApp)
|
||||
|
||||
// Create test user
|
||||
userRecord, err := createTestUser(testApp)
|
||||
@@ -652,7 +672,7 @@ func TestHandleAgentConnect(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer testApp.Cleanup()
|
||||
defer cleanupTestHub(hub, testApp)
|
||||
|
||||
// Create test user
|
||||
userRecord, err := createTestUser(testApp)
|
||||
@@ -737,7 +757,7 @@ func TestAgentWebSocketIntegration(t *testing.T) {
|
||||
// Create hub and test app
|
||||
hub, testApp, err := createTestHub(t)
|
||||
require.NoError(t, err)
|
||||
defer testApp.Cleanup()
|
||||
defer cleanupTestHub(hub, testApp)
|
||||
|
||||
// Get the hub's SSH key
|
||||
hubSigner, err := hub.GetSSHKey("")
|
||||
@@ -942,6 +962,8 @@ func TestAgentWebSocketIntegration(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
|
||||
// Verify fingerprint state by re-reading the specific record
|
||||
updatedFingerprintRecord, err := testApp.FindRecordById("fingerprints", fingerprintRecord.Id)
|
||||
require.NoError(t, err)
|
||||
@@ -976,7 +998,7 @@ func TestMultipleSystemsWithSameUniversalToken(t *testing.T) {
|
||||
// Create hub and test app
|
||||
hub, testApp, err := createTestHub(t)
|
||||
require.NoError(t, err)
|
||||
defer testApp.Cleanup()
|
||||
defer cleanupTestHub(hub, testApp)
|
||||
|
||||
// Get the hub's SSH key
|
||||
hubSigner, err := hub.GetSSHKey("")
|
||||
@@ -1144,6 +1166,8 @@ func TestMultipleSystemsWithSameUniversalToken(t *testing.T) {
|
||||
assert.Equal(t, systemCount, systemsAfterCount, "Total system count should remain the same")
|
||||
}
|
||||
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
|
||||
// Verify that a fingerprint record exists for this fingerprint
|
||||
fingerprints, err := testApp.FindRecordsByFilter("fingerprints", "token = {:token} && fingerprint = {:fingerprint}", "", -1, 0, map[string]any{
|
||||
"token": universalToken,
|
||||
@@ -1176,7 +1200,7 @@ func TestPermanentUniversalTokenFromDB(t *testing.T) {
|
||||
// Create hub and test app
|
||||
hub, testApp, err := createTestHub(t)
|
||||
require.NoError(t, err)
|
||||
defer testApp.Cleanup()
|
||||
defer cleanupTestHub(hub, testApp)
|
||||
|
||||
// Get the hub's SSH key
|
||||
hubSigner, err := hub.GetSSHKey("")
|
||||
@@ -1273,7 +1297,7 @@ verify:
|
||||
func TestFindOrCreateSystemForToken(t *testing.T) {
|
||||
hub, testApp, err := createTestHub(t)
|
||||
require.NoError(t, err)
|
||||
defer testApp.Cleanup()
|
||||
defer cleanupTestHub(hub, testApp)
|
||||
|
||||
// Create test user
|
||||
userRecord, err := createTestUser(testApp)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build testing
|
||||
// +build testing
|
||||
|
||||
package config_test
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build testing
|
||||
// +build testing
|
||||
|
||||
package expirymap
|
||||
|
||||
|
||||
303
internal/hub/heartbeat/heartbeat.go
Normal file
303
internal/hub/heartbeat/heartbeat.go
Normal file
@@ -0,0 +1,303 @@
|
||||
// Package heartbeat sends periodic outbound pings to an external monitoring
|
||||
// endpoint (e.g. BetterStack, Uptime Kuma, Healthchecks.io) so operators can
|
||||
// monitor Beszel without exposing it to the internet.
|
||||
package heartbeat
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/henrygd/beszel"
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
)
|
||||
|
||||
// Default values for heartbeat configuration.
|
||||
const (
|
||||
defaultInterval = 60 // seconds
|
||||
httpTimeout = 10 * time.Second
|
||||
)
|
||||
|
||||
// Payload is the JSON body sent with each heartbeat request.
|
||||
type Payload struct {
|
||||
// Status is "ok" when all non-paused systems are up, "warn" when alerts
|
||||
// are triggered but no systems are down, and "error" when any system is down.
|
||||
Status string `json:"status"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
Msg string `json:"msg"`
|
||||
Systems SystemsSummary `json:"systems"`
|
||||
Down []SystemInfo `json:"down_systems,omitempty"`
|
||||
Alerts []AlertInfo `json:"triggered_alerts,omitempty"`
|
||||
Version string `json:"beszel_version"`
|
||||
}
|
||||
|
||||
// SystemsSummary contains counts of systems by status.
|
||||
type SystemsSummary struct {
|
||||
Total int `json:"total"`
|
||||
Up int `json:"up"`
|
||||
Down int `json:"down"`
|
||||
Paused int `json:"paused"`
|
||||
Pending int `json:"pending"`
|
||||
}
|
||||
|
||||
// SystemInfo identifies a system that is currently down.
|
||||
type SystemInfo struct {
|
||||
ID string `json:"id" db:"id"`
|
||||
Name string `json:"name" db:"name"`
|
||||
Host string `json:"host" db:"host"`
|
||||
}
|
||||
|
||||
// AlertInfo describes a currently triggered alert.
|
||||
type AlertInfo struct {
|
||||
SystemID string `json:"system_id"`
|
||||
SystemName string `json:"system_name"`
|
||||
AlertName string `json:"alert_name"`
|
||||
Threshold float64 `json:"threshold"`
|
||||
}
|
||||
|
||||
// Config holds heartbeat settings read from environment variables.
|
||||
type Config struct {
|
||||
URL string // endpoint to ping
|
||||
Interval int // seconds between pings
|
||||
Method string // HTTP method (GET or POST, default POST)
|
||||
}
|
||||
|
||||
// Heartbeat manages the periodic outbound health check.
|
||||
type Heartbeat struct {
|
||||
app core.App
|
||||
config Config
|
||||
client *http.Client
|
||||
}
|
||||
|
||||
// New creates a Heartbeat if configuration is present.
|
||||
// Returns nil if HEARTBEAT_URL is not set (feature disabled).
|
||||
func New(app core.App, getEnv func(string) (string, bool)) *Heartbeat {
|
||||
url, _ := getEnv("HEARTBEAT_URL")
|
||||
url = strings.TrimSpace(url)
|
||||
if app == nil || url == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
interval := defaultInterval
|
||||
if v, ok := getEnv("HEARTBEAT_INTERVAL"); ok {
|
||||
if parsed, err := strconv.Atoi(v); err == nil && parsed > 0 {
|
||||
interval = parsed
|
||||
}
|
||||
}
|
||||
|
||||
method := http.MethodPost
|
||||
if v, ok := getEnv("HEARTBEAT_METHOD"); ok {
|
||||
v = strings.ToUpper(strings.TrimSpace(v))
|
||||
if v == http.MethodGet || v == http.MethodHead {
|
||||
method = v
|
||||
}
|
||||
}
|
||||
|
||||
return &Heartbeat{
|
||||
app: app,
|
||||
config: Config{
|
||||
URL: url,
|
||||
Interval: interval,
|
||||
Method: method,
|
||||
},
|
||||
client: &http.Client{Timeout: httpTimeout},
|
||||
}
|
||||
}
|
||||
|
||||
// Start begins the heartbeat loop. It blocks and should be called in a goroutine.
|
||||
// The loop runs until the provided stop channel is closed.
|
||||
func (hb *Heartbeat) Start(stop <-chan struct{}) {
|
||||
sanitizedURL := sanitizeHeartbeatURL(hb.config.URL)
|
||||
hb.app.Logger().Info("Heartbeat enabled",
|
||||
"url", sanitizedURL,
|
||||
"interval", fmt.Sprintf("%ds", hb.config.Interval),
|
||||
"method", hb.config.Method,
|
||||
)
|
||||
|
||||
// Send an initial heartbeat immediately on startup.
|
||||
hb.send()
|
||||
|
||||
ticker := time.NewTicker(time.Duration(hb.config.Interval) * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-stop:
|
||||
return
|
||||
case <-ticker.C:
|
||||
hb.send()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Send performs a single heartbeat ping. Exposed for the test-heartbeat API endpoint.
|
||||
func (hb *Heartbeat) Send() error {
|
||||
return hb.send()
|
||||
}
|
||||
|
||||
// GetConfig returns the current heartbeat configuration.
|
||||
func (hb *Heartbeat) GetConfig() Config {
|
||||
return hb.config
|
||||
}
|
||||
|
||||
func (hb *Heartbeat) send() error {
|
||||
var req *http.Request
|
||||
var err error
|
||||
method := normalizeMethod(hb.config.Method)
|
||||
|
||||
if method == http.MethodGet || method == http.MethodHead {
|
||||
req, err = http.NewRequest(method, hb.config.URL, nil)
|
||||
} else {
|
||||
payload, payloadErr := hb.buildPayload()
|
||||
if payloadErr != nil {
|
||||
hb.app.Logger().Error("Heartbeat: failed to build payload", "err", payloadErr)
|
||||
return payloadErr
|
||||
}
|
||||
|
||||
body, jsonErr := json.Marshal(payload)
|
||||
if jsonErr != nil {
|
||||
hb.app.Logger().Error("Heartbeat: failed to marshal payload", "err", jsonErr)
|
||||
return jsonErr
|
||||
}
|
||||
req, err = http.NewRequest(http.MethodPost, hb.config.URL, bytes.NewReader(body))
|
||||
if err == nil {
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
hb.app.Logger().Error("Heartbeat: failed to create request", "err", err)
|
||||
return err
|
||||
}
|
||||
|
||||
req.Header.Set("User-Agent", "Beszel-Heartbeat")
|
||||
|
||||
resp, err := hb.client.Do(req)
|
||||
if err != nil {
|
||||
hb.app.Logger().Error("Heartbeat: request failed", "url", sanitizeHeartbeatURL(hb.config.URL), "err", err)
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode >= 400 {
|
||||
hb.app.Logger().Warn("Heartbeat: non-success response",
|
||||
"url", sanitizeHeartbeatURL(hb.config.URL),
|
||||
"status", resp.StatusCode,
|
||||
)
|
||||
return fmt.Errorf("heartbeat endpoint returned status %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hb *Heartbeat) buildPayload() (*Payload, error) {
|
||||
db := hb.app.DB()
|
||||
|
||||
// Count systems by status.
|
||||
var systemCounts []struct {
|
||||
Status string `db:"status"`
|
||||
Count int `db:"cnt"`
|
||||
}
|
||||
err := db.NewQuery("SELECT status, COUNT(*) as cnt FROM systems GROUP BY status").All(&systemCounts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("query system counts: %w", err)
|
||||
}
|
||||
|
||||
summary := SystemsSummary{}
|
||||
for _, sc := range systemCounts {
|
||||
switch sc.Status {
|
||||
case "up":
|
||||
summary.Up = sc.Count
|
||||
case "down":
|
||||
summary.Down = sc.Count
|
||||
case "paused":
|
||||
summary.Paused = sc.Count
|
||||
case "pending":
|
||||
summary.Pending = sc.Count
|
||||
}
|
||||
summary.Total += sc.Count
|
||||
}
|
||||
|
||||
// Get names of down systems.
|
||||
var downSystems []SystemInfo
|
||||
if summary.Down > 0 {
|
||||
err = db.NewQuery("SELECT id, name, host FROM systems WHERE status = 'down'").All(&downSystems)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("query down systems: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Get triggered alerts with system names.
|
||||
var triggeredAlerts []struct {
|
||||
SystemID string `db:"system"`
|
||||
SystemName string `db:"system_name"`
|
||||
AlertName string `db:"name"`
|
||||
Value float64 `db:"value"`
|
||||
}
|
||||
err = db.NewQuery(`
|
||||
SELECT a.system, s.name as system_name, a.name, a.value
|
||||
FROM alerts a
|
||||
JOIN systems s ON a.system = s.id
|
||||
WHERE a.triggered = true
|
||||
`).All(&triggeredAlerts)
|
||||
if err != nil {
|
||||
// Non-fatal: alerts info is supplementary.
|
||||
triggeredAlerts = nil
|
||||
}
|
||||
|
||||
alerts := make([]AlertInfo, 0, len(triggeredAlerts))
|
||||
for _, ta := range triggeredAlerts {
|
||||
alerts = append(alerts, AlertInfo{
|
||||
SystemID: ta.SystemID,
|
||||
SystemName: ta.SystemName,
|
||||
AlertName: ta.AlertName,
|
||||
Threshold: ta.Value,
|
||||
})
|
||||
}
|
||||
|
||||
// Determine overall status.
|
||||
status := "ok"
|
||||
msg := "All systems operational"
|
||||
if summary.Down > 0 {
|
||||
status = "error"
|
||||
names := make([]string, len(downSystems))
|
||||
for i, ds := range downSystems {
|
||||
names[i] = ds.Name
|
||||
}
|
||||
msg = fmt.Sprintf("%d system(s) down: %s", summary.Down, strings.Join(names, ", "))
|
||||
} else if len(alerts) > 0 {
|
||||
status = "warn"
|
||||
msg = fmt.Sprintf("%d alert(s) triggered", len(alerts))
|
||||
}
|
||||
|
||||
return &Payload{
|
||||
Status: status,
|
||||
Timestamp: time.Now().UTC().Format(time.RFC3339),
|
||||
Msg: msg,
|
||||
Systems: summary,
|
||||
Down: downSystems,
|
||||
Alerts: alerts,
|
||||
Version: beszel.Version,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func normalizeMethod(method string) string {
|
||||
upper := strings.ToUpper(strings.TrimSpace(method))
|
||||
if upper == http.MethodGet || upper == http.MethodHead || upper == http.MethodPost {
|
||||
return upper
|
||||
}
|
||||
return http.MethodPost
|
||||
}
|
||||
|
||||
func sanitizeHeartbeatURL(rawURL string) string {
|
||||
parsed, err := url.Parse(strings.TrimSpace(rawURL))
|
||||
if err != nil || parsed.Scheme == "" || parsed.Host == "" {
|
||||
return "<invalid-url>"
|
||||
}
|
||||
return parsed.Scheme + "://" + parsed.Host
|
||||
}
|
||||
257
internal/hub/heartbeat/heartbeat_test.go
Normal file
257
internal/hub/heartbeat/heartbeat_test.go
Normal file
@@ -0,0 +1,257 @@
|
||||
//go:build testing
|
||||
|
||||
package heartbeat_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/henrygd/beszel/internal/hub/heartbeat"
|
||||
beszeltests "github.com/henrygd/beszel/internal/tests"
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
t.Run("returns nil when app is missing", func(t *testing.T) {
|
||||
hb := heartbeat.New(nil, envGetter(map[string]string{
|
||||
"HEARTBEAT_URL": "https://heartbeat.example.com/ping",
|
||||
}))
|
||||
assert.Nil(t, hb)
|
||||
})
|
||||
|
||||
t.Run("returns nil when URL is missing", func(t *testing.T) {
|
||||
app := newTestHub(t)
|
||||
hb := heartbeat.New(app.App, func(string) (string, bool) {
|
||||
return "", false
|
||||
})
|
||||
assert.Nil(t, hb)
|
||||
})
|
||||
|
||||
t.Run("parses and normalizes config values", func(t *testing.T) {
|
||||
app := newTestHub(t)
|
||||
env := map[string]string{
|
||||
"HEARTBEAT_URL": " https://heartbeat.example.com/ping ",
|
||||
"HEARTBEAT_INTERVAL": "90",
|
||||
"HEARTBEAT_METHOD": "head",
|
||||
}
|
||||
getEnv := func(key string) (string, bool) {
|
||||
v, ok := env[key]
|
||||
return v, ok
|
||||
}
|
||||
|
||||
hb := heartbeat.New(app.App, getEnv)
|
||||
require.NotNil(t, hb)
|
||||
cfg := hb.GetConfig()
|
||||
assert.Equal(t, "https://heartbeat.example.com/ping", cfg.URL)
|
||||
assert.Equal(t, 90, cfg.Interval)
|
||||
assert.Equal(t, http.MethodHead, cfg.Method)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSendGETDoesNotRequireAppOrDB(t *testing.T) {
|
||||
app := newTestHub(t)
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equal(t, http.MethodGet, r.Method)
|
||||
assert.Equal(t, "Beszel-Heartbeat", r.Header.Get("User-Agent"))
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
hb := heartbeat.New(app.App, envGetter(map[string]string{
|
||||
"HEARTBEAT_URL": server.URL,
|
||||
"HEARTBEAT_METHOD": "GET",
|
||||
}))
|
||||
require.NotNil(t, hb)
|
||||
|
||||
require.NoError(t, hb.Send())
|
||||
}
|
||||
|
||||
func TestSendReturnsErrorOnHTTPFailureStatus(t *testing.T) {
|
||||
app := newTestHub(t)
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
hb := heartbeat.New(app.App, envGetter(map[string]string{
|
||||
"HEARTBEAT_URL": server.URL,
|
||||
"HEARTBEAT_METHOD": "GET",
|
||||
}))
|
||||
require.NotNil(t, hb)
|
||||
|
||||
err := hb.Send()
|
||||
require.Error(t, err)
|
||||
assert.ErrorContains(t, err, "heartbeat endpoint returned status 500")
|
||||
}
|
||||
|
||||
func TestSendPOSTBuildsExpectedStatuses(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
setup func(t *testing.T, app *beszeltests.TestHub, user *core.Record)
|
||||
expectStatus string
|
||||
expectMsgPart string
|
||||
expectDown int
|
||||
expectAlerts int
|
||||
expectTotal int
|
||||
expectUp int
|
||||
expectPaused int
|
||||
expectPending int
|
||||
expectDownSumm int
|
||||
}{
|
||||
{
|
||||
name: "error when at least one system is down",
|
||||
setup: func(t *testing.T, app *beszeltests.TestHub, user *core.Record) {
|
||||
downSystem := createTestSystem(t, app, user.Id, "db-1", "10.0.0.1", "down")
|
||||
_ = createTestSystem(t, app, user.Id, "web-1", "10.0.0.2", "up")
|
||||
createTriggeredAlert(t, app, user.Id, downSystem.Id, "CPU", 95)
|
||||
},
|
||||
expectStatus: "error",
|
||||
expectMsgPart: "1 system(s) down",
|
||||
expectDown: 1,
|
||||
expectAlerts: 1,
|
||||
expectTotal: 2,
|
||||
expectUp: 1,
|
||||
expectDownSumm: 1,
|
||||
},
|
||||
{
|
||||
name: "warn when only alerts are triggered",
|
||||
setup: func(t *testing.T, app *beszeltests.TestHub, user *core.Record) {
|
||||
system := createTestSystem(t, app, user.Id, "api-1", "10.1.0.1", "up")
|
||||
createTriggeredAlert(t, app, user.Id, system.Id, "CPU", 90)
|
||||
},
|
||||
expectStatus: "warn",
|
||||
expectMsgPart: "1 alert(s) triggered",
|
||||
expectDown: 0,
|
||||
expectAlerts: 1,
|
||||
expectTotal: 1,
|
||||
expectUp: 1,
|
||||
expectDownSumm: 0,
|
||||
},
|
||||
{
|
||||
name: "ok when no down systems and no alerts",
|
||||
setup: func(t *testing.T, app *beszeltests.TestHub, user *core.Record) {
|
||||
_ = createTestSystem(t, app, user.Id, "node-1", "10.2.0.1", "up")
|
||||
_ = createTestSystem(t, app, user.Id, "node-2", "10.2.0.2", "paused")
|
||||
_ = createTestSystem(t, app, user.Id, "node-3", "10.2.0.3", "pending")
|
||||
},
|
||||
expectStatus: "ok",
|
||||
expectMsgPart: "All systems operational",
|
||||
expectDown: 0,
|
||||
expectAlerts: 0,
|
||||
expectTotal: 3,
|
||||
expectUp: 1,
|
||||
expectPaused: 1,
|
||||
expectPending: 1,
|
||||
expectDownSumm: 0,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
app := newTestHub(t)
|
||||
user := createTestUser(t, app)
|
||||
tt.setup(t, app, user)
|
||||
|
||||
type requestCapture struct {
|
||||
method string
|
||||
userAgent string
|
||||
contentType string
|
||||
payload heartbeat.Payload
|
||||
}
|
||||
|
||||
captured := make(chan requestCapture, 1)
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
body, err := io.ReadAll(r.Body)
|
||||
require.NoError(t, err)
|
||||
|
||||
var payload heartbeat.Payload
|
||||
require.NoError(t, json.Unmarshal(body, &payload))
|
||||
captured <- requestCapture{
|
||||
method: r.Method,
|
||||
userAgent: r.Header.Get("User-Agent"),
|
||||
contentType: r.Header.Get("Content-Type"),
|
||||
payload: payload,
|
||||
}
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
hb := heartbeat.New(app.App, envGetter(map[string]string{
|
||||
"HEARTBEAT_URL": server.URL,
|
||||
"HEARTBEAT_METHOD": "POST",
|
||||
}))
|
||||
require.NotNil(t, hb)
|
||||
require.NoError(t, hb.Send())
|
||||
|
||||
req := <-captured
|
||||
assert.Equal(t, http.MethodPost, req.method)
|
||||
assert.Equal(t, "Beszel-Heartbeat", req.userAgent)
|
||||
assert.Equal(t, "application/json", req.contentType)
|
||||
|
||||
assert.Equal(t, tt.expectStatus, req.payload.Status)
|
||||
assert.Contains(t, req.payload.Msg, tt.expectMsgPart)
|
||||
assert.Equal(t, tt.expectDown, len(req.payload.Down))
|
||||
assert.Equal(t, tt.expectAlerts, len(req.payload.Alerts))
|
||||
assert.Equal(t, tt.expectTotal, req.payload.Systems.Total)
|
||||
assert.Equal(t, tt.expectUp, req.payload.Systems.Up)
|
||||
assert.Equal(t, tt.expectDownSumm, req.payload.Systems.Down)
|
||||
assert.Equal(t, tt.expectPaused, req.payload.Systems.Paused)
|
||||
assert.Equal(t, tt.expectPending, req.payload.Systems.Pending)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func newTestHub(t *testing.T) *beszeltests.TestHub {
|
||||
t.Helper()
|
||||
app, err := beszeltests.NewTestHub(t.TempDir())
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(app.Cleanup)
|
||||
return app
|
||||
}
|
||||
|
||||
func createTestUser(t *testing.T, app *beszeltests.TestHub) *core.Record {
|
||||
t.Helper()
|
||||
user, err := beszeltests.CreateUser(app.App, "admin@example.com", "password123")
|
||||
require.NoError(t, err)
|
||||
return user
|
||||
}
|
||||
|
||||
func createTestSystem(t *testing.T, app *beszeltests.TestHub, userID, name, host, status string) *core.Record {
|
||||
t.Helper()
|
||||
system, err := beszeltests.CreateRecord(app.App, "systems", map[string]any{
|
||||
"name": name,
|
||||
"host": host,
|
||||
"port": "45876",
|
||||
"users": []string{userID},
|
||||
"status": status,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
return system
|
||||
}
|
||||
|
||||
func createTriggeredAlert(t *testing.T, app *beszeltests.TestHub, userID, systemID, name string, threshold float64) *core.Record {
|
||||
t.Helper()
|
||||
alert, err := beszeltests.CreateRecord(app.App, "alerts", map[string]any{
|
||||
"name": name,
|
||||
"system": systemID,
|
||||
"user": userID,
|
||||
"value": threshold,
|
||||
"min": 0,
|
||||
"triggered": true,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
return alert
|
||||
}
|
||||
|
||||
func envGetter(values map[string]string) func(string) (string, bool) {
|
||||
return func(key string) (string, bool) {
|
||||
v, ok := values[key]
|
||||
return v, ok
|
||||
}
|
||||
}
|
||||
@@ -9,12 +9,14 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/henrygd/beszel"
|
||||
"github.com/henrygd/beszel/internal/alerts"
|
||||
"github.com/henrygd/beszel/internal/hub/config"
|
||||
"github.com/henrygd/beszel/internal/hub/heartbeat"
|
||||
"github.com/henrygd/beszel/internal/hub/systems"
|
||||
"github.com/henrygd/beszel/internal/records"
|
||||
"github.com/henrygd/beszel/internal/users"
|
||||
@@ -33,11 +35,15 @@ type Hub struct {
|
||||
um *users.UserManager
|
||||
rm *records.RecordManager
|
||||
sm *systems.SystemManager
|
||||
hb *heartbeat.Heartbeat
|
||||
hbStop chan struct{}
|
||||
pubKey string
|
||||
signer ssh.Signer
|
||||
appURL string
|
||||
}
|
||||
|
||||
var containerIDPattern = regexp.MustCompile(`^[a-fA-F0-9]{12,64}$`)
|
||||
|
||||
// NewHub creates a new Hub instance with default configuration
|
||||
func NewHub(app core.App) *Hub {
|
||||
hub := &Hub{}
|
||||
@@ -48,6 +54,10 @@ func NewHub(app core.App) *Hub {
|
||||
hub.rm = records.NewRecordManager(hub)
|
||||
hub.sm = systems.NewSystemManager(hub)
|
||||
hub.appURL, _ = GetEnv("APP_URL")
|
||||
hub.hb = heartbeat.New(app, GetEnv)
|
||||
if hub.hb != nil {
|
||||
hub.hbStop = make(chan struct{})
|
||||
}
|
||||
return hub
|
||||
}
|
||||
|
||||
@@ -88,6 +98,10 @@ func (h *Hub) StartHub() error {
|
||||
if err := h.sm.Initialize(); err != nil {
|
||||
return err
|
||||
}
|
||||
// start heartbeat if configured
|
||||
if h.hb != nil {
|
||||
go h.hb.Start(h.hbStop)
|
||||
}
|
||||
return e.Next()
|
||||
})
|
||||
|
||||
@@ -287,6 +301,9 @@ func (h *Hub) registerApiRoutes(se *core.ServeEvent) error {
|
||||
})
|
||||
// send test notification
|
||||
apiAuth.POST("/test-notification", h.SendTestNotification)
|
||||
// heartbeat status and test
|
||||
apiAuth.GET("/heartbeat-status", h.getHeartbeatStatus)
|
||||
apiAuth.POST("/test-heartbeat", h.testHeartbeat)
|
||||
// get config.yml content
|
||||
apiAuth.GET("/config-yaml", config.GetYamlConfig)
|
||||
// handle agent websocket connection
|
||||
@@ -403,6 +420,42 @@ func (h *Hub) getUniversalToken(e *core.RequestEvent) error {
|
||||
return e.JSON(http.StatusOK, response)
|
||||
}
|
||||
|
||||
// getHeartbeatStatus returns current heartbeat configuration and whether it's enabled
|
||||
func (h *Hub) getHeartbeatStatus(e *core.RequestEvent) error {
|
||||
if e.Auth.GetString("role") != "admin" {
|
||||
return e.ForbiddenError("Requires admin role", nil)
|
||||
}
|
||||
if h.hb == nil {
|
||||
return e.JSON(http.StatusOK, map[string]any{
|
||||
"enabled": false,
|
||||
"msg": "Set HEARTBEAT_URL to enable outbound heartbeat monitoring",
|
||||
})
|
||||
}
|
||||
cfg := h.hb.GetConfig()
|
||||
return e.JSON(http.StatusOK, map[string]any{
|
||||
"enabled": true,
|
||||
"url": cfg.URL,
|
||||
"interval": cfg.Interval,
|
||||
"method": cfg.Method,
|
||||
})
|
||||
}
|
||||
|
||||
// testHeartbeat triggers a single heartbeat ping and returns the result
|
||||
func (h *Hub) testHeartbeat(e *core.RequestEvent) error {
|
||||
if e.Auth.GetString("role") != "admin" {
|
||||
return e.ForbiddenError("Requires admin role", nil)
|
||||
}
|
||||
if h.hb == nil {
|
||||
return e.JSON(http.StatusOK, map[string]any{
|
||||
"err": "Heartbeat not configured. Set HEARTBEAT_URL environment variable.",
|
||||
})
|
||||
}
|
||||
if err := h.hb.Send(); err != nil {
|
||||
return e.JSON(http.StatusOK, map[string]any{"err": err.Error()})
|
||||
}
|
||||
return e.JSON(http.StatusOK, map[string]any{"err": false})
|
||||
}
|
||||
|
||||
// containerRequestHandler handles both container logs and info requests
|
||||
func (h *Hub) containerRequestHandler(e *core.RequestEvent, fetchFunc func(*systems.System, string) (string, error), responseKey string) error {
|
||||
systemID := e.Request.URL.Query().Get("system")
|
||||
@@ -411,6 +464,9 @@ func (h *Hub) containerRequestHandler(e *core.RequestEvent, fetchFunc func(*syst
|
||||
if systemID == "" || containerID == "" {
|
||||
return e.JSON(http.StatusBadRequest, map[string]string{"error": "system and container parameters are required"})
|
||||
}
|
||||
if !containerIDPattern.MatchString(containerID) {
|
||||
return e.JSON(http.StatusBadRequest, map[string]string{"error": "invalid container parameter"})
|
||||
}
|
||||
|
||||
system, err := h.sm.GetSystem(systemID)
|
||||
if err != nil {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build testing
|
||||
// +build testing
|
||||
|
||||
package hub_test
|
||||
|
||||
@@ -362,6 +361,58 @@ func TestApiRoutesAuthentication(t *testing.T) {
|
||||
ExpectedContent: []string{"test-system"},
|
||||
TestAppFactory: testAppFactory,
|
||||
},
|
||||
{
|
||||
Name: "GET /heartbeat-status - no auth should fail",
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/beszel/heartbeat-status",
|
||||
ExpectedStatus: 401,
|
||||
ExpectedContent: []string{"requires valid"},
|
||||
TestAppFactory: testAppFactory,
|
||||
},
|
||||
{
|
||||
Name: "GET /heartbeat-status - with user auth should fail",
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/beszel/heartbeat-status",
|
||||
Headers: map[string]string{
|
||||
"Authorization": userToken,
|
||||
},
|
||||
ExpectedStatus: 403,
|
||||
ExpectedContent: []string{"Requires admin role"},
|
||||
TestAppFactory: testAppFactory,
|
||||
},
|
||||
{
|
||||
Name: "GET /heartbeat-status - with admin auth should succeed",
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/beszel/heartbeat-status",
|
||||
Headers: map[string]string{
|
||||
"Authorization": adminUserToken,
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{`"enabled":false`},
|
||||
TestAppFactory: testAppFactory,
|
||||
},
|
||||
{
|
||||
Name: "POST /test-heartbeat - with user auth should fail",
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/beszel/test-heartbeat",
|
||||
Headers: map[string]string{
|
||||
"Authorization": userToken,
|
||||
},
|
||||
ExpectedStatus: 403,
|
||||
ExpectedContent: []string{"Requires admin role"},
|
||||
TestAppFactory: testAppFactory,
|
||||
},
|
||||
{
|
||||
Name: "POST /test-heartbeat - with admin auth should report disabled state",
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/beszel/test-heartbeat",
|
||||
Headers: map[string]string{
|
||||
"Authorization": adminUserToken,
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{"Heartbeat not configured"},
|
||||
TestAppFactory: testAppFactory,
|
||||
},
|
||||
{
|
||||
Name: "GET /universal-token - no auth should fail",
|
||||
Method: http.MethodGet,
|
||||
@@ -493,7 +544,7 @@ func TestApiRoutesAuthentication(t *testing.T) {
|
||||
{
|
||||
Name: "GET /containers/logs - with auth but invalid system should fail",
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/beszel/containers/logs?system=invalid-system&container=test-container",
|
||||
URL: "/api/beszel/containers/logs?system=invalid-system&container=0123456789ab",
|
||||
Headers: map[string]string{
|
||||
"Authorization": userToken,
|
||||
},
|
||||
@@ -501,6 +552,39 @@ func TestApiRoutesAuthentication(t *testing.T) {
|
||||
ExpectedContent: []string{"system not found"},
|
||||
TestAppFactory: testAppFactory,
|
||||
},
|
||||
{
|
||||
Name: "GET /containers/logs - traversal container should fail validation",
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/beszel/containers/logs?system=" + system.Id + "&container=..%2F..%2Fversion",
|
||||
Headers: map[string]string{
|
||||
"Authorization": userToken,
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{"invalid container parameter"},
|
||||
TestAppFactory: testAppFactory,
|
||||
},
|
||||
{
|
||||
Name: "GET /containers/info - traversal container should fail validation",
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/beszel/containers/info?system=" + system.Id + "&container=../../version?x=",
|
||||
Headers: map[string]string{
|
||||
"Authorization": userToken,
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{"invalid container parameter"},
|
||||
TestAppFactory: testAppFactory,
|
||||
},
|
||||
{
|
||||
Name: "GET /containers/info - non-hex container should fail validation",
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/beszel/containers/info?system=" + system.Id + "&container=container_name",
|
||||
Headers: map[string]string{
|
||||
"Authorization": userToken,
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{"invalid container parameter"},
|
||||
TestAppFactory: testAppFactory,
|
||||
},
|
||||
|
||||
// Auth Optional Routes - Should work without authentication
|
||||
{
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build testing
|
||||
// +build testing
|
||||
|
||||
package hub
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build !testing
|
||||
// +build !testing
|
||||
|
||||
package systems
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build testing
|
||||
// +build testing
|
||||
|
||||
package systems_test
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build testing
|
||||
// +build testing
|
||||
|
||||
package systems
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build testing
|
||||
// +build testing
|
||||
|
||||
package ws
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build testing
|
||||
// +build testing
|
||||
|
||||
package ws
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build testing
|
||||
// +build testing
|
||||
|
||||
package ws
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build testing
|
||||
// +build testing
|
||||
|
||||
package records_test
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build testing
|
||||
// +build testing
|
||||
|
||||
package records
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"$schema": "https://biomejs.dev/schemas/2.2.3/schema.json",
|
||||
"$schema": "https://biomejs.dev/schemas/2.2.4/schema.json",
|
||||
"vcs": {
|
||||
"enabled": true,
|
||||
"clientKind": "git",
|
||||
@@ -12,7 +12,7 @@
|
||||
"lineWidth": 120,
|
||||
"formatWithErrors": true
|
||||
},
|
||||
"assist": { "actions": { "source": { "organizeImports": "on" } } },
|
||||
"assist": { "actions": { "source": { "organizeImports": "off" } } },
|
||||
"linter": {
|
||||
"enabled": true,
|
||||
"rules": {
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
{
|
||||
"name": "beszel",
|
||||
"private": true,
|
||||
"version": "0.18.3",
|
||||
"version": "0.18.4",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite --host",
|
||||
"build": "lingui extract --overwrite && lingui compile && vite build",
|
||||
"preview": "vite preview",
|
||||
"sync": "lingui extract --overwrite && lingui compile",
|
||||
"sync_no_compile": "lingui extract --overwrite --clean",
|
||||
"sync_and_purge": "lingui extract --overwrite --clean && lingui compile",
|
||||
"format": "biome format --write .",
|
||||
"lint": "biome lint .",
|
||||
|
||||
@@ -26,7 +26,7 @@ export default memo(function AlertsButton({ system }: { system: SystemRecord })
|
||||
/>
|
||||
</Button>
|
||||
</SheetTrigger>
|
||||
<SheetContent className="max-h-full overflow-auto w-150 !max-w-full p-4 sm:p-6">
|
||||
<SheetContent className="max-h-full overflow-auto w-160 !max-w-full p-4 sm:p-6">
|
||||
{opened && <AlertDialogContent system={system} />}
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
|
||||
@@ -7,6 +7,7 @@ import { lazy, memo, Suspense, useMemo, useState } from "react"
|
||||
import { $router, Link } from "@/components/router"
|
||||
import { Checkbox } from "@/components/ui/checkbox"
|
||||
import { DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Switch } from "@/components/ui/switch"
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
||||
import { toast } from "@/components/ui/use-toast"
|
||||
@@ -20,7 +21,7 @@ const Slider = lazy(() => import("@/components/ui/slider"))
|
||||
|
||||
const endpoint = "/api/beszel/user-alerts"
|
||||
|
||||
const alertDebounce = 100
|
||||
const alertDebounce = 400
|
||||
|
||||
const alertKeys = Object.keys(alertInfo) as (keyof typeof alertInfo)[]
|
||||
|
||||
@@ -244,7 +245,7 @@ export function AlertContent({
|
||||
<Suspense fallback={<div className="h-10" />}>
|
||||
{!singleDescription && (
|
||||
<div>
|
||||
<p id={`v${name}`} className="text-sm block h-8">
|
||||
<p id={`v${name}`} className="text-sm block h-6">
|
||||
{alertData.invert ? (
|
||||
<Trans>
|
||||
Average drops below{" "}
|
||||
@@ -263,21 +264,38 @@ export function AlertContent({
|
||||
</Trans>
|
||||
)}
|
||||
</p>
|
||||
<div className="flex gap-3">
|
||||
<div className="flex gap-3 items-center">
|
||||
<Slider
|
||||
aria-labelledby={`v${name}`}
|
||||
defaultValue={[value]}
|
||||
value={[value]}
|
||||
onValueCommit={(val) => sendUpsert(min, val[0])}
|
||||
onValueChange={(val) => setValue(val[0])}
|
||||
step={alertData.step ?? 1}
|
||||
min={alertData.min ?? 1}
|
||||
max={alertData.max ?? 99}
|
||||
/>
|
||||
<Input
|
||||
type="number"
|
||||
value={value}
|
||||
onChange={(e) => {
|
||||
let val = parseFloat(e.target.value)
|
||||
if (!Number.isNaN(val)) {
|
||||
if (alertData.max != null) val = Math.min(val, alertData.max)
|
||||
if (alertData.min != null) val = Math.max(val, alertData.min)
|
||||
setValue(val)
|
||||
sendUpsert(min, val)
|
||||
}
|
||||
}}
|
||||
step={alertData.step ?? 1}
|
||||
min={alertData.min ?? 1}
|
||||
max={alertData.max ?? 99}
|
||||
className="w-16 h-8 text-center px-1"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className={cn(singleDescription && "col-span-full lowercase")}>
|
||||
<p id={`t${name}`} className="text-sm block h-8 first-letter:uppercase">
|
||||
<p id={`t${name}`} className="text-sm block h-6 first-letter:uppercase">
|
||||
{singleDescription && (
|
||||
<>
|
||||
{singleDescription}
|
||||
@@ -289,15 +307,30 @@ export function AlertContent({
|
||||
<Plural value={min} one="minute" other="minutes" />
|
||||
</Trans>
|
||||
</p>
|
||||
<div className="flex gap-3">
|
||||
<div className="flex gap-3 items-center">
|
||||
<Slider
|
||||
aria-labelledby={`v${name}`}
|
||||
defaultValue={[min]}
|
||||
onValueCommit={(minVal) => sendUpsert(minVal[0], value)}
|
||||
aria-labelledby={`t${name}`}
|
||||
value={[min]}
|
||||
onValueCommit={(val) => sendUpsert(val[0], value)}
|
||||
onValueChange={(val) => setMin(val[0])}
|
||||
min={1}
|
||||
max={60}
|
||||
/>
|
||||
<Input
|
||||
type="number"
|
||||
value={min}
|
||||
onChange={(e) => {
|
||||
let val = parseInt(e.target.value, 10)
|
||||
if (!Number.isNaN(val)) {
|
||||
val = Math.max(1, Math.min(val, 60))
|
||||
setMin(val)
|
||||
sendUpsert(val, value)
|
||||
}
|
||||
}}
|
||||
min={1}
|
||||
max={60}
|
||||
className="w-16 h-8 text-center px-1"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Suspense>
|
||||
|
||||
@@ -43,7 +43,7 @@ export function copyDockerCompose(port = "45876", publicKey: string, token: stri
|
||||
|
||||
export function copyDockerRun(port = "45876", publicKey: string, token: string) {
|
||||
copyToClipboard(
|
||||
`docker run -d --name beszel-agent --network host --restart unless-stopped -v /var/run/docker.sock:/var/run/docker.sock:ro -v ./beszel_agent_data:/var/lib/beszel-agent -e KEY="${publicKey}" -e LISTEN=${port} -e TOKEN="${token}" -e HUB_URL="${getHubURL()}" henrygd/beszel-agent`
|
||||
`docker run -d --name beszel-agent --network host --restart unless-stopped -v /var/run/docker.sock:/var/run/docker.sock:ro -v beszel_agent_data:/var/lib/beszel-agent -e KEY="${publicKey}" -e LISTEN=${port} -e TOKEN="${token}" -e HUB_URL="${getHubURL()}" henrygd/beszel-agent`
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,10 @@ export function LangToggle() {
|
||||
className={cn("px-2.5 flex gap-2.5 cursor-pointer", lang === i18n.locale && "bg-accent/70 font-medium")}
|
||||
onClick={() => dynamicActivate(lang)}
|
||||
>
|
||||
<span>{e}</span> {label}
|
||||
<span>
|
||||
{e || <code className="font-mono bg-muted text-[.65em] w-5 h-4 grid place-items-center">{lang}</code>}
|
||||
</span>{" "}
|
||||
{label}
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
</DropdownMenuContent>
|
||||
|
||||
@@ -70,7 +70,16 @@ export default function SettingsProfilePage({ userSettings }: { userSettings: Us
|
||||
<SelectContent>
|
||||
{languages.map(([lang, label, e]) => (
|
||||
<SelectItem key={lang} value={lang}>
|
||||
<span className="me-2.5">{e}</span>
|
||||
<span className="me-2.5">
|
||||
{e || (
|
||||
<code
|
||||
aria-hidden="true"
|
||||
className="font-mono bg-muted text-[.65em] w-5 h-4 inline-grid place-items-center"
|
||||
>
|
||||
{lang}
|
||||
</code>
|
||||
)}
|
||||
</span>
|
||||
{label}
|
||||
</SelectItem>
|
||||
))}
|
||||
|
||||
219
internal/site/src/components/routes/settings/heartbeat.tsx
Normal file
219
internal/site/src/components/routes/settings/heartbeat.tsx
Normal file
@@ -0,0 +1,219 @@
|
||||
import { t } from "@lingui/core/macro"
|
||||
import { Trans } from "@lingui/react/macro"
|
||||
import { redirectPage } from "@nanostores/router"
|
||||
import { LoaderCircleIcon, SendIcon } from "lucide-react"
|
||||
import { useEffect, useState } from "react"
|
||||
import { $router } from "@/components/router"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Separator } from "@/components/ui/separator"
|
||||
import { toast } from "@/components/ui/use-toast"
|
||||
import { isAdmin, pb } from "@/lib/api"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
interface HeartbeatStatus {
|
||||
enabled: boolean
|
||||
url?: string
|
||||
interval?: number
|
||||
method?: string
|
||||
msg?: string
|
||||
}
|
||||
|
||||
export default function HeartbeatSettings() {
|
||||
const [status, setStatus] = useState<HeartbeatStatus | null>(null)
|
||||
const [isLoading, setIsLoading] = useState(true)
|
||||
const [isTesting, setIsTesting] = useState(false)
|
||||
|
||||
if (!isAdmin()) {
|
||||
redirectPage($router, "settings", { name: "general" })
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
fetchStatus()
|
||||
}, [])
|
||||
|
||||
async function fetchStatus() {
|
||||
try {
|
||||
setIsLoading(true)
|
||||
const res = await pb.send<HeartbeatStatus>("/api/beszel/heartbeat-status", {})
|
||||
setStatus(res)
|
||||
} catch (error: unknown) {
|
||||
toast({
|
||||
title: t`Error`,
|
||||
description: (error as Error).message,
|
||||
variant: "destructive",
|
||||
})
|
||||
} finally {
|
||||
setIsLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
async function sendTestHeartbeat() {
|
||||
setIsTesting(true)
|
||||
try {
|
||||
const res = await pb.send<{ err: string | false }>("/api/beszel/test-heartbeat", {
|
||||
method: "POST",
|
||||
})
|
||||
if ("err" in res && !res.err) {
|
||||
toast({
|
||||
title: t`Heartbeat sent successfully`,
|
||||
description: t`Check your monitoring service`,
|
||||
})
|
||||
} else {
|
||||
toast({
|
||||
title: t`Error`,
|
||||
description: (res.err as string) ?? t`Failed to send heartbeat`,
|
||||
variant: "destructive",
|
||||
})
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
toast({
|
||||
title: t`Error`,
|
||||
description: (error as Error).message,
|
||||
variant: "destructive",
|
||||
})
|
||||
} finally {
|
||||
setIsTesting(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>
|
||||
<h3 className="text-xl font-medium mb-2">
|
||||
<Trans>Heartbeat Monitoring</Trans>
|
||||
</h3>
|
||||
<p className="text-sm text-muted-foreground leading-relaxed">
|
||||
<Trans>
|
||||
Send periodic outbound pings to an external monitoring service so you can monitor Beszel without exposing it
|
||||
to the internet.
|
||||
</Trans>
|
||||
</p>
|
||||
</div>
|
||||
<Separator className="my-4" />
|
||||
|
||||
{status?.enabled ? (
|
||||
<EnabledState status={status} isTesting={isTesting} sendTestHeartbeat={sendTestHeartbeat} />
|
||||
) : (
|
||||
<NotEnabledState isLoading={isLoading} />
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function EnabledState({
|
||||
status,
|
||||
isTesting,
|
||||
sendTestHeartbeat,
|
||||
}: {
|
||||
status: HeartbeatStatus
|
||||
isTesting: boolean
|
||||
sendTestHeartbeat: () => void
|
||||
}) {
|
||||
const TestIcon = isTesting ? LoaderCircleIcon : SendIcon
|
||||
return (
|
||||
<div className="space-y-5">
|
||||
<div className="flex items-center gap-2">
|
||||
<Badge variant="success">
|
||||
<Trans>Active</Trans>
|
||||
</Badge>
|
||||
</div>
|
||||
<div className="grid gap-4 sm:grid-cols-2">
|
||||
<ConfigItem label={t`Endpoint URL`} value={status.url ?? ""} mono />
|
||||
<ConfigItem label={t`Interval`} value={`${status.interval}s`} />
|
||||
<ConfigItem label={t`HTTP Method`} value={status.method ?? "POST"} />
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
<div>
|
||||
<h4 className="text-base font-medium mb-1">
|
||||
<Trans>Test heartbeat</Trans>
|
||||
</h4>
|
||||
<p className="text-sm text-muted-foreground leading-relaxed mb-3">
|
||||
<Trans>Send a single heartbeat ping to verify your endpoint is working.</Trans>
|
||||
</p>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
className="flex items-center gap-1.5"
|
||||
onClick={sendTestHeartbeat}
|
||||
disabled={isTesting}
|
||||
>
|
||||
<TestIcon className={cn("size-4", isTesting && "animate-spin")} />
|
||||
<Trans>Send test heartbeat</Trans>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
<div>
|
||||
<h4 className="text-base font-medium mb-2">
|
||||
<Trans>Payload format</Trans>
|
||||
</h4>
|
||||
<p className="text-sm text-muted-foreground leading-relaxed mb-2">
|
||||
<Trans>
|
||||
When using POST, each heartbeat includes a JSON payload with system status summary, list of down systems,
|
||||
and triggered alerts.
|
||||
</Trans>
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground leading-relaxed">
|
||||
<Trans>
|
||||
The overall status is <code className="bg-muted rounded-sm px-1 text-primary">ok</code> when all systems are
|
||||
up, <code className="bg-muted rounded-sm px-1 text-primary">warn</code> when alerts are triggered, and{" "}
|
||||
<code className="bg-muted rounded-sm px-1 text-primary">error</code> when any system is down.
|
||||
</Trans>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function NotEnabledState({ isLoading }: { isLoading?: boolean }) {
|
||||
return (
|
||||
<div className={cn("grid gap-4", isLoading && "animate-pulse")}>
|
||||
<div>
|
||||
<p className="text-sm text-muted-foreground leading-relaxed mb-3">
|
||||
<Trans>Set the following environment variables on your Beszel hub to enable heartbeat monitoring:</Trans>
|
||||
</p>
|
||||
<div className="grid gap-2.5">
|
||||
<EnvVarItem
|
||||
name="HEARTBEAT_URL"
|
||||
description={t`Endpoint URL to ping (required)`}
|
||||
example="https://uptime.betterstack.com/api/v1/heartbeat/xxxx"
|
||||
/>
|
||||
<EnvVarItem name="HEARTBEAT_INTERVAL" description={t`Seconds between pings (default: 60)`} example="60" />
|
||||
<EnvVarItem
|
||||
name="HEARTBEAT_METHOD"
|
||||
description={t`HTTP method: POST, GET, or HEAD (default: POST)`}
|
||||
example="POST"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground leading-relaxed">
|
||||
<Trans>After setting the environment variables, restart your Beszel hub for changes to take effect.</Trans>
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function ConfigItem({ label, value, mono }: { label: string; value: string; mono?: boolean }) {
|
||||
return (
|
||||
<div>
|
||||
<p className="text-sm font-medium mb-0.5">{label}</p>
|
||||
<p className={cn("text-sm text-muted-foreground break-all", mono && "font-mono")}>{value}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function EnvVarItem({ name, description, example }: { name: string; description: string; example: string }) {
|
||||
return (
|
||||
<div className="bg-muted/50 rounded-md px-3 py-2.5 grid gap-1.5">
|
||||
<code className="text-sm font-mono text-primary font-medium leading-tight">{name}</code>
|
||||
<p className="text-sm text-muted-foreground">{description}</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
<Trans>Example:</Trans> <code className="font-mono">{example}</code>
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -2,7 +2,14 @@ import { t } from "@lingui/core/macro"
|
||||
import { Trans, useLingui } from "@lingui/react/macro"
|
||||
import { useStore } from "@nanostores/react"
|
||||
import { getPagePath, redirectPage } from "@nanostores/router"
|
||||
import { AlertOctagonIcon, BellIcon, FileSlidersIcon, FingerprintIcon, SettingsIcon } from "lucide-react"
|
||||
import {
|
||||
AlertOctagonIcon,
|
||||
BellIcon,
|
||||
FileSlidersIcon,
|
||||
FingerprintIcon,
|
||||
HeartPulseIcon,
|
||||
SettingsIcon,
|
||||
} from "lucide-react"
|
||||
import { lazy, useEffect } from "react"
|
||||
import { $router } from "@/components/router.tsx"
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card.tsx"
|
||||
@@ -18,12 +25,14 @@ const notificationsSettingsImport = () => import("./notifications.tsx")
|
||||
const configYamlSettingsImport = () => import("./config-yaml.tsx")
|
||||
const fingerprintsSettingsImport = () => import("./tokens-fingerprints.tsx")
|
||||
const alertsHistoryDataTableSettingsImport = () => import("./alerts-history-data-table.tsx")
|
||||
const heartbeatSettingsImport = () => import("./heartbeat.tsx")
|
||||
|
||||
const GeneralSettings = lazy(generalSettingsImport)
|
||||
const NotificationsSettings = lazy(notificationsSettingsImport)
|
||||
const ConfigYamlSettings = lazy(configYamlSettingsImport)
|
||||
const FingerprintsSettings = lazy(fingerprintsSettingsImport)
|
||||
const AlertsHistoryDataTableSettings = lazy(alertsHistoryDataTableSettingsImport)
|
||||
const HeartbeatSettings = lazy(heartbeatSettingsImport)
|
||||
|
||||
export async function saveSettings(newSettings: Partial<UserSettings>) {
|
||||
try {
|
||||
@@ -81,6 +90,13 @@ export default function SettingsLayout() {
|
||||
icon: AlertOctagonIcon,
|
||||
preload: alertsHistoryDataTableSettingsImport,
|
||||
},
|
||||
{
|
||||
title: t`Heartbeat`,
|
||||
href: getPagePath($router, "settings", { name: "heartbeat" }),
|
||||
icon: HeartPulseIcon,
|
||||
admin: true,
|
||||
preload: heartbeatSettingsImport,
|
||||
},
|
||||
{
|
||||
title: t`YAML Config`,
|
||||
href: getPagePath($router, "settings", { name: "config" }),
|
||||
@@ -141,5 +157,7 @@ function SettingsContent({ name }: { name: string }) {
|
||||
return <FingerprintsSettings />
|
||||
case "alert-history":
|
||||
return <AlertsHistoryDataTableSettings />
|
||||
case "heartbeat":
|
||||
return <HeartbeatSettings />
|
||||
}
|
||||
}
|
||||
|
||||
@@ -593,7 +593,7 @@ export default memo(function SystemDetail({ id }: { id: string }) {
|
||||
if (showMax) {
|
||||
return data?.stats?.bm?.[0] ?? (data?.stats?.nsm ?? 0) * 1024 * 1024
|
||||
}
|
||||
return data?.stats?.b?.[0] ?? data?.stats?.ns * 1024 * 1024
|
||||
return data?.stats?.b?.[0] ?? (data?.stats?.ns ?? 0) * 1024 * 1024
|
||||
},
|
||||
color: 5,
|
||||
opacity: 0.2,
|
||||
@@ -604,7 +604,7 @@ export default memo(function SystemDetail({ id }: { id: string }) {
|
||||
if (showMax) {
|
||||
return data?.stats?.bm?.[1] ?? (data?.stats?.nrm ?? 0) * 1024 * 1024
|
||||
}
|
||||
return data?.stats?.b?.[1] ?? data?.stats?.nr * 1024 * 1024
|
||||
return data?.stats?.b?.[1] ?? (data?.stats?.nr ?? 0) * 1024 * 1024
|
||||
},
|
||||
color: 2,
|
||||
opacity: 0.2,
|
||||
|
||||
@@ -656,14 +656,14 @@ function DiskSheet({
|
||||
</Tooltip>
|
||||
</SheetDescription>
|
||||
</SheetHeader>
|
||||
<div className="flex-1 overflow-auto p-4 flex flex-col gap-4">
|
||||
<div className="flex-1 overflow-hidden p-4 flex flex-col gap-4">
|
||||
{isLoading ? (
|
||||
<div className="flex justify-center py-8">
|
||||
<LoaderCircleIcon className="animate-spin size-10 opacity-60" />
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<Alert className="pb-3">
|
||||
<Alert className="pb-3 shrink-0">
|
||||
{status === "PASSED" ? <CheckCircle2Icon className="size-4" /> : <XCircleIcon className="size-4" />}
|
||||
<AlertTitle>
|
||||
<Trans>S.M.A.R.T. Self-Test</Trans>: {status}
|
||||
@@ -675,9 +675,9 @@ function DiskSheet({
|
||||
)}
|
||||
</Alert>
|
||||
{smartAttributes.length > 0 ? (
|
||||
<div className="rounded-md border overflow-auto">
|
||||
<div className="rounded-md border min-h-0 flex flex-col">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableHeader className="sticky top-0 z-10">
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<TableRow key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => (
|
||||
|
||||
@@ -33,7 +33,6 @@ import {
|
||||
decimalString,
|
||||
formatBytes,
|
||||
formatTemperature,
|
||||
getMeterState,
|
||||
parseSemVer,
|
||||
secondsToUptimeString,
|
||||
} from "@/lib/utils"
|
||||
@@ -81,6 +80,10 @@ const STATUS_COLORS = {
|
||||
[SystemStatus.Pending]: "bg-yellow-500",
|
||||
} as const
|
||||
|
||||
function getMeterStateByThresholds(value: number, warn = 65, crit = 90): MeterState {
|
||||
return value >= crit ? MeterState.Crit : value >= warn ? MeterState.Warn : MeterState.Good
|
||||
}
|
||||
|
||||
/**
|
||||
* @param viewMode - "table" or "grid"
|
||||
* @returns - Column definitions for the systems table
|
||||
@@ -209,6 +212,7 @@ export function SystemsTableColumns(viewMode: "table" | "grid"): ColumnDef<Syste
|
||||
header: sortableHeader,
|
||||
cell(info: CellContext<SystemRecord, unknown>) {
|
||||
const { info: sysInfo, status } = info.row.original
|
||||
const { colorWarn = 65, colorCrit = 90 } = useStore($userSettings, { keys: ["colorWarn", "colorCrit"] })
|
||||
// agent version
|
||||
const { minor, patch } = parseSemVer(sysInfo.v)
|
||||
let loadAverages = sysInfo.la
|
||||
@@ -224,7 +228,7 @@ export function SystemsTableColumns(viewMode: "table" | "grid"): ColumnDef<Syste
|
||||
}
|
||||
|
||||
const normalizedLoad = max / (sysInfo.t ?? 1)
|
||||
const threshold = getMeterState(normalizedLoad * 100)
|
||||
const threshold = getMeterStateByThresholds(normalizedLoad * 100, colorWarn, colorCrit)
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-[.35em] w-full tabular-nums tracking-tight">
|
||||
@@ -463,8 +467,9 @@ function sortableHeader(context: HeaderContext<SystemRecord, unknown>) {
|
||||
}
|
||||
|
||||
function TableCellWithMeter(info: CellContext<SystemRecord, unknown>) {
|
||||
const { colorWarn = 65, colorCrit = 90 } = useStore($userSettings, { keys: ["colorWarn", "colorCrit"] })
|
||||
const val = Number(info.getValue()) || 0
|
||||
const threshold = getMeterState(val)
|
||||
const threshold = getMeterStateByThresholds(val, colorWarn, colorCrit)
|
||||
const meterClass = cn(
|
||||
"h-full",
|
||||
(info.row.original.status !== SystemStatus.Up && STATUS_COLORS.paused) ||
|
||||
@@ -483,6 +488,7 @@ function TableCellWithMeter(info: CellContext<SystemRecord, unknown>) {
|
||||
}
|
||||
|
||||
function DiskCellWithMultiple(info: CellContext<SystemRecord, unknown>) {
|
||||
const { colorWarn = 65, colorCrit = 90 } = useStore($userSettings, { keys: ["colorWarn", "colorCrit"] })
|
||||
const { info: sysInfo, status, id } = info.row.original
|
||||
const extraFs = Object.entries(sysInfo.efs ?? {})
|
||||
|
||||
@@ -496,7 +502,7 @@ function DiskCellWithMultiple(info: CellContext<SystemRecord, unknown>) {
|
||||
extraFs.sort((a, b) => b[1] - a[1])
|
||||
|
||||
function getIndicatorColor(pct: number) {
|
||||
const threshold = getMeterState(pct)
|
||||
const threshold = getMeterStateByThresholds(pct, colorWarn, colorCrit)
|
||||
return (
|
||||
(status !== SystemStatus.Up && STATUS_COLORS.paused) ||
|
||||
(threshold === MeterState.Good && STATUS_COLORS.up) ||
|
||||
@@ -514,7 +520,9 @@ function DiskCellWithMultiple(info: CellContext<SystemRecord, unknown>) {
|
||||
const extraDiskIndicators =
|
||||
status !== SystemStatus.Up
|
||||
? []
|
||||
: [...new Set(extraFs.map(([, pct]) => getMeterState(pct)))].sort().map((state) => stateColors[state])
|
||||
: [...new Set(extraFs.map(([, pct]) => getMeterStateByThresholds(pct, colorWarn, colorCrit)))]
|
||||
.sort()
|
||||
.map((state) => stateColors[state])
|
||||
|
||||
return (
|
||||
<Tooltip>
|
||||
|
||||
@@ -40,7 +40,7 @@ export const alertInfo: Record<string, AlertInfo> = {
|
||||
unit: " MB/s",
|
||||
icon: EthernetIcon,
|
||||
desc: () => t`Triggers when combined up/down exceeds a threshold`,
|
||||
max: 125,
|
||||
max: 250,
|
||||
},
|
||||
GPU: {
|
||||
name: () => t`GPU Usage`,
|
||||
|
||||
@@ -8,7 +8,7 @@ export default [
|
||||
["es", "Español", "🇪🇸"],
|
||||
["fa", "فارسی", "🇮🇷"],
|
||||
["fr", "Français", "🇫🇷"],
|
||||
["he", "עברית", "🕎"],
|
||||
["he", "עברית", ""],
|
||||
["hr", "Hrvatski", "🇭🇷"],
|
||||
["hu", "Magyar", "🇭🇺"],
|
||||
["id", "Indonesia", "🇮🇩"],
|
||||
|
||||
@@ -6,7 +6,7 @@ import { useEffect, useState } from "react"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
import { toast } from "@/components/ui/use-toast"
|
||||
import type { ChartTimeData, FingerprintRecord, SemVer, SystemRecord } from "@/types"
|
||||
import { HourFormat, MeterState, Unit } from "./enums"
|
||||
import { HourFormat, Unit } from "./enums"
|
||||
import { $copyContent, $userSettings } from "./stores"
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
@@ -210,7 +210,6 @@ export function useBrowserStorage<T>(key: string, defaultValue: T, storageInterf
|
||||
const [value, setValue] = useState(() => {
|
||||
return getStorageValue(key, defaultValue, storageInterface)
|
||||
})
|
||||
// biome-ignore lint/correctness/useExhaustiveDependencies: storageInterface won't change
|
||||
useEffect(() => {
|
||||
storageInterface?.setItem(key, JSON.stringify(value))
|
||||
}, [key, value])
|
||||
@@ -394,12 +393,6 @@ export function compareSemVer(a: SemVer, b: SemVer) {
|
||||
return a.patch - b.patch
|
||||
}
|
||||
|
||||
/** Get meter state from 0-100 value. Used for color coding meters. */
|
||||
export function getMeterState(value: number): MeterState {
|
||||
const { colorWarn = 65, colorCrit = 90 } = $userSettings.get()
|
||||
return value >= colorCrit ? MeterState.Crit : value >= colorWarn ? MeterState.Warn : MeterState.Good
|
||||
}
|
||||
|
||||
// biome-ignore lint/suspicious/noExplicitAny: any is used to allow any function to be passed in
|
||||
export function debounce<T extends (...args: any[]) => any>(func: T, wait: number): (...args: Parameters<T>) => void {
|
||||
let timeout: ReturnType<typeof setTimeout>
|
||||
|
||||
@@ -93,6 +93,7 @@ msgstr "إجراءات"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Active"
|
||||
msgstr "نشط"
|
||||
@@ -140,6 +141,10 @@ msgstr "مسؤول"
|
||||
msgid "After"
|
||||
msgstr "بعد"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "After setting the environment variables, restart your Beszel hub for changes to take effect."
|
||||
msgstr "بعد تعيين متغيرات البيئة، أعد تشغيل مركز Beszel الخاص بك لتصبح التغييرات سارية المفعول."
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "وكيل"
|
||||
@@ -350,6 +355,10 @@ msgstr "تحقق من {email} للحصول على رابط إعادة التعي
|
||||
msgid "Check logs for more details."
|
||||
msgstr "تحقق من السجلات لمزيد من التفاصيل."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Check your monitoring service"
|
||||
msgstr "تحقق من خدمة المراقبة الخاصة بك"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Check your notification service"
|
||||
msgstr "تحقق من خدمة الإشعارات الخاصة بك"
|
||||
@@ -643,6 +652,14 @@ msgstr "فارغة"
|
||||
msgid "End Time"
|
||||
msgstr "وقت النهاية"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL"
|
||||
msgstr "عنوان URL للنقطة النهائية"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL to ping (required)"
|
||||
msgstr "عنوان URL للنقطة النهائية لل ping (مطلوب)"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Enter email address to reset password"
|
||||
msgstr "أدخل عنوان البريد الإشباكي لإعادة تعيين كلمة المرور"
|
||||
@@ -662,6 +679,9 @@ msgstr "مؤقت"
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
@@ -670,6 +690,10 @@ msgstr "مؤقت"
|
||||
msgid "Error"
|
||||
msgstr "خطأ"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Example:"
|
||||
msgstr "مثال:"
|
||||
|
||||
#. placeholder {0}: alert.value
|
||||
#. placeholder {1}: info.unit
|
||||
#. placeholder {2}: alert.min
|
||||
@@ -727,6 +751,10 @@ msgstr "فشل في المصادقة"
|
||||
msgid "Failed to save settings"
|
||||
msgstr "فشل في حفظ الإعدادات"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Failed to send heartbeat"
|
||||
msgstr "فشل في إرسال نبضة القلب"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Failed to send test notification"
|
||||
msgstr "فشل في إرسال إشعار الاختبار"
|
||||
@@ -806,6 +834,18 @@ msgstr "شبكة"
|
||||
msgid "Health"
|
||||
msgstr "الصحة"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Heartbeat"
|
||||
msgstr "نبضة القلب"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat Monitoring"
|
||||
msgstr "مراقبة نبضة القلب"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat sent successfully"
|
||||
msgstr "تم إرسال نبضة القلب بنجاح"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
@@ -816,6 +856,14 @@ msgstr "أمر Homebrew"
|
||||
msgid "Host / IP"
|
||||
msgstr "مضيف / IP"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP Method"
|
||||
msgstr "طريقة HTTP"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP method: POST, GET, or HEAD (default: POST)"
|
||||
msgstr "طريقة HTTP: POST، GET، أو HEAD (الافتراضي: POST)"
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Idle"
|
||||
@@ -834,6 +882,10 @@ msgstr "صورة"
|
||||
msgid "Inactive"
|
||||
msgstr "غير نشط"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Interval"
|
||||
msgstr "الفاصل الزمني"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Invalid email address."
|
||||
msgstr "عنوان البريد الإشباكي غير صالح."
|
||||
@@ -1110,6 +1162,10 @@ msgstr "متوقف مؤقتا"
|
||||
msgid "Paused ({pausedSystemsLength})"
|
||||
msgstr "متوقف مؤقتا ({pausedSystemsLength})"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Payload format"
|
||||
msgstr "تنسيق الحمولة"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Per-core average utilization"
|
||||
@@ -1307,6 +1363,10 @@ msgstr "بحث"
|
||||
msgid "Search for systems or settings..."
|
||||
msgstr "البحث عن الأنظمة أو الإعدادات..."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Seconds between pings (default: 60)"
|
||||
msgstr "ثواني بين ال pings (الافتراضي: 60)"
|
||||
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "راجع <0>إعدادات الإشعارات</0> لتكوين كيفية تلقي التنبيهات."
|
||||
@@ -1315,6 +1375,18 @@ msgstr "راجع <0>إعدادات الإشعارات</0> لتكوين كيفي
|
||||
msgid "Select {foo}"
|
||||
msgstr "تحديد {foo}"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send a single heartbeat ping to verify your endpoint is working."
|
||||
msgstr "أرسل ping نبضة قلب واحدة للتحقق من أن نقطة النهاية الخاصة بك تعمل."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send periodic outbound pings to an external monitoring service so you can monitor Beszel without exposing it to the internet."
|
||||
msgstr "أرسل pings صادرة دورية إلى خدمة مراقبة خارجية حتى تتمكن من مراقبة Beszel دون تعريضه للإنترنت."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send test heartbeat"
|
||||
msgstr "إرسال نبضة قلب اختبارية"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "تم الإرسال"
|
||||
@@ -1335,6 +1407,10 @@ msgstr "الخدمات"
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "تعيين عتبات النسبة المئوية لألوان العداد."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Set the following environment variables on your Beszel hub to enable heartbeat monitoring:"
|
||||
msgstr "قم بتعيين متغيرات البيئة التالية على مركز Beszel الخاص بك لتمكين مراقبة نبضة القلب:"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
@@ -1452,10 +1528,18 @@ msgstr "درجات حرارة مستشعرات النظام"
|
||||
msgid "Test <0>URL</0>"
|
||||
msgstr "اختبار <0>URL</0>"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Test heartbeat"
|
||||
msgstr "اختبار نبضة القلب"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Test notification sent"
|
||||
msgstr "تم إرسال إشعار الاختبار"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "The overall status is <0>ok</0> when all systems are up, <1>warn</1> when alerts are triggered, and <2>error</2> when any system is down."
|
||||
msgstr "الحالة العامة هي <0>موافق</0> عندما تكون جميع الأنظمة تعمل، و<1>تحذير</1> عند تشغيل التنبيهات، و<2>خطأ</2> عندما يكون أي نظام معطلاً."
|
||||
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
msgid "Then log into the backend and reset your user account password in the users table."
|
||||
msgstr "ثم قم بتسجيل الدخول إلى الواجهة الخلفية وأعد تعيين كلمة مرور حساب المستخدم الخاص بك في جدول المستخدمين."
|
||||
@@ -1642,6 +1726,7 @@ msgid "Upload"
|
||||
msgstr "رفع"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Uptime"
|
||||
msgstr "مدة التشغيل"
|
||||
|
||||
@@ -1716,6 +1801,10 @@ msgstr "إشعارات Webhook / Push"
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation."
|
||||
msgstr "عند التفعيل، يسمح هذا الرمز المميز للوكلاء بالتسجيل الذاتي دون إنشاء نظام مسبق."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "When using POST, each heartbeat includes a JSON payload with system status summary, list of down systems, and triggered alerts."
|
||||
msgstr "عند استخدام POST، تتضمن كل نبضة قلب حمولة JSON مع ملخص حالة النظام وقائمة الأنظمة المعطلة والتنبيهات التي تم تشغيلها."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
|
||||
@@ -93,6 +93,7 @@ msgstr "Действия"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Active"
|
||||
msgstr "Активен"
|
||||
@@ -140,6 +141,10 @@ msgstr "Администратор"
|
||||
msgid "After"
|
||||
msgstr "След"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "After setting the environment variables, restart your Beszel hub for changes to take effect."
|
||||
msgstr "След като настроите променливите на средата, рестартирайте вашия Beszel hub, за да влязат промените в сила."
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Агент"
|
||||
@@ -231,7 +236,7 @@ msgstr "Bandwidth на мрежата"
|
||||
#. Battery label in systems table header
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Bat"
|
||||
msgstr ""
|
||||
msgstr "Бат"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/lib/alerts.ts
|
||||
@@ -350,6 +355,10 @@ msgstr "Провери {email} за линк за нулиране."
|
||||
msgid "Check logs for more details."
|
||||
msgstr "Провери log-овете за повече информация."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Check your monitoring service"
|
||||
msgstr "Проверете вашата услуга за мониторинг"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Check your notification service"
|
||||
msgstr "Провери услугата си за удостоверяване"
|
||||
@@ -621,7 +630,7 @@ msgstr "Редактирай"
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Edit {foo}"
|
||||
msgstr ""
|
||||
msgstr "Редактиране на {foo}"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
@@ -643,6 +652,14 @@ msgstr "Празна"
|
||||
msgid "End Time"
|
||||
msgstr "Крайно време"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL"
|
||||
msgstr "URL адрес на крайната точка"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL to ping (required)"
|
||||
msgstr "URL адрес на крайната точка за пинг (задължително)"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Enter email address to reset password"
|
||||
msgstr "Въведи имейл адрес за да нулираш паролата"
|
||||
@@ -662,6 +679,9 @@ msgstr "Ефимерен"
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
@@ -670,6 +690,10 @@ msgstr "Ефимерен"
|
||||
msgid "Error"
|
||||
msgstr "Грешка"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Example:"
|
||||
msgstr "Пример:"
|
||||
|
||||
#. placeholder {0}: alert.value
|
||||
#. placeholder {1}: info.unit
|
||||
#. placeholder {2}: alert.min
|
||||
@@ -679,7 +703,7 @@ msgstr "Надвишава {0}{1} в последните {2, plural, one {# м
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Exec main PID"
|
||||
msgstr ""
|
||||
msgstr "PID на главния изпълнителен процес"
|
||||
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
|
||||
@@ -727,6 +751,10 @@ msgstr "Неуспешно удостоверяване"
|
||||
msgid "Failed to save settings"
|
||||
msgstr "Неуспешно запазване на настройки"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Failed to send heartbeat"
|
||||
msgstr "Неуспешно изпращане на heartbeat"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Failed to send test notification"
|
||||
msgstr "Неуспешно изпрати тестова нотификация"
|
||||
@@ -806,6 +834,18 @@ msgstr "Мрежово"
|
||||
msgid "Health"
|
||||
msgstr "Здраве"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Heartbeat"
|
||||
msgstr "Heartbeat"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat Monitoring"
|
||||
msgstr "Мониторинг на heartbeat"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat sent successfully"
|
||||
msgstr "Heartbeat е изпратен успешно"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
@@ -816,6 +856,14 @@ msgstr "Команда Homebrew"
|
||||
msgid "Host / IP"
|
||||
msgstr "Хост / IP"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP Method"
|
||||
msgstr "HTTP метод"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP method: POST, GET, or HEAD (default: POST)"
|
||||
msgstr "HTTP метод: POST, GET или HEAD (по подразбиране: POST)"
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Idle"
|
||||
@@ -834,6 +882,10 @@ msgstr "Образ"
|
||||
msgid "Inactive"
|
||||
msgstr "Неактивен"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Interval"
|
||||
msgstr "Интервал"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Invalid email address."
|
||||
msgstr "Невалиден имейл адрес."
|
||||
@@ -914,7 +966,7 @@ msgstr "Търсиш къде да създадеш тревоги? Натисн
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Main PID"
|
||||
msgstr ""
|
||||
msgstr "Главен PID"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Manage display and notification preferences."
|
||||
@@ -1110,6 +1162,10 @@ msgstr "На пауза"
|
||||
msgid "Paused ({pausedSystemsLength})"
|
||||
msgstr "На пауза ({pausedSystemsLength})"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Payload format"
|
||||
msgstr "Формат на полезния товар"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Per-core average utilization"
|
||||
@@ -1307,6 +1363,10 @@ msgstr "Търси"
|
||||
msgid "Search for systems or settings..."
|
||||
msgstr "Търси за системи или настройки..."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Seconds between pings (default: 60)"
|
||||
msgstr "Секунди между пинговете (по подразбиране: 60)"
|
||||
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "Виж <0>настройките за нотификациите</0> за да конфигурираш как получаваш тревоги."
|
||||
@@ -1315,6 +1375,18 @@ msgstr "Виж <0>настройките за нотификациите</0> з
|
||||
msgid "Select {foo}"
|
||||
msgstr "Избери {foo}"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send a single heartbeat ping to verify your endpoint is working."
|
||||
msgstr "Изпратете единичен heartbeat пинг, за да проверите дали вашата крайна точка работи."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send periodic outbound pings to an external monitoring service so you can monitor Beszel without exposing it to the internet."
|
||||
msgstr "Изпращайте периодични изходящи пингове към външна услуга за мониторинг, за да можете да наблюдавате Beszel, без да го излагате на интернет."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send test heartbeat"
|
||||
msgstr "Изпращане на тестов heartbeat"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "Изпратени"
|
||||
@@ -1335,6 +1407,10 @@ msgstr "Услуги"
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "Задайте процентни прагове за цветовете на измервателните уреди."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Set the following environment variables on your Beszel hub to enable heartbeat monitoring:"
|
||||
msgstr "Задайте следните променливи на средата на вашия Beszel hub, за да активирате мониторинга на heartbeat:"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
@@ -1452,10 +1528,18 @@ msgstr "Температири на системни сензори"
|
||||
msgid "Test <0>URL</0>"
|
||||
msgstr "Тествай <0>URL</0>"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Test heartbeat"
|
||||
msgstr "Тестов heartbeat"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Test notification sent"
|
||||
msgstr "Тестова нотификация изпратена"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "The overall status is <0>ok</0> when all systems are up, <1>warn</1> when alerts are triggered, and <2>error</2> when any system is down."
|
||||
msgstr "Общият статус е <0>ok</0>, когато всички системи работят, <1>warn</1>, когато са задействани предупреждения, и <2>error</2>, когато някоя система е спряла."
|
||||
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
msgid "Then log into the backend and reset your user account password in the users table."
|
||||
msgstr "След това влез в backend-а и нулирай паролата за потребителския акаунт в таблицата за потребители."
|
||||
@@ -1642,8 +1726,9 @@ msgid "Upload"
|
||||
msgstr "Качване"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Uptime"
|
||||
msgstr "Време на работа"
|
||||
msgstr "Uptime"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
@@ -1716,6 +1801,10 @@ msgstr "Webhook / Пуш нотификации"
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation."
|
||||
msgstr "Когато е активиран, този символ позволява на агентите да се регистрират сами без предварително създаване на система."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "When using POST, each heartbeat includes a JSON payload with system status summary, list of down systems, and triggered alerts."
|
||||
msgstr "При използване на POST всеки heartbeat включва JSON полезен товар с резюме на състоянието на системата, списък на спрените системи и задействаните предупреждения."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
|
||||
@@ -93,6 +93,7 @@ msgstr "Akce"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Active"
|
||||
msgstr "Aktivní"
|
||||
@@ -140,6 +141,10 @@ msgstr "Administrátor"
|
||||
msgid "After"
|
||||
msgstr "Po"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "After setting the environment variables, restart your Beszel hub for changes to take effect."
|
||||
msgstr "Po nastavení proměnných prostředí restartujte hub Beszel, aby se změny projevily."
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Agent"
|
||||
@@ -231,7 +236,7 @@ msgstr "Přenos"
|
||||
#. Battery label in systems table header
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Bat"
|
||||
msgstr ""
|
||||
msgstr "Bat"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/lib/alerts.ts
|
||||
@@ -350,6 +355,10 @@ msgstr "Zkontrolujte {email} pro odkaz na obnovení."
|
||||
msgid "Check logs for more details."
|
||||
msgstr "Pro více informací zkontrolujte logy."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Check your monitoring service"
|
||||
msgstr "Zkontrolujte svou monitorovací službu"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Check your notification service"
|
||||
msgstr "Zkontrolujte službu upozornění"
|
||||
@@ -556,11 +565,11 @@ msgstr "Vybíjení"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Disk"
|
||||
msgstr ""
|
||||
msgstr "Disk"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Disk I/O"
|
||||
msgstr ""
|
||||
msgstr "Disk I/O"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Disk unit"
|
||||
@@ -643,6 +652,14 @@ msgstr "Prázdná"
|
||||
msgid "End Time"
|
||||
msgstr "Čas ukončení"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL"
|
||||
msgstr "URL koncového bodu"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL to ping (required)"
|
||||
msgstr "URL koncového bodu pro ping (vyžadováno)"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Enter email address to reset password"
|
||||
msgstr "Zadejte e-mailovou adresu pro obnovu hesla"
|
||||
@@ -662,6 +679,9 @@ msgstr "Efemérní"
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
@@ -670,6 +690,10 @@ msgstr "Efemérní"
|
||||
msgid "Error"
|
||||
msgstr "Chyba"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Example:"
|
||||
msgstr "Příklad:"
|
||||
|
||||
#. placeholder {0}: alert.value
|
||||
#. placeholder {1}: info.unit
|
||||
#. placeholder {2}: alert.min
|
||||
@@ -727,6 +751,10 @@ msgstr "Ověření se nezdařilo"
|
||||
msgid "Failed to save settings"
|
||||
msgstr "Nepodařilo se uložit nastavení"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Failed to send heartbeat"
|
||||
msgstr "Nepodařilo se odeslat heartbeat"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Failed to send test notification"
|
||||
msgstr "Nepodařilo se odeslat testovací oznámení"
|
||||
@@ -755,7 +783,7 @@ msgstr "Otisk"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Firmware"
|
||||
msgstr ""
|
||||
msgstr "Firmware"
|
||||
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "For <0>{min}</0> {min, plural, one {minute} other {minutes}}"
|
||||
@@ -806,6 +834,18 @@ msgstr "Mřížka"
|
||||
msgid "Health"
|
||||
msgstr "Zdraví"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Heartbeat"
|
||||
msgstr "Heartbeat"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat Monitoring"
|
||||
msgstr "Monitorování heartbeat"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat sent successfully"
|
||||
msgstr "Heartbeat úspěšně odeslán"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
@@ -816,6 +856,14 @@ msgstr "Homebrew příkaz"
|
||||
msgid "Host / IP"
|
||||
msgstr "Hostitel / IP"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP Method"
|
||||
msgstr "HTTP metoda"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP method: POST, GET, or HEAD (default: POST)"
|
||||
msgstr "HTTP metoda: POST, GET nebo HEAD (výchozí: POST)"
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Idle"
|
||||
@@ -834,6 +882,10 @@ msgstr "Obraz"
|
||||
msgid "Inactive"
|
||||
msgstr "Neaktivní"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Interval"
|
||||
msgstr "Interval"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Invalid email address."
|
||||
msgstr "Neplatná e-mailová adresa."
|
||||
@@ -858,7 +910,7 @@ msgstr "Životní cyklus"
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "limit"
|
||||
msgstr ""
|
||||
msgstr "limit"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Load Average"
|
||||
@@ -958,7 +1010,7 @@ msgstr "Využití paměti docker kontejnerů"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Model"
|
||||
msgstr ""
|
||||
msgstr "Model"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
@@ -1110,6 +1162,10 @@ msgstr "Pozastaveno"
|
||||
msgid "Paused ({pausedSystemsLength})"
|
||||
msgstr "Pozastaveno ({pausedSystemsLength})"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Payload format"
|
||||
msgstr "Formát payloadu"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Per-core average utilization"
|
||||
@@ -1162,7 +1218,7 @@ msgstr "Přihlaste se prosím k vašemu účtu"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Port"
|
||||
msgstr ""
|
||||
msgstr "Port"
|
||||
|
||||
#. Power On Time
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
@@ -1307,6 +1363,10 @@ msgstr "Hledat"
|
||||
msgid "Search for systems or settings..."
|
||||
msgstr "Hledat systémy nebo nastavení..."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Seconds between pings (default: 60)"
|
||||
msgstr "Sekundy mezi pingy (výchozí: 60)"
|
||||
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "Podívejte se na <0>nastavení upozornění</0> pro nastavení toho, jak přijímáte upozornění."
|
||||
@@ -1315,6 +1375,18 @@ msgstr "Podívejte se na <0>nastavení upozornění</0> pro nastavení toho, jak
|
||||
msgid "Select {foo}"
|
||||
msgstr "Vybrat {foo}"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send a single heartbeat ping to verify your endpoint is working."
|
||||
msgstr "Odešlete jeden heartbeat ping pro ověření funkčnosti vašeho koncového bodu."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send periodic outbound pings to an external monitoring service so you can monitor Beszel without exposing it to the internet."
|
||||
msgstr "Odesílejte periodické odchozí pingy na externí monitorovací službu, abyste mohli monitorovat Beszel bez jeho vystavení internetu."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send test heartbeat"
|
||||
msgstr "Odeslat testovací heartbeat"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "Odeslat"
|
||||
@@ -1335,6 +1407,10 @@ msgstr "Služby"
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "Nastavte procentuální prahové hodnoty pro barvy měřičů."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Set the following environment variables on your Beszel hub to enable heartbeat monitoring:"
|
||||
msgstr "Pro povolení monitorování heartbeat nastavte na hubu Beszel následující proměnné prostředí:"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
@@ -1452,10 +1528,18 @@ msgstr "Teploty systémových senzorů"
|
||||
msgid "Test <0>URL</0>"
|
||||
msgstr "Testovat <0>URL</0>"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Test heartbeat"
|
||||
msgstr "Testovat heartbeat"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Test notification sent"
|
||||
msgstr "Testovací oznámení odesláno"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "The overall status is <0>ok</0> when all systems are up, <1>warn</1> when alerts are triggered, and <2>error</2> when any system is down."
|
||||
msgstr "Celkový stav je <0>ok</0>, když jsou všechny systémy v provozu, <1>warn</1>, když jsou spuštěny výstrahy, a <2>error</2>, když je některý systém mimo provoz."
|
||||
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
msgid "Then log into the backend and reset your user account password in the users table."
|
||||
msgstr "Poté se přihlaste do backendu a obnovte heslo k uživatelskému účtu v tabulce uživatelů."
|
||||
@@ -1497,7 +1581,7 @@ msgstr "Přepnout motiv"
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Token"
|
||||
msgstr ""
|
||||
msgstr "Token"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
@@ -1642,8 +1726,9 @@ msgid "Upload"
|
||||
msgstr "Odeslání"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Uptime"
|
||||
msgstr "Doba provozu"
|
||||
msgstr "Uptime"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
@@ -1716,6 +1801,10 @@ msgstr "Webhook / Push oznámení"
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation."
|
||||
msgstr "Pokud je povoleno, umožňuje tento token agentům samo-registraci bez předchozího vytvoření systému."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "When using POST, each heartbeat includes a JSON payload with system status summary, list of down systems, and triggered alerts."
|
||||
msgstr "Při použití metody POST obsahuje každý heartbeat JSON payload se souhrnem stavu systému, seznamem nefunkčních systémů a spuštěnými výstrahami."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
|
||||
@@ -93,6 +93,7 @@ msgstr "Handlinger"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Active"
|
||||
msgstr "Aktiv"
|
||||
@@ -140,6 +141,10 @@ msgstr "Administrator"
|
||||
msgid "After"
|
||||
msgstr "Efter"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "After setting the environment variables, restart your Beszel hub for changes to take effect."
|
||||
msgstr "Efter indstilling af miljøvariablerne skal du genstarte din Beszel-hub for at ændringerne kan træde i kraft."
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Agent"
|
||||
@@ -231,7 +236,7 @@ msgstr "Båndbredde"
|
||||
#. Battery label in systems table header
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Bat"
|
||||
msgstr ""
|
||||
msgstr "Bat"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/lib/alerts.ts
|
||||
@@ -350,6 +355,10 @@ msgstr "Tjek {email} for et nulstillingslink."
|
||||
msgid "Check logs for more details."
|
||||
msgstr "Tjek logfiler for flere detaljer."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Check your monitoring service"
|
||||
msgstr "Tjek din overvågningstjeneste"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Check your notification service"
|
||||
msgstr "Tjek din notifikationstjeneste"
|
||||
@@ -643,6 +652,14 @@ msgstr "Tom"
|
||||
msgid "End Time"
|
||||
msgstr "Sluttid"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL"
|
||||
msgstr "Endpoint-URL"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL to ping (required)"
|
||||
msgstr "Endpoint-URL til ping (påkrævet)"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Enter email address to reset password"
|
||||
msgstr "Indtast emailadresse for at nulstille adgangskoden"
|
||||
@@ -662,6 +679,9 @@ msgstr "Efemer"
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
@@ -670,6 +690,10 @@ msgstr "Efemer"
|
||||
msgid "Error"
|
||||
msgstr "Fejl"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Example:"
|
||||
msgstr "Eksempel:"
|
||||
|
||||
#. placeholder {0}: alert.value
|
||||
#. placeholder {1}: info.unit
|
||||
#. placeholder {2}: alert.min
|
||||
@@ -727,6 +751,10 @@ msgstr "Kunne ikke godkende"
|
||||
msgid "Failed to save settings"
|
||||
msgstr "Kunne ikke gemme indstillinger"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Failed to send heartbeat"
|
||||
msgstr "Kunne ikke sende heartbeat"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Failed to send test notification"
|
||||
msgstr "Afsendelse af testnotifikation mislykkedes"
|
||||
@@ -806,6 +834,18 @@ msgstr "Gitter"
|
||||
msgid "Health"
|
||||
msgstr "Sundhed"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Heartbeat"
|
||||
msgstr "Heartbeat"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat Monitoring"
|
||||
msgstr "Heartbeat-overvågning"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat sent successfully"
|
||||
msgstr "Heartbeat sendt succesfuldt"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
@@ -816,6 +856,14 @@ msgstr "Homebrew-kommando"
|
||||
msgid "Host / IP"
|
||||
msgstr "Vært / IP"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP Method"
|
||||
msgstr "HTTP-metode"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP method: POST, GET, or HEAD (default: POST)"
|
||||
msgstr "HTTP-metode: POST, GET eller HEAD (standard: POST)"
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Idle"
|
||||
@@ -834,6 +882,10 @@ msgstr "Billede"
|
||||
msgid "Inactive"
|
||||
msgstr "Inaktiv"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Interval"
|
||||
msgstr "Interval"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Invalid email address."
|
||||
msgstr "Ugyldig email adresse."
|
||||
@@ -1110,6 +1162,10 @@ msgstr "Sat på pause"
|
||||
msgid "Paused ({pausedSystemsLength})"
|
||||
msgstr "Sat på pause ({pausedSystemsLength})"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Payload format"
|
||||
msgstr "Payload-format"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Per-core average utilization"
|
||||
@@ -1121,7 +1177,7 @@ msgstr "Procentdel af tid brugt i hver tilstand"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Permanent"
|
||||
msgstr ""
|
||||
msgstr "Permanent"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Persistence"
|
||||
@@ -1307,6 +1363,10 @@ msgstr "Søg"
|
||||
msgid "Search for systems or settings..."
|
||||
msgstr "Søg efter systemer eller indstillinger..."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Seconds between pings (default: 60)"
|
||||
msgstr "Sekunder mellem pings (standard: 60)"
|
||||
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "Se <0>meddelelsesindstillinger</0> for at konfigurere, hvordan du modtager alarmer."
|
||||
@@ -1315,6 +1375,18 @@ msgstr "Se <0>meddelelsesindstillinger</0> for at konfigurere, hvordan du modtag
|
||||
msgid "Select {foo}"
|
||||
msgstr "Vælg {foo}"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send a single heartbeat ping to verify your endpoint is working."
|
||||
msgstr "Send et enkelt heartbeat-ping for at bekræfte, at dit endpoint fungerer."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send periodic outbound pings to an external monitoring service so you can monitor Beszel without exposing it to the internet."
|
||||
msgstr "Send periodiske udgående pings til en ekstern overvågningstjeneste, så du kan overvåge Beszel uden at eksponere det for internettet."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send test heartbeat"
|
||||
msgstr "Send test-heartbeat"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "Sendt"
|
||||
@@ -1335,6 +1407,10 @@ msgstr "Tjenester"
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "Indstil procentvise tærskler for målerfarver."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Set the following environment variables on your Beszel hub to enable heartbeat monitoring:"
|
||||
msgstr "Indstil følgende miljøvariabler på din Beszel-hub for at aktivere heartbeat-overvågning:"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
@@ -1452,10 +1528,18 @@ msgstr "Temperaturer i systemsensorer"
|
||||
msgid "Test <0>URL</0>"
|
||||
msgstr "Test <0>URL</0>"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Test heartbeat"
|
||||
msgstr "Test-heartbeat"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Test notification sent"
|
||||
msgstr "Test notifikation sendt"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "The overall status is <0>ok</0> when all systems are up, <1>warn</1> when alerts are triggered, and <2>error</2> when any system is down."
|
||||
msgstr "Den overordnede status er <0>ok</0>, når alle systemer kører, <1>warn</1>, når alarmer udløses, og <2>error</2>, når et system er nede."
|
||||
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
msgid "Then log into the backend and reset your user account password in the users table."
|
||||
msgstr "Log derefter ind på backend og nulstil adgangskoden til din brugerkonto i tabellen brugere."
|
||||
@@ -1642,6 +1726,7 @@ msgid "Upload"
|
||||
msgstr "Overfør"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Uptime"
|
||||
msgstr "Oppetid"
|
||||
|
||||
@@ -1716,6 +1801,10 @@ msgstr "Webhook / Push notifikationer"
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation."
|
||||
msgstr "Når aktiveret, tillader denne token agenter at registrere sig selv uden forudgående systemoprettelse."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "When using POST, each heartbeat includes a JSON payload with system status summary, list of down systems, and triggered alerts."
|
||||
msgstr "Når du bruger POST, inkluderer hvert heartbeat en JSON-payload med resumé af systemstatus, liste over systemer, der er nede, og udløste alarmer."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
|
||||
@@ -93,6 +93,7 @@ msgstr "Aktionen"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Active"
|
||||
msgstr "Aktiv"
|
||||
@@ -140,6 +141,10 @@ msgstr "Admin"
|
||||
msgid "After"
|
||||
msgstr "Nach"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "After setting the environment variables, restart your Beszel hub for changes to take effect."
|
||||
msgstr "Starten Sie nach dem Festlegen der Umgebungsvariablen Ihren Beszel-Hub neu, damit die Änderungen wirksam werden."
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Agent"
|
||||
@@ -231,7 +236,7 @@ msgstr "Bandbreite"
|
||||
#. Battery label in systems table header
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Bat"
|
||||
msgstr ""
|
||||
msgstr "Bat"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/lib/alerts.ts
|
||||
@@ -350,6 +355,10 @@ msgstr "Überprüfe {email} auf einen Link zum Zurücksetzen."
|
||||
msgid "Check logs for more details."
|
||||
msgstr "Überprüfe die Protokolle für weitere Details."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Check your monitoring service"
|
||||
msgstr "Überprüfen Sie Ihren Überwachungsdienst"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Check your notification service"
|
||||
msgstr "Überprüfe deinen Benachrichtigungsdienst"
|
||||
@@ -643,6 +652,14 @@ msgstr "Leer"
|
||||
msgid "End Time"
|
||||
msgstr "Endzeit"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL"
|
||||
msgstr "Endpunkt-URL"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL to ping (required)"
|
||||
msgstr "Endpunkt-URL zum Pingen (erforderlich)"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Enter email address to reset password"
|
||||
msgstr "E-Mail-Adresse eingeben, um das Passwort zurückzusetzen"
|
||||
@@ -662,6 +679,9 @@ msgstr "Flüchtig"
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
@@ -670,6 +690,10 @@ msgstr "Flüchtig"
|
||||
msgid "Error"
|
||||
msgstr "Fehler"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Example:"
|
||||
msgstr "Beispiel:"
|
||||
|
||||
#. placeholder {0}: alert.value
|
||||
#. placeholder {1}: info.unit
|
||||
#. placeholder {2}: alert.min
|
||||
@@ -727,6 +751,10 @@ msgstr "Authentifizierung fehlgeschlagen"
|
||||
msgid "Failed to save settings"
|
||||
msgstr "Einstellungen konnten nicht gespeichert werden"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Failed to send heartbeat"
|
||||
msgstr "Heartbeat konnte nicht gesendet werden"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Failed to send test notification"
|
||||
msgstr "Testbenachrichtigung konnte nicht gesendet werden"
|
||||
@@ -806,6 +834,18 @@ msgstr "Raster"
|
||||
msgid "Health"
|
||||
msgstr "Gesundheit"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Heartbeat"
|
||||
msgstr "Heartbeat"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat Monitoring"
|
||||
msgstr "Heartbeat-Überwachung"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat sent successfully"
|
||||
msgstr "Heartbeat erfolgreich gesendet"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
@@ -816,6 +856,14 @@ msgstr "Homebrew-Befehl"
|
||||
msgid "Host / IP"
|
||||
msgstr "Host / IP"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP Method"
|
||||
msgstr "HTTP-Methode"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP method: POST, GET, or HEAD (default: POST)"
|
||||
msgstr "HTTP-Methode: POST, GET oder HEAD (Standard: POST)"
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Idle"
|
||||
@@ -834,6 +882,10 @@ msgstr "Image"
|
||||
msgid "Inactive"
|
||||
msgstr "Inaktiv"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Interval"
|
||||
msgstr "Intervall"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Invalid email address."
|
||||
msgstr "Ungültige E-Mail-Adresse."
|
||||
@@ -1096,7 +1148,7 @@ msgstr "Anfrage zum Zurücksetzen des Passworts erhalten"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Past"
|
||||
msgstr ""
|
||||
msgstr "Vergangen"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Pause"
|
||||
@@ -1110,6 +1162,10 @@ msgstr "Pausiert"
|
||||
msgid "Paused ({pausedSystemsLength})"
|
||||
msgstr "Pausiert ({pausedSystemsLength})"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Payload format"
|
||||
msgstr "Payload-Format"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Per-core average utilization"
|
||||
@@ -1121,7 +1177,7 @@ msgstr "Prozentsatz der Zeit in jedem Zustand"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Permanent"
|
||||
msgstr ""
|
||||
msgstr "Permanent"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Persistence"
|
||||
@@ -1248,7 +1304,7 @@ msgstr "Fortsetzen"
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgctxt "Root disk label"
|
||||
msgid "Root"
|
||||
msgstr ""
|
||||
msgstr "Root"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Rotate token"
|
||||
@@ -1307,6 +1363,10 @@ msgstr "Suche"
|
||||
msgid "Search for systems or settings..."
|
||||
msgstr "Nach Systemen oder Einstellungen suchen..."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Seconds between pings (default: 60)"
|
||||
msgstr "Sekunden zwischen Pings (Standard: 60)"
|
||||
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "Siehe <0>Benachrichtigungseinstellungen</0>, um zu konfigurieren, wie du Warnungen erhältst."
|
||||
@@ -1315,6 +1375,18 @@ msgstr "Siehe <0>Benachrichtigungseinstellungen</0>, um zu konfigurieren, wie du
|
||||
msgid "Select {foo}"
|
||||
msgstr "Auswählen {foo}"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send a single heartbeat ping to verify your endpoint is working."
|
||||
msgstr "Senden Sie einen einzelnen Heartbeat-Ping, um zu überprüfen, ob Ihr Endpunkt funktioniert."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send periodic outbound pings to an external monitoring service so you can monitor Beszel without exposing it to the internet."
|
||||
msgstr "Senden Sie regelmäßige ausgehende Pings an einen externen Überwachungsdienst, damit Sie Beszel überwachen können, ohne es dem Internet auszusetzen."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send test heartbeat"
|
||||
msgstr "Test-Heartbeat senden"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "Gesendet"
|
||||
@@ -1335,6 +1407,10 @@ msgstr "Dienste"
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "Prozentuale Schwellenwerte für Zählerfarben festlegen."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Set the following environment variables on your Beszel hub to enable heartbeat monitoring:"
|
||||
msgstr "Legen Sie die folgenden Umgebungsvariablen auf Ihrem Beszel-Hub fest, um die Heartbeat-Überwachung zu aktivieren:"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
@@ -1452,10 +1528,18 @@ msgstr "Temperaturen der Systemsensoren"
|
||||
msgid "Test <0>URL</0>"
|
||||
msgstr "Test <0>URL</0>"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Test heartbeat"
|
||||
msgstr "Test-Heartbeat"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Test notification sent"
|
||||
msgstr "Testbenachrichtigung gesendet"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "The overall status is <0>ok</0> when all systems are up, <1>warn</1> when alerts are triggered, and <2>error</2> when any system is down."
|
||||
msgstr "Der Gesamtstatus ist <0>ok</0>, wenn alle Systeme in Betrieb sind, <1>warn</1>, wenn Warnungen ausgelöst werden, und <2>error</2>, wenn ein System ausgefallen ist."
|
||||
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
msgid "Then log into the backend and reset your user account password in the users table."
|
||||
msgstr "Melde dich dann im Backend an und setze dein Benutzerkontopasswort in der Benutzertabelle zurück."
|
||||
@@ -1642,6 +1726,7 @@ msgid "Upload"
|
||||
msgstr "Hochladen"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Uptime"
|
||||
msgstr "Betriebszeit"
|
||||
|
||||
@@ -1716,6 +1801,10 @@ msgstr "Webhook / Push-Benachrichtigungen"
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation."
|
||||
msgstr "Wenn aktiviert, ermöglicht dieser Token Agenten die Selbstregistrierung ohne vorherige Systemerstellung."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "When using POST, each heartbeat includes a JSON payload with system status summary, list of down systems, and triggered alerts."
|
||||
msgstr "Bei Verwendung von POST enthält jeder Heartbeat eine JSON-Payload mit einer Zusammenfassung des Systemstatus, einer Liste der ausgefallenen Systeme und ausgelösten Warnungen."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
|
||||
@@ -88,6 +88,7 @@ msgstr "Actions"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Active"
|
||||
msgstr "Active"
|
||||
@@ -135,6 +136,10 @@ msgstr "Admin"
|
||||
msgid "After"
|
||||
msgstr "After"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "After setting the environment variables, restart your Beszel hub for changes to take effect."
|
||||
msgstr "After setting the environment variables, restart your Beszel hub for changes to take effect."
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Agent"
|
||||
@@ -345,6 +350,10 @@ msgstr "Check {email} for a reset link."
|
||||
msgid "Check logs for more details."
|
||||
msgstr "Check logs for more details."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Check your monitoring service"
|
||||
msgstr "Check your monitoring service"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Check your notification service"
|
||||
msgstr "Check your notification service"
|
||||
@@ -638,6 +647,14 @@ msgstr "Empty"
|
||||
msgid "End Time"
|
||||
msgstr "End Time"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL"
|
||||
msgstr "Endpoint URL"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL to ping (required)"
|
||||
msgstr "Endpoint URL to ping (required)"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Enter email address to reset password"
|
||||
msgstr "Enter email address to reset password"
|
||||
@@ -657,6 +674,9 @@ msgstr "Ephemeral"
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
@@ -665,6 +685,10 @@ msgstr "Ephemeral"
|
||||
msgid "Error"
|
||||
msgstr "Error"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Example:"
|
||||
msgstr "Example:"
|
||||
|
||||
#. placeholder {0}: alert.value
|
||||
#. placeholder {1}: info.unit
|
||||
#. placeholder {2}: alert.min
|
||||
@@ -722,6 +746,10 @@ msgstr "Failed to authenticate"
|
||||
msgid "Failed to save settings"
|
||||
msgstr "Failed to save settings"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Failed to send heartbeat"
|
||||
msgstr "Failed to send heartbeat"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Failed to send test notification"
|
||||
msgstr "Failed to send test notification"
|
||||
@@ -801,6 +829,18 @@ msgstr "Grid"
|
||||
msgid "Health"
|
||||
msgstr "Health"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Heartbeat"
|
||||
msgstr "Heartbeat"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat Monitoring"
|
||||
msgstr "Heartbeat Monitoring"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat sent successfully"
|
||||
msgstr "Heartbeat sent successfully"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
@@ -811,6 +851,14 @@ msgstr "Homebrew command"
|
||||
msgid "Host / IP"
|
||||
msgstr "Host / IP"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP Method"
|
||||
msgstr "HTTP Method"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP method: POST, GET, or HEAD (default: POST)"
|
||||
msgstr "HTTP method: POST, GET, or HEAD (default: POST)"
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Idle"
|
||||
@@ -829,6 +877,10 @@ msgstr "Image"
|
||||
msgid "Inactive"
|
||||
msgstr "Inactive"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Interval"
|
||||
msgstr "Interval"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Invalid email address."
|
||||
msgstr "Invalid email address."
|
||||
@@ -1105,6 +1157,10 @@ msgstr "Paused"
|
||||
msgid "Paused ({pausedSystemsLength})"
|
||||
msgstr "Paused ({pausedSystemsLength})"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Payload format"
|
||||
msgstr "Payload format"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Per-core average utilization"
|
||||
@@ -1302,6 +1358,10 @@ msgstr "Search"
|
||||
msgid "Search for systems or settings..."
|
||||
msgstr "Search for systems or settings..."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Seconds between pings (default: 60)"
|
||||
msgstr "Seconds between pings (default: 60)"
|
||||
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "See <0>notification settings</0> to configure how you receive alerts."
|
||||
@@ -1310,6 +1370,18 @@ msgstr "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgid "Select {foo}"
|
||||
msgstr "Select {foo}"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send a single heartbeat ping to verify your endpoint is working."
|
||||
msgstr "Send a single heartbeat ping to verify your endpoint is working."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send periodic outbound pings to an external monitoring service so you can monitor Beszel without exposing it to the internet."
|
||||
msgstr "Send periodic outbound pings to an external monitoring service so you can monitor Beszel without exposing it to the internet."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send test heartbeat"
|
||||
msgstr "Send test heartbeat"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "Sent"
|
||||
@@ -1330,6 +1402,10 @@ msgstr "Services"
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "Set percentage thresholds for meter colors."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Set the following environment variables on your Beszel hub to enable heartbeat monitoring:"
|
||||
msgstr "Set the following environment variables on your Beszel hub to enable heartbeat monitoring:"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
@@ -1447,10 +1523,18 @@ msgstr "Temperatures of system sensors"
|
||||
msgid "Test <0>URL</0>"
|
||||
msgstr "Test <0>URL</0>"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Test heartbeat"
|
||||
msgstr "Test heartbeat"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Test notification sent"
|
||||
msgstr "Test notification sent"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "The overall status is <0>ok</0> when all systems are up, <1>warn</1> when alerts are triggered, and <2>error</2> when any system is down."
|
||||
msgstr "The overall status is <0>ok</0> when all systems are up, <1>warn</1> when alerts are triggered, and <2>error</2> when any system is down."
|
||||
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
msgid "Then log into the backend and reset your user account password in the users table."
|
||||
msgstr "Then log into the backend and reset your user account password in the users table."
|
||||
@@ -1637,6 +1721,7 @@ msgid "Upload"
|
||||
msgstr "Upload"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Uptime"
|
||||
msgstr "Uptime"
|
||||
|
||||
@@ -1711,6 +1796,10 @@ msgstr "Webhook / Push notifications"
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation."
|
||||
msgstr "When enabled, this token allows agents to self-register without prior system creation."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "When using POST, each heartbeat includes a JSON payload with system status summary, list of down systems, and triggered alerts."
|
||||
msgstr "When using POST, each heartbeat includes a JSON payload with system status summary, list of down systems, and triggered alerts."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
|
||||
@@ -93,6 +93,7 @@ msgstr "Acciones"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Active"
|
||||
msgstr "Activo"
|
||||
@@ -140,6 +141,10 @@ msgstr "Administrador"
|
||||
msgid "After"
|
||||
msgstr "Después"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "After setting the environment variables, restart your Beszel hub for changes to take effect."
|
||||
msgstr "Después de configurar las variables de entorno, reinicie su hub Beszel para que los cambios surtan efecto."
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Agente"
|
||||
@@ -350,6 +355,10 @@ msgstr "Revisa {email} para un enlace de restablecimiento."
|
||||
msgid "Check logs for more details."
|
||||
msgstr "Revisa los registros para más detalles."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Check your monitoring service"
|
||||
msgstr "Compruebe su servicio de monitorización"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Check your notification service"
|
||||
msgstr "Verifica tu servicio de notificaciones"
|
||||
@@ -643,6 +652,14 @@ msgstr "Vacía"
|
||||
msgid "End Time"
|
||||
msgstr "Hora de finalización"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL"
|
||||
msgstr "URL del punto de conexión"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL to ping (required)"
|
||||
msgstr "URL del punto de conexión para ping (obligatorio)"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Enter email address to reset password"
|
||||
msgstr "Ingresa la dirección de correo electrónico para restablecer la contraseña"
|
||||
@@ -662,6 +679,9 @@ msgstr "Efímero"
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
@@ -670,6 +690,10 @@ msgstr "Efímero"
|
||||
msgid "Error"
|
||||
msgstr "Error"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Example:"
|
||||
msgstr "Ejemplo:"
|
||||
|
||||
#. placeholder {0}: alert.value
|
||||
#. placeholder {1}: info.unit
|
||||
#. placeholder {2}: alert.min
|
||||
@@ -727,6 +751,10 @@ msgstr "Error al autenticar"
|
||||
msgid "Failed to save settings"
|
||||
msgstr "Error al guardar la configuración"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Failed to send heartbeat"
|
||||
msgstr "Error al enviar el latido (heartbeat)"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Failed to send test notification"
|
||||
msgstr "Error al enviar la notificación de prueba"
|
||||
@@ -806,6 +834,18 @@ msgstr "Cuadrícula"
|
||||
msgid "Health"
|
||||
msgstr "Estado"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Heartbeat"
|
||||
msgstr "Heartbeat"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat Monitoring"
|
||||
msgstr "Monitorización de Heartbeat"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat sent successfully"
|
||||
msgstr "Latido enviado con éxito"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
@@ -816,6 +856,14 @@ msgstr "Comando Homebrew"
|
||||
msgid "Host / IP"
|
||||
msgstr "Servidor / IP"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP Method"
|
||||
msgstr "Método HTTP"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP method: POST, GET, or HEAD (default: POST)"
|
||||
msgstr "Método HTTP: POST, GET o HEAD (predeterminado: POST)"
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Idle"
|
||||
@@ -834,6 +882,10 @@ msgstr "Imagen"
|
||||
msgid "Inactive"
|
||||
msgstr "Inactivo"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Interval"
|
||||
msgstr "Intervalo"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Invalid email address."
|
||||
msgstr "Dirección de correo electrónico no válida."
|
||||
@@ -1110,6 +1162,10 @@ msgstr "Pausado"
|
||||
msgid "Paused ({pausedSystemsLength})"
|
||||
msgstr "Pausado ({pausedSystemsLength})"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Payload format"
|
||||
msgstr "Formato de carga útil (payload)"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Per-core average utilization"
|
||||
@@ -1307,6 +1363,10 @@ msgstr "Buscar"
|
||||
msgid "Search for systems or settings..."
|
||||
msgstr "Buscar sistemas o configuraciones..."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Seconds between pings (default: 60)"
|
||||
msgstr "Segundos entre pings (predeterminado: 60)"
|
||||
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "Consulta la <0>configuración de notificaciones</0> para configurar cómo recibes alertas."
|
||||
@@ -1315,6 +1375,18 @@ msgstr "Consulta la <0>configuración de notificaciones</0> para configurar cóm
|
||||
msgid "Select {foo}"
|
||||
msgstr "Seleccionar {foo}"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send a single heartbeat ping to verify your endpoint is working."
|
||||
msgstr "Envíe un único ping de latido para verificar que su punto de conexión funciona."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send periodic outbound pings to an external monitoring service so you can monitor Beszel without exposing it to the internet."
|
||||
msgstr "Envíe pings salientes periódicos a un servicio de monitorización externo para que pueda supervisar Beszel sin exponerlo a internet."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send test heartbeat"
|
||||
msgstr "Enviar latido de prueba"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "Enviado"
|
||||
@@ -1335,6 +1407,10 @@ msgstr "Servicios"
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "Establecer umbrales de porcentaje para los colores de los medidores."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Set the following environment variables on your Beszel hub to enable heartbeat monitoring:"
|
||||
msgstr "Configure las siguientes variables de entorno en su hub Beszel para habilitar la monitorización de latidos:"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
@@ -1452,10 +1528,18 @@ msgstr "Temperaturas de los sensores del sistema"
|
||||
msgid "Test <0>URL</0>"
|
||||
msgstr "Probar <0>URL</0>"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Test heartbeat"
|
||||
msgstr "Probar latido"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Test notification sent"
|
||||
msgstr "Notificación de prueba enviada"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "The overall status is <0>ok</0> when all systems are up, <1>warn</1> when alerts are triggered, and <2>error</2> when any system is down."
|
||||
msgstr "El estado general es <0>ok</0> cuando todos los sistemas están activos, <1>warn</1> cuando se activan alertas y <2>error</2> cuando algún sistema está caído."
|
||||
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
msgid "Then log into the backend and reset your user account password in the users table."
|
||||
msgstr "Luego inicia sesión en el backend y restablece la contraseña de tu cuenta de usuario en la tabla de usuarios."
|
||||
@@ -1642,8 +1726,9 @@ msgid "Upload"
|
||||
msgstr "Cargar"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Uptime"
|
||||
msgstr "Tiempo de actividad"
|
||||
msgstr "Uptime"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
@@ -1716,6 +1801,10 @@ msgstr "Notificaciones Webhook / Push"
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation."
|
||||
msgstr "Cuando está habilitado, este token permite a los agentes registrarse automáticamente sin creación previa del sistema."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "When using POST, each heartbeat includes a JSON payload with system status summary, list of down systems, and triggered alerts."
|
||||
msgstr "Al usar POST, cada latido incluye una carga útil JSON con un resumen del estado del sistema, una lista de sistemas caídos y alertas activadas."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
|
||||
@@ -93,6 +93,7 @@ msgstr "عملیات"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Active"
|
||||
msgstr "فعال"
|
||||
@@ -140,6 +141,10 @@ msgstr "مدیر"
|
||||
msgid "After"
|
||||
msgstr "بعد از"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "After setting the environment variables, restart your Beszel hub for changes to take effect."
|
||||
msgstr "پس از تنظیم متغیرهای محیطی، هاب Beszel خود را مجدداً راه اندازی کنید تا تغییرات اعمال شوند."
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "عامل"
|
||||
@@ -350,6 +355,10 @@ msgstr "ایمیل {email} خود را برای لینک بازنشانی برر
|
||||
msgid "Check logs for more details."
|
||||
msgstr "برای جزئیات بیشتر، لاگها را بررسی کنید."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Check your monitoring service"
|
||||
msgstr "سرویس نظارتی خود را بررسی کنید"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Check your notification service"
|
||||
msgstr "سرویس اطلاعرسانی خود را بررسی کنید"
|
||||
@@ -643,6 +652,14 @@ msgstr "خالی"
|
||||
msgid "End Time"
|
||||
msgstr "زمان پایان"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL"
|
||||
msgstr "URL نقطه پایانی"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL to ping (required)"
|
||||
msgstr "URL نقطه پایانی برای پینگ (الزامی)"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Enter email address to reset password"
|
||||
msgstr "آدرس ایمیل را برای بازنشانی رمز عبور وارد کنید"
|
||||
@@ -662,6 +679,9 @@ msgstr "گذرا"
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
@@ -670,6 +690,10 @@ msgstr "گذرا"
|
||||
msgid "Error"
|
||||
msgstr "خطا"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Example:"
|
||||
msgstr "مثال:"
|
||||
|
||||
#. placeholder {0}: alert.value
|
||||
#. placeholder {1}: info.unit
|
||||
#. placeholder {2}: alert.min
|
||||
@@ -727,6 +751,10 @@ msgstr "احراز هویت ناموفق بود"
|
||||
msgid "Failed to save settings"
|
||||
msgstr "ذخیره تنظیمات ناموفق بود"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Failed to send heartbeat"
|
||||
msgstr "ارسال ضربان قلب ناموفق بود"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Failed to send test notification"
|
||||
msgstr "ارسال اعلان آزمایشی ناموفق بود"
|
||||
@@ -806,6 +834,18 @@ msgstr "جدول"
|
||||
msgid "Health"
|
||||
msgstr "سلامتی"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Heartbeat"
|
||||
msgstr "ضربان قلب"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat Monitoring"
|
||||
msgstr "نظارت بر ضربان قلب"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat sent successfully"
|
||||
msgstr "ضربان قلب با موفقیت ارسال شد"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
@@ -816,6 +856,14 @@ msgstr "دستور Homebrew"
|
||||
msgid "Host / IP"
|
||||
msgstr "میزبان / IP"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP Method"
|
||||
msgstr "متد HTTP"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP method: POST, GET, or HEAD (default: POST)"
|
||||
msgstr "متد HTTP: POST، GET، یا HEAD (پیشفرض: POST)"
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Idle"
|
||||
@@ -834,6 +882,10 @@ msgstr "تصویر"
|
||||
msgid "Inactive"
|
||||
msgstr "غیرفعال"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Interval"
|
||||
msgstr "بازه زمانی"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Invalid email address."
|
||||
msgstr "آدرس ایمیل نامعتبر است."
|
||||
@@ -1110,6 +1162,10 @@ msgstr "مکث شده"
|
||||
msgid "Paused ({pausedSystemsLength})"
|
||||
msgstr "مکث شده ({pausedSystemsLength})"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Payload format"
|
||||
msgstr "فرمت پیلود"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Per-core average utilization"
|
||||
@@ -1307,6 +1363,10 @@ msgstr "جستجو"
|
||||
msgid "Search for systems or settings..."
|
||||
msgstr "جستجو برای سیستمها یا تنظیمات..."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Seconds between pings (default: 60)"
|
||||
msgstr "ثانیه بین پینگها (پیشفرض: ۶۰)"
|
||||
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "برای پیکربندی نحوه دریافت هشدارها، به <0>تنظیمات اعلان</0> مراجعه کنید."
|
||||
@@ -1315,6 +1375,18 @@ msgstr "برای پیکربندی نحوه دریافت هشدارها، به <0
|
||||
msgid "Select {foo}"
|
||||
msgstr "انتخاب {foo}"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send a single heartbeat ping to verify your endpoint is working."
|
||||
msgstr "یک پینگ ضربان قلب تکی ارسال کنید تا از کارکرد نقطه پایانی خود اطمینان حاصل کنید."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send periodic outbound pings to an external monitoring service so you can monitor Beszel without exposing it to the internet."
|
||||
msgstr "پینگهای خروجی دورهای را به یک سرویس نظارتی خارجی ارسال کنید تا بتوانید Beszel را بدون قرار دادن آن در معرض اینترنت نظارت کنید."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send test heartbeat"
|
||||
msgstr "ارسال ضربان قلب آزمایشی"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "ارسال شد"
|
||||
@@ -1335,6 +1407,10 @@ msgstr "سرویسها"
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "آستانه های درصدی را برای رنگ های متر تنظیم کنید."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Set the following environment variables on your Beszel hub to enable heartbeat monitoring:"
|
||||
msgstr "متغیرهای محیطی زیر را در هاب Beszel خود تنظیم کنید تا نظارت بر ضربان قلب فعال شود:"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
@@ -1452,10 +1528,18 @@ msgstr "دمای حسگرهای سیستم"
|
||||
msgid "Test <0>URL</0>"
|
||||
msgstr "تست <0>آدرس اینترنتی</0>"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Test heartbeat"
|
||||
msgstr "تست ضربان قلب"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Test notification sent"
|
||||
msgstr "اعلان آزمایشی ارسال شد"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "The overall status is <0>ok</0> when all systems are up, <1>warn</1> when alerts are triggered, and <2>error</2> when any system is down."
|
||||
msgstr "وضعیت کلی زمانی <0>ok</0> است که همه سیستمها بالا باشند، <1>warn</1> زمانی که هشدارها فعال شوند، و <2>error</2> زمانی که هر سیستمی پایین باشد."
|
||||
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
msgid "Then log into the backend and reset your user account password in the users table."
|
||||
msgstr "سپس وارد بخش پشتیبان شوید و رمز عبور حساب کاربری خود را در جدول کاربران بازنشانی کنید."
|
||||
@@ -1642,6 +1726,7 @@ msgid "Upload"
|
||||
msgstr "آپلود"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Uptime"
|
||||
msgstr "آپتایم"
|
||||
|
||||
@@ -1716,6 +1801,10 @@ msgstr "اعلانهای Webhook / Push"
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation."
|
||||
msgstr "هنگامی که فعال باشد، این توکن به عوامل اجازه میدهد بدون ایجاد سیستم قبلی، خود را ثبت کنند."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "When using POST, each heartbeat includes a JSON payload with system status summary, list of down systems, and triggered alerts."
|
||||
msgstr "هنگام استفاده از POST، هر ضربان قلب شامل یک پیلود JSON با خلاصه وضعیت سیستم، لیست سیستمهای پایین و هشدارهای فعال شده است."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
|
||||
@@ -93,6 +93,7 @@ msgstr "Actions"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Active"
|
||||
msgstr "Active"
|
||||
@@ -140,6 +141,10 @@ msgstr "Admin"
|
||||
msgid "After"
|
||||
msgstr "Après"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "After setting the environment variables, restart your Beszel hub for changes to take effect."
|
||||
msgstr "Après avoir défini les variables d'environnement, redémarrez votre hub Beszel pour que les changements prennent effet."
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Agent"
|
||||
@@ -231,7 +236,7 @@ msgstr "Bande passante"
|
||||
#. Battery label in systems table header
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Bat"
|
||||
msgstr ""
|
||||
msgstr "Bat"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/lib/alerts.ts
|
||||
@@ -350,6 +355,10 @@ msgstr "Vérifiez {email} pour un lien de réinitialisation."
|
||||
msgid "Check logs for more details."
|
||||
msgstr "Vérifiez les journaux pour plus de détails."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Check your monitoring service"
|
||||
msgstr "Vérifiez votre service de surveillance"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Check your notification service"
|
||||
msgstr "Vérifiez votre service de notification"
|
||||
@@ -643,6 +652,14 @@ msgstr "Vide"
|
||||
msgid "End Time"
|
||||
msgstr "Heure de fin"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL"
|
||||
msgstr "URL du point de terminaison"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL to ping (required)"
|
||||
msgstr "URL du point de terminaison à pinguer (requis)"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Enter email address to reset password"
|
||||
msgstr "Entrez l'adresse email pour réinitialiser le mot de passe"
|
||||
@@ -662,6 +679,9 @@ msgstr "Éphémère"
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
@@ -670,6 +690,10 @@ msgstr "Éphémère"
|
||||
msgid "Error"
|
||||
msgstr "Erreur"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Example:"
|
||||
msgstr "Exemple :"
|
||||
|
||||
#. placeholder {0}: alert.value
|
||||
#. placeholder {1}: info.unit
|
||||
#. placeholder {2}: alert.min
|
||||
@@ -727,6 +751,10 @@ msgstr "Échec de l'authentification"
|
||||
msgid "Failed to save settings"
|
||||
msgstr "Échec de l'enregistrement des paramètres"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Failed to send heartbeat"
|
||||
msgstr "Échec de l'envoi du battement de cœur (heartbeat)"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Failed to send test notification"
|
||||
msgstr "Échec de l'envoi de la notification de test"
|
||||
@@ -806,6 +834,18 @@ msgstr "Grille"
|
||||
msgid "Health"
|
||||
msgstr "Santé"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Heartbeat"
|
||||
msgstr "Heartbeat"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat Monitoring"
|
||||
msgstr "Surveillance Heartbeat"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat sent successfully"
|
||||
msgstr "Battement de cœur envoyé avec succès"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
@@ -816,6 +856,14 @@ msgstr "Commande Homebrew"
|
||||
msgid "Host / IP"
|
||||
msgstr "Hôte / IP"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP Method"
|
||||
msgstr "Méthode HTTP"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP method: POST, GET, or HEAD (default: POST)"
|
||||
msgstr "Méthode HTTP : POST, GET ou HEAD (par défaut : POST)"
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Idle"
|
||||
@@ -834,6 +882,10 @@ msgstr "Image"
|
||||
msgid "Inactive"
|
||||
msgstr "Inactif"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Interval"
|
||||
msgstr "Intervalle"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Invalid email address."
|
||||
msgstr "Adresse email invalide."
|
||||
@@ -1110,6 +1162,10 @@ msgstr "En pause"
|
||||
msgid "Paused ({pausedSystemsLength})"
|
||||
msgstr "Mis en pause ({pausedSystemsLength})"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Payload format"
|
||||
msgstr "Format de la charge utile"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Per-core average utilization"
|
||||
@@ -1307,6 +1363,10 @@ msgstr "Recherche"
|
||||
msgid "Search for systems or settings..."
|
||||
msgstr "Rechercher des systèmes ou des paramètres..."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Seconds between pings (default: 60)"
|
||||
msgstr "Secondes entre les pings (par défaut : 60)"
|
||||
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "Voir les <0>paramètres de notification</0> pour configurer comment vous recevez les alertes."
|
||||
@@ -1315,6 +1375,18 @@ msgstr "Voir les <0>paramètres de notification</0> pour configurer comment vous
|
||||
msgid "Select {foo}"
|
||||
msgstr "Sélectionner {foo}"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send a single heartbeat ping to verify your endpoint is working."
|
||||
msgstr "Envoyez un seul ping heartbeat pour vérifier que votre point de terminaison fonctionne."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send periodic outbound pings to an external monitoring service so you can monitor Beszel without exposing it to the internet."
|
||||
msgstr "Envoyez des pings sortants périodiques vers un service de surveillance externe afin de pouvoir surveiller Beszel sans l'exposer à Internet."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send test heartbeat"
|
||||
msgstr "Envoyer un heartbeat de test"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "Envoyé"
|
||||
@@ -1335,6 +1407,10 @@ msgstr "Services"
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "Définir des seuils de pourcentage pour les couleurs des compteurs."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Set the following environment variables on your Beszel hub to enable heartbeat monitoring:"
|
||||
msgstr "Définissez les variables d'environnement suivantes sur votre hub Beszel pour activer la surveillance du heartbeat :"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
@@ -1452,10 +1528,18 @@ msgstr "Températures des capteurs du système"
|
||||
msgid "Test <0>URL</0>"
|
||||
msgstr "Tester <0>URL</0>"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Test heartbeat"
|
||||
msgstr "Tester le heartbeat"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Test notification sent"
|
||||
msgstr "Notification de test envoyée"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "The overall status is <0>ok</0> when all systems are up, <1>warn</1> when alerts are triggered, and <2>error</2> when any system is down."
|
||||
msgstr "L'état général est <0>ok</0> quand tous les systèmes sont opérationnels, <1>warn</1> quand des alertes sont déclenchées, et <2>error</2> quand un système est en panne."
|
||||
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
msgid "Then log into the backend and reset your user account password in the users table."
|
||||
msgstr "Ensuite, connectez-vous au backend et réinitialisez le mot de passe de votre compte utilisateur dans la table des utilisateurs."
|
||||
@@ -1642,8 +1726,9 @@ msgid "Upload"
|
||||
msgstr "Téléverser"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Uptime"
|
||||
msgstr "Temps de fonctionnement"
|
||||
msgstr "Uptime"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
@@ -1716,6 +1801,10 @@ msgstr "Notifications Webhook / Push"
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation."
|
||||
msgstr "Lorsqu'il est activé, ce jeton permet aux agents de s'enregistrer automatiquement sans création préalable du système."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "When using POST, each heartbeat includes a JSON payload with system status summary, list of down systems, and triggered alerts."
|
||||
msgstr "En utilisant POST, chaque heartbeat inclut une charge utile JSON avec un résumé de l'état du sistema, la liste des systèmes en panne et les alertes déclenchées."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
|
||||
@@ -93,6 +93,7 @@ msgstr "פעולות"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Active"
|
||||
msgstr "פעיל"
|
||||
@@ -140,6 +141,10 @@ msgstr "מנהל"
|
||||
msgid "After"
|
||||
msgstr "אחרי"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "After setting the environment variables, restart your Beszel hub for changes to take effect."
|
||||
msgstr "לאחר הגדרת משתני הסביבה, הפעל מחדש את ה-Beszel hub שלך כדי שהשינויים ייכנסו לתוקף."
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "סוכן"
|
||||
@@ -350,6 +355,10 @@ msgstr "בדוק את {email} לקישור איפוס."
|
||||
msgid "Check logs for more details."
|
||||
msgstr "בדוק לוגים לפרטים נוספים"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Check your monitoring service"
|
||||
msgstr "בדוק את שירות הניטור שלך"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Check your notification service"
|
||||
msgstr "בדוק את שירות ההתראות שלך"
|
||||
@@ -643,6 +652,14 @@ msgstr "ריק"
|
||||
msgid "End Time"
|
||||
msgstr "זמן סיום"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL"
|
||||
msgstr "URL של נקודת קצה"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL to ping (required)"
|
||||
msgstr "URL של נקודת קצה לפינג (חובה)"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Enter email address to reset password"
|
||||
msgstr "הכנס כתובת אימייל לאיפוס סיסמה"
|
||||
@@ -662,6 +679,9 @@ msgstr "זמני"
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
@@ -670,6 +690,10 @@ msgstr "זמני"
|
||||
msgid "Error"
|
||||
msgstr "שגיאה"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Example:"
|
||||
msgstr "דוגמה:"
|
||||
|
||||
#. placeholder {0}: alert.value
|
||||
#. placeholder {1}: info.unit
|
||||
#. placeholder {2}: alert.min
|
||||
@@ -727,6 +751,10 @@ msgstr "אימות נכשל"
|
||||
msgid "Failed to save settings"
|
||||
msgstr "שמירת הגדרות נכשלה"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Failed to send heartbeat"
|
||||
msgstr "שליחת פעימת הלב נכשלה"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Failed to send test notification"
|
||||
msgstr "שליחת התראת בדיקה נכשלה"
|
||||
@@ -806,6 +834,18 @@ msgstr "רשת"
|
||||
msgid "Health"
|
||||
msgstr "בריאות"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Heartbeat"
|
||||
msgstr "פעימת לב"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat Monitoring"
|
||||
msgstr "ניטור פעימות לב"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat sent successfully"
|
||||
msgstr "פעימת הלב נשלחה בהצלחה"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
@@ -816,6 +856,14 @@ msgstr "פקודת Homebrew"
|
||||
msgid "Host / IP"
|
||||
msgstr "מארח / IP"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP Method"
|
||||
msgstr "שיטת HTTP"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP method: POST, GET, or HEAD (default: POST)"
|
||||
msgstr "שיטת HTTP: POST, GET, או HEAD (ברירת מחדל: POST)"
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Idle"
|
||||
@@ -834,6 +882,10 @@ msgstr "תמונה"
|
||||
msgid "Inactive"
|
||||
msgstr "לא פעיל"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Interval"
|
||||
msgstr "מרווח"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Invalid email address."
|
||||
msgstr "כתובת אימייל לא תקינה."
|
||||
@@ -1110,6 +1162,10 @@ msgstr "מושהה"
|
||||
msgid "Paused ({pausedSystemsLength})"
|
||||
msgstr "מושהה ({pausedSystemsLength})"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Payload format"
|
||||
msgstr "פורמט מטען (Payload)"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Per-core average utilization"
|
||||
@@ -1307,6 +1363,10 @@ msgstr "חיפוש"
|
||||
msgid "Search for systems or settings..."
|
||||
msgstr "חפש מערכות או הגדרות..."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Seconds between pings (default: 60)"
|
||||
msgstr "שניות בין פינגים (ברירת מחדל: 60)"
|
||||
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "ראה <0>הגדרות התראות</0> כדי להגדיר כיצד אתה מקבל התראות."
|
||||
@@ -1315,6 +1375,18 @@ msgstr "ראה <0>הגדרות התראות</0> כדי להגדיר כיצד א
|
||||
msgid "Select {foo}"
|
||||
msgstr "בחר {foo}"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send a single heartbeat ping to verify your endpoint is working."
|
||||
msgstr "שלח פינג פעימת לב בודד כדי לוודא שנקודת הקצה שלך עובדת."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send periodic outbound pings to an external monitoring service so you can monitor Beszel without exposing it to the internet."
|
||||
msgstr "שלח פינגים יוצאים תקופתיים לשירות ניטור חיצוני כדי שתוכל לנטר את Beszel מבלי לחשוף אותו לאינטרנט."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send test heartbeat"
|
||||
msgstr "שלח פעימת לב לבדיקה"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "נשלח"
|
||||
@@ -1335,6 +1407,10 @@ msgstr "שירותים"
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "הגדר סף אחוזים עבור צבעי מד."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Set the following environment variables on your Beszel hub to enable heartbeat monitoring:"
|
||||
msgstr "הגדר את משתני הסביבה הבאים ב-Beszel hub שלך כדי לאפשר ניטור פעימות לב:"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
@@ -1452,10 +1528,18 @@ msgstr "טמפרטורות של חיישני המערכת"
|
||||
msgid "Test <0>URL</0>"
|
||||
msgstr "בדוק <0>URL</0>"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Test heartbeat"
|
||||
msgstr "בדוק פעימת לב"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Test notification sent"
|
||||
msgstr "התראת בדיקה נשלחה"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "The overall status is <0>ok</0> when all systems are up, <1>warn</1> when alerts are triggered, and <2>error</2> when any system is down."
|
||||
msgstr "הסטטוס הכללי הוא <0>ok</0> כשכל המערכות פועלות, <1>warn</1> כשמופעלות התראות, ו-<2>error</2> כשמערכת כלשהי מושבתת."
|
||||
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
msgid "Then log into the backend and reset your user account password in the users table."
|
||||
msgstr "לאחר מכן התחבר ל-backend ואפס את סיסמת חשבון המשתמש שלך בטבלת המשתמשים."
|
||||
@@ -1642,6 +1726,7 @@ msgid "Upload"
|
||||
msgstr "העלאה"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Uptime"
|
||||
msgstr "זמן פעילות"
|
||||
|
||||
@@ -1716,6 +1801,10 @@ msgstr "Webhook / התראות דחיפה"
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation."
|
||||
msgstr "כאשר מופעל, אסימון זה מאפשר לסוכנים להירשם באופן עצמי ללא יצירת מערכת מוקדמת."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "When using POST, each heartbeat includes a JSON payload with system status summary, list of down systems, and triggered alerts."
|
||||
msgstr "בשימוש ב-POST, כל פעימת לב כוללת מטען JSON עם סיכום סטטוס המערכת, רשימת מערכות מושבתות והתראות שהופעלו."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
|
||||
@@ -93,6 +93,7 @@ msgstr "Akcije"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Active"
|
||||
msgstr "Aktivan"
|
||||
@@ -140,6 +141,10 @@ msgstr "Admin"
|
||||
msgid "After"
|
||||
msgstr "Nakon"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "After setting the environment variables, restart your Beszel hub for changes to take effect."
|
||||
msgstr "Nakon postavljanja varijabli okruženja, ponovno pokrenite svoj Beszel hub kako bi promjene stupile na snagu."
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Agent"
|
||||
@@ -231,7 +236,7 @@ msgstr "Mrežna Propusnost"
|
||||
#. Battery label in systems table header
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Bat"
|
||||
msgstr ""
|
||||
msgstr "Bat"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/lib/alerts.ts
|
||||
@@ -350,6 +355,10 @@ msgstr "Provjerite {email} za pristup poveznici za resetiranje."
|
||||
msgid "Check logs for more details."
|
||||
msgstr "Provjerite zapise (logove) za više detalja."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Check your monitoring service"
|
||||
msgstr "Provjerite svoju uslugu nadzora"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Check your notification service"
|
||||
msgstr "Provjerite svoju obavještajnu uslugu"
|
||||
@@ -643,6 +652,14 @@ msgstr "Prazno"
|
||||
msgid "End Time"
|
||||
msgstr "Vrijeme završetka"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL"
|
||||
msgstr "URL krajnje točke"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL to ping (required)"
|
||||
msgstr "URL krajnje točke za pinganje (obavezno)"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Enter email address to reset password"
|
||||
msgstr "Unesite email adresu kako biste resetirali lozinku"
|
||||
@@ -662,6 +679,9 @@ msgstr "Efemeran"
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
@@ -670,6 +690,10 @@ msgstr "Efemeran"
|
||||
msgid "Error"
|
||||
msgstr "Greška"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Example:"
|
||||
msgstr "Primjer:"
|
||||
|
||||
#. placeholder {0}: alert.value
|
||||
#. placeholder {1}: info.unit
|
||||
#. placeholder {2}: alert.min
|
||||
@@ -727,6 +751,10 @@ msgstr "Neuspješna provjera autentičnosti"
|
||||
msgid "Failed to save settings"
|
||||
msgstr "Neuspješno spremanje postavki"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Failed to send heartbeat"
|
||||
msgstr "Slanje heartbeata nije uspjelo"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Failed to send test notification"
|
||||
msgstr "Neuspješno slanje probne obavijesti"
|
||||
@@ -806,6 +834,18 @@ msgstr "Rešetka"
|
||||
msgid "Health"
|
||||
msgstr "Zdravlje"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Heartbeat"
|
||||
msgstr "Heartbeat"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat Monitoring"
|
||||
msgstr "Nadzor heartbeata"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat sent successfully"
|
||||
msgstr "Heartbeat uspješno poslan"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
@@ -816,6 +856,14 @@ msgstr "Homebrew naredba"
|
||||
msgid "Host / IP"
|
||||
msgstr "Host / IP"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP Method"
|
||||
msgstr "HTTP metoda"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP method: POST, GET, or HEAD (default: POST)"
|
||||
msgstr "HTTP metoda: POST, GET ili HEAD (zadano: POST)"
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Idle"
|
||||
@@ -834,6 +882,10 @@ msgstr "Slika"
|
||||
msgid "Inactive"
|
||||
msgstr "Neaktivno"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Interval"
|
||||
msgstr "Interval"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Invalid email address."
|
||||
msgstr "Nevažeća email adresa."
|
||||
@@ -958,7 +1010,7 @@ msgstr "Iskorištenost memorije Docker spremnika"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Model"
|
||||
msgstr ""
|
||||
msgstr "Model"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
@@ -1110,6 +1162,10 @@ msgstr "Pauzirano"
|
||||
msgid "Paused ({pausedSystemsLength})"
|
||||
msgstr "Pauzirano ({pausedSystemsLength})"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Payload format"
|
||||
msgstr "Format korisnog tereta (Payload)"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Per-core average utilization"
|
||||
@@ -1307,6 +1363,10 @@ msgstr "Pretraži"
|
||||
msgid "Search for systems or settings..."
|
||||
msgstr "Pretraži za sisteme ili postavke..."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Seconds between pings (default: 60)"
|
||||
msgstr "Sekunde između pingova (zadano: 60)"
|
||||
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "Pogledajte <0>postavke obavijesti</0> da biste konfigurirali način primanja upozorenja."
|
||||
@@ -1315,6 +1375,18 @@ msgstr "Pogledajte <0>postavke obavijesti</0> da biste konfigurirali način prim
|
||||
msgid "Select {foo}"
|
||||
msgstr "Odaberi {foo}"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send a single heartbeat ping to verify your endpoint is working."
|
||||
msgstr "Pošaljite jedan heartbeat ping kako biste provjerili radi li vaša krajnja točka."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send periodic outbound pings to an external monitoring service so you can monitor Beszel without exposing it to the internet."
|
||||
msgstr "Šaljite povremene odlazne pingove vanjskoj usluzi nadzora kako biste mogli nadzirati Beszel bez izlaganja internetu."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send test heartbeat"
|
||||
msgstr "Pošalji testni heartbeat"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "Poslano"
|
||||
@@ -1335,6 +1407,10 @@ msgstr "Usluge"
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "Postavite pragove postotka za boje mjerača."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Set the following environment variables on your Beszel hub to enable heartbeat monitoring:"
|
||||
msgstr "Postavite sljedeće varijable okruženja na svom Beszel hubu kako biste omogućili nadzor heartbeata:"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
@@ -1452,10 +1528,18 @@ msgstr "Temperature sistemskih senzora"
|
||||
msgid "Test <0>URL</0>"
|
||||
msgstr "Testni <0>URL</0>"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Test heartbeat"
|
||||
msgstr "Testiraj heartbeat"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Test notification sent"
|
||||
msgstr "Testna obavijest poslana"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "The overall status is <0>ok</0> when all systems are up, <1>warn</1> when alerts are triggered, and <2>error</2> when any system is down."
|
||||
msgstr "Ukupni status je <0>ok</0> kada su svi sustavi u radu, <1>warn</1> kada su aktivirana upozorenja i <2>error</2> kada je bilo koji sustav isključen."
|
||||
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
msgid "Then log into the backend and reset your user account password in the users table."
|
||||
msgstr "Zatim se prijavite u backend i resetirajte lozinku korisničkog računa u tablici korisnika."
|
||||
@@ -1642,8 +1726,9 @@ msgid "Upload"
|
||||
msgstr "Otpremi"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Uptime"
|
||||
msgstr "Vrijeme rada"
|
||||
msgstr "Uptime"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
@@ -1716,6 +1801,10 @@ msgstr "Webhook / Push obavijest"
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation."
|
||||
msgstr "Kada je omogućen, ovaj token omogućuje agentima da se sami registriraju bez prethodnog stvaranja sustava."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "When using POST, each heartbeat includes a JSON payload with system status summary, list of down systems, and triggered alerts."
|
||||
msgstr "Kada koristite POST, svaki heartbeat uključuje JSON payload sa sažetkom statusa sustava, popisom isključenih sustava i aktiviranim upozorenjima."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
|
||||
@@ -93,6 +93,7 @@ msgstr "Műveletek"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Active"
|
||||
msgstr "Aktív"
|
||||
@@ -140,6 +141,10 @@ msgstr "Adminisztráció"
|
||||
msgid "After"
|
||||
msgstr "Utána"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "After setting the environment variables, restart your Beszel hub for changes to take effect."
|
||||
msgstr "A környezeti változók beállítása után indítsa újra a Beszel hubot a módosítások érvénybe léptetéséhez."
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Ügynök"
|
||||
@@ -350,6 +355,10 @@ msgstr "Ellenőrizd a {email} címet a visszaállító linkért."
|
||||
msgid "Check logs for more details."
|
||||
msgstr "Ellenőrizd a naplót a további részletekért."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Check your monitoring service"
|
||||
msgstr "Ellenőrizze a megfigyelő szolgáltatást"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Check your notification service"
|
||||
msgstr "Ellenőrizd az értesítési szolgáltatásodat"
|
||||
@@ -643,6 +652,14 @@ msgstr "Üres"
|
||||
msgid "End Time"
|
||||
msgstr "Befejezés ideje"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL"
|
||||
msgstr "Végpont URL"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL to ping (required)"
|
||||
msgstr "Pingelendő végpont URL (kötelező)"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Enter email address to reset password"
|
||||
msgstr "E-mail cím megadása a jelszó visszaállításához"
|
||||
@@ -662,6 +679,9 @@ msgstr "Ideiglenes"
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
@@ -670,6 +690,10 @@ msgstr "Ideiglenes"
|
||||
msgid "Error"
|
||||
msgstr "Hiba"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Example:"
|
||||
msgstr "Példa:"
|
||||
|
||||
#. placeholder {0}: alert.value
|
||||
#. placeholder {1}: info.unit
|
||||
#. placeholder {2}: alert.min
|
||||
@@ -727,6 +751,10 @@ msgstr "Hitelesítés sikertelen"
|
||||
msgid "Failed to save settings"
|
||||
msgstr "Nem sikerült menteni a beállításokat"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Failed to send heartbeat"
|
||||
msgstr "Nem sikerült elküldeni a szívverést (heartbeat)"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Failed to send test notification"
|
||||
msgstr "Teszt értesítés elküldése sikertelen"
|
||||
@@ -806,6 +834,18 @@ msgstr "Rács"
|
||||
msgid "Health"
|
||||
msgstr "Egészség"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Heartbeat"
|
||||
msgstr "Heartbeat"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat Monitoring"
|
||||
msgstr "Heartbeat figyelés"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat sent successfully"
|
||||
msgstr "Heartbeat sikeresen elküldve"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
@@ -816,6 +856,14 @@ msgstr "Homebrew parancs"
|
||||
msgid "Host / IP"
|
||||
msgstr "Állomás / IP"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP Method"
|
||||
msgstr "HTTP metódus"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP method: POST, GET, or HEAD (default: POST)"
|
||||
msgstr "HTTP metódus: POST, GET vagy HEAD (alapértelmezett: POST)"
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Idle"
|
||||
@@ -834,6 +882,10 @@ msgstr "Kép"
|
||||
msgid "Inactive"
|
||||
msgstr "Inaktív"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Interval"
|
||||
msgstr "Intervallum"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Invalid email address."
|
||||
msgstr "Érvénytelen e-mail cím."
|
||||
@@ -1110,6 +1162,10 @@ msgstr "Szüneteltetve"
|
||||
msgid "Paused ({pausedSystemsLength})"
|
||||
msgstr "Szüneteltetve ({pausedSystemsLength})"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Payload format"
|
||||
msgstr "Payload formátum"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Per-core average utilization"
|
||||
@@ -1307,6 +1363,10 @@ msgstr "Keresés"
|
||||
msgid "Search for systems or settings..."
|
||||
msgstr "Keresés rendszerek vagy beállítások után..."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Seconds between pings (default: 60)"
|
||||
msgstr "Pingek közötti másodpercek (alapértelmezett: 60)"
|
||||
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "Lásd <0>az értesítési beállításokat</0>, hogy konfigurálja, hogyan kap értesítéseket."
|
||||
@@ -1315,6 +1375,18 @@ msgstr "Lásd <0>az értesítési beállításokat</0>, hogy konfigurálja, hogy
|
||||
msgid "Select {foo}"
|
||||
msgstr "{foo} kiválasztása"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send a single heartbeat ping to verify your endpoint is working."
|
||||
msgstr "Küldjön egyetlen heartbeat pinget a végpont működésének ellenőrzéséhez."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send periodic outbound pings to an external monitoring service so you can monitor Beszel without exposing it to the internet."
|
||||
msgstr "Küldjön időszakos kimenő pingeket egy külső megfigyelő szolgáltatásnak, így a Beszel-t az internetnek való kitettség nélkül is megfigyelheti."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send test heartbeat"
|
||||
msgstr "Teszt heartbeat küldése"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "Elküldve"
|
||||
@@ -1335,6 +1407,10 @@ msgstr "Szolgáltatások"
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "Százalékos küszöbértékek beállítása a mérőszínekhez."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Set the following environment variables on your Beszel hub to enable heartbeat monitoring:"
|
||||
msgstr "Állítsa be a következő környezeti változókat a Beszel hubon a heartbeat figyelés engedélyezéséhez:"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
@@ -1452,10 +1528,18 @@ msgstr "A rendszer érzékelőinek hőmérséklete"
|
||||
msgid "Test <0>URL</0>"
|
||||
msgstr "Teszt <0>URL</0>"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Test heartbeat"
|
||||
msgstr "Teszt heartbeat"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Test notification sent"
|
||||
msgstr "Teszt értesítés elküldve"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "The overall status is <0>ok</0> when all systems are up, <1>warn</1> when alerts are triggered, and <2>error</2> when any system is down."
|
||||
msgstr "Az összesített állapot <0>ok</0>, ha minden rendszer fut, <1>warn</1>, ha riasztások léptek fel, és <2>error</2>, ha bármelyik rendszer leállt."
|
||||
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
msgid "Then log into the backend and reset your user account password in the users table."
|
||||
msgstr "Ezután jelentkezzen be a backendbe, és állítsa vissza a felhasználói fiók jelszavát a felhasználók táblázatban."
|
||||
@@ -1642,6 +1726,7 @@ msgid "Upload"
|
||||
msgstr "Feltöltés"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Uptime"
|
||||
msgstr "Üzemidő"
|
||||
|
||||
@@ -1716,6 +1801,10 @@ msgstr "Webhook / Push értesítések"
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation."
|
||||
msgstr "Ha engedélyezve van, ez a token lehetővé teszi az ügynökök számára a regisztrációt a rendszer előzetes létrehozása nélkül."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "When using POST, each heartbeat includes a JSON payload with system status summary, list of down systems, and triggered alerts."
|
||||
msgstr "POST használata esetén minden heartbeat tartalmaz egy JSON payload-ot a rendszerállapot összefoglalójával, a leállt rendszerek listájával és a kiváltott riasztásokkal."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
|
||||
@@ -93,6 +93,7 @@ msgstr "Aksi"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Active"
|
||||
msgstr "Aktif"
|
||||
@@ -140,6 +141,10 @@ msgstr "Admin"
|
||||
msgid "After"
|
||||
msgstr "Setelah"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "After setting the environment variables, restart your Beszel hub for changes to take effect."
|
||||
msgstr "Setelah mengatur variabel lingkungan, restart hub Beszel Anda agar perubahan dapat diterapkan."
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Agen"
|
||||
@@ -350,6 +355,10 @@ msgstr "Periksa {email} untuk tautan atur ulang password."
|
||||
msgid "Check logs for more details."
|
||||
msgstr "Periksa riwayat untuk lebih detail."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Check your monitoring service"
|
||||
msgstr "Periksa layanan pemantauan Anda"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Check your notification service"
|
||||
msgstr "Periksa jasa penyedia notifikasi anda"
|
||||
@@ -457,7 +466,7 @@ msgstr "Salin YAML"
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "CPU"
|
||||
msgstr ""
|
||||
msgstr "CPU"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "CPU Cores"
|
||||
@@ -543,7 +552,7 @@ msgstr "Deskripsi"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
msgid "Detail"
|
||||
msgstr ""
|
||||
msgstr "Detail"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Device"
|
||||
@@ -556,11 +565,11 @@ msgstr "Sedang tidak di charge"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Disk"
|
||||
msgstr ""
|
||||
msgstr "Disk"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Disk I/O"
|
||||
msgstr ""
|
||||
msgstr "Disk I/O"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Disk unit"
|
||||
@@ -586,7 +595,7 @@ msgstr "Penggunaan Memori Docker"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Docker Network I/O"
|
||||
msgstr ""
|
||||
msgstr "Docker Network I/O"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
@@ -607,7 +616,7 @@ msgstr "Mati ({downSystemsLength})"
|
||||
|
||||
#: src/components/routes/system/network-sheet.tsx
|
||||
msgid "Download"
|
||||
msgstr ""
|
||||
msgstr "Unduh"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Duration"
|
||||
@@ -627,7 +636,7 @@ msgstr "Ubah {foo}"
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
#: src/components/login/otp-forms.tsx
|
||||
msgid "Email"
|
||||
msgstr ""
|
||||
msgstr "Email"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Email notifications"
|
||||
@@ -643,6 +652,14 @@ msgstr "Kosong"
|
||||
msgid "End Time"
|
||||
msgstr "Waktu Berakhir"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL"
|
||||
msgstr "URL Titik Akhir"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL to ping (required)"
|
||||
msgstr "URL titik akhir untuk di-ping (diperlukan)"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Enter email address to reset password"
|
||||
msgstr "Masukkan alamat email untuk mereset kata sandi"
|
||||
@@ -662,13 +679,20 @@ msgstr "Sementara"
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Error"
|
||||
msgstr ""
|
||||
msgstr "Error"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Example:"
|
||||
msgstr "Contoh:"
|
||||
|
||||
#. placeholder {0}: alert.value
|
||||
#. placeholder {1}: info.unit
|
||||
@@ -707,7 +731,7 @@ msgstr "Export konfigurasi sistem anda saat ini."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Fahrenheit (°F)"
|
||||
msgstr ""
|
||||
msgstr "Fahrenheit (°F)"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Failed"
|
||||
@@ -727,6 +751,10 @@ msgstr "Gagal mengautentikasi"
|
||||
msgid "Failed to save settings"
|
||||
msgstr "Gagal menyimpan pengaturan"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Failed to send heartbeat"
|
||||
msgstr "Gagal mengirim heartbeat"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Failed to send test notification"
|
||||
msgstr "Gagal mengirim tes notifikasi"
|
||||
@@ -747,7 +775,7 @@ msgstr "Gagal: {0}"
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Filter..."
|
||||
msgstr ""
|
||||
msgstr "Saring..."
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Fingerprint"
|
||||
@@ -755,7 +783,7 @@ msgstr "Sidik jari"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Firmware"
|
||||
msgstr ""
|
||||
msgstr "Firmware"
|
||||
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "For <0>{min}</0> {min, plural, one {minute} other {minutes}}"
|
||||
@@ -784,7 +812,7 @@ msgstr "Umum"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Global"
|
||||
msgstr ""
|
||||
msgstr "Global"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "GPU Engines"
|
||||
@@ -806,6 +834,18 @@ msgstr "Kartu"
|
||||
msgid "Health"
|
||||
msgstr "Kesehatan"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Heartbeat"
|
||||
msgstr "Heartbeat"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat Monitoring"
|
||||
msgstr "Pemantauan Heartbeat"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat sent successfully"
|
||||
msgstr "Heartbeat berhasil dikirim"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
@@ -814,12 +854,20 @@ msgstr "Perintah Homebrew"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Host / IP"
|
||||
msgstr ""
|
||||
msgstr "Host / IP"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP Method"
|
||||
msgstr "Metode HTTP"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP method: POST, GET, or HEAD (default: POST)"
|
||||
msgstr "Metode HTTP: POST, GET, atau HEAD (default: POST)"
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Idle"
|
||||
msgstr ""
|
||||
msgstr "Idle"
|
||||
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
msgid "If you've lost the password to your admin account, you may reset it using the following command."
|
||||
@@ -828,12 +876,16 @@ msgstr "Jika anda kehilangan kata sandi untuk akun admin anda, anda dapat merese
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
msgctxt "Docker image"
|
||||
msgid "Image"
|
||||
msgstr ""
|
||||
msgstr "Image"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Inactive"
|
||||
msgstr "Tidak aktif"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Interval"
|
||||
msgstr "Interval"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Invalid email address."
|
||||
msgstr "Alamat email tidak valid."
|
||||
@@ -958,7 +1010,7 @@ msgstr "Penggunaan memori kontainer docker"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Model"
|
||||
msgstr ""
|
||||
msgstr "Model"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
@@ -1110,6 +1162,10 @@ msgstr "Dijeda"
|
||||
msgid "Paused ({pausedSystemsLength})"
|
||||
msgstr "Dijeda ({pausedSystemsLength})"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Payload format"
|
||||
msgstr "Format payload"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Per-core average utilization"
|
||||
@@ -1162,7 +1218,7 @@ msgstr "Silakan masuk ke akun anda"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Port"
|
||||
msgstr ""
|
||||
msgstr "Port"
|
||||
|
||||
#. Power On Time
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
@@ -1248,7 +1304,7 @@ msgstr "Lanjutkan"
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgctxt "Root disk label"
|
||||
msgid "Root"
|
||||
msgstr ""
|
||||
msgstr "Root"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Rotate token"
|
||||
@@ -1307,6 +1363,10 @@ msgstr "Cari"
|
||||
msgid "Search for systems or settings..."
|
||||
msgstr "Cari sistem atau pengaturan..."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Seconds between pings (default: 60)"
|
||||
msgstr "Detik di antara ping (default: 60)"
|
||||
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "Lihat <0>pengaturan notifikasi</0> untuk mengkonfigurasi bagaimana anda menerima peringatan."
|
||||
@@ -1315,6 +1375,18 @@ msgstr "Lihat <0>pengaturan notifikasi</0> untuk mengkonfigurasi bagaimana anda
|
||||
msgid "Select {foo}"
|
||||
msgstr "Pilih {foo}"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send a single heartbeat ping to verify your endpoint is working."
|
||||
msgstr "Kirim satu ping heartbeat untuk memverifikasi titik akhir Anda berfungsi."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send periodic outbound pings to an external monitoring service so you can monitor Beszel without exposing it to the internet."
|
||||
msgstr "Kirim ping keluar secara berkala ke layanan pemantauan eksternal sehingga Anda dapat memantau Beszel tanpa mengeksposnya ke internet."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send test heartbeat"
|
||||
msgstr "Kirim tes heartbeat"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "Dikirim"
|
||||
@@ -1335,6 +1407,10 @@ msgstr "Layanan"
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "Tetapkan ambang persentase untuk warna meter."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Set the following environment variables on your Beszel hub to enable heartbeat monitoring:"
|
||||
msgstr "Setel variabel lingkungan berikut di hub Beszel Anda untuk mengaktifkan pemantauan heartbeat:"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
@@ -1377,7 +1453,7 @@ msgstr "Status"
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Status"
|
||||
msgstr ""
|
||||
msgstr "Status"
|
||||
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Sub State"
|
||||
@@ -1452,10 +1528,18 @@ msgstr "Temperatur sensor sistem"
|
||||
msgid "Test <0>URL</0>"
|
||||
msgstr "Tes <0>URL</0>"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Test heartbeat"
|
||||
msgstr "Tes heartbeat"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Test notification sent"
|
||||
msgstr "Notifikasi tes dikirim"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "The overall status is <0>ok</0> when all systems are up, <1>warn</1> when alerts are triggered, and <2>error</2> when any system is down."
|
||||
msgstr "Status keseluruhan adalah <0>ok</0> ketika semua sistem aktif, <1>warn</1> ketika peringatan dipicu, dan <2>error</2> ketika ada sistem yang mati."
|
||||
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
msgid "Then log into the backend and reset your user account password in the users table."
|
||||
msgstr "Kemudian masuk ke backend dan reset kata sandi akun pengguna anda di tabel pengguna."
|
||||
@@ -1497,7 +1581,7 @@ msgstr "Ganti tema"
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Token"
|
||||
msgstr ""
|
||||
msgstr "Token"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
@@ -1516,7 +1600,7 @@ msgstr "Token dan Fingerprint digunakan untuk mengautentikasi koneksi WebSocket
|
||||
#: src/components/ui/chart.tsx
|
||||
#: src/components/ui/chart.tsx
|
||||
msgid "Total"
|
||||
msgstr ""
|
||||
msgstr "Total"
|
||||
|
||||
#: src/components/routes/system/network-sheet.tsx
|
||||
msgid "Total data received for each interface"
|
||||
@@ -1529,7 +1613,7 @@ msgstr "Total data yang dikirim untuk setiap antarmuka"
|
||||
#. placeholder {0}: data.length
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Total: {0}"
|
||||
msgstr ""
|
||||
msgstr "Total: {0}"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Triggered by"
|
||||
@@ -1639,9 +1723,10 @@ msgstr "Diperbarui setiap 10 menit."
|
||||
|
||||
#: src/components/routes/system/network-sheet.tsx
|
||||
msgid "Upload"
|
||||
msgstr ""
|
||||
msgstr "Unggah"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Uptime"
|
||||
msgstr "Waktu aktif"
|
||||
|
||||
@@ -1716,6 +1801,10 @@ msgstr "Webhook / Push notifikasi"
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation."
|
||||
msgstr "Ketika diaktifkan, token ini memungkinkan agen untuk mendaftar sendiri tanpa pembuatan sistem."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "When using POST, each heartbeat includes a JSON payload with system status summary, list of down systems, and triggered alerts."
|
||||
msgstr "Saat menggunakan POST, setiap heartbeat menyertakan payload JSON dengan ringkasan status sistem, daftar sistem yang mati, dan peringatan yang dipicu."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
|
||||
@@ -93,6 +93,7 @@ msgstr "Azioni"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Active"
|
||||
msgstr "Attivo"
|
||||
@@ -140,6 +141,10 @@ msgstr "Amministratore"
|
||||
msgid "After"
|
||||
msgstr "Dopo"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "After setting the environment variables, restart your Beszel hub for changes to take effect."
|
||||
msgstr "Dopo aver impostato le variabili d'ambiente, riavvia il tuo Beszel hub affinché le modifiche abbiano effetto."
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Agente"
|
||||
@@ -231,7 +236,7 @@ msgstr "Larghezza di banda"
|
||||
#. Battery label in systems table header
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Bat"
|
||||
msgstr ""
|
||||
msgstr "Bat"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/lib/alerts.ts
|
||||
@@ -350,6 +355,10 @@ msgstr "Controlla {email} per un link di reset."
|
||||
msgid "Check logs for more details."
|
||||
msgstr "Controlla i log per maggiori dettagli."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Check your monitoring service"
|
||||
msgstr "Controlla il tuo servizio di monitoraggio"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Check your notification service"
|
||||
msgstr "Controlla il tuo servizio di notifica"
|
||||
@@ -643,6 +652,14 @@ msgstr "Vuota"
|
||||
msgid "End Time"
|
||||
msgstr "Ora di fine"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL"
|
||||
msgstr "URL dell'endpoint"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL to ping (required)"
|
||||
msgstr "URL dell'endpoint da pingare (richiesto)"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Enter email address to reset password"
|
||||
msgstr "Inserisci l'indirizzo email per reimpostare la password"
|
||||
@@ -662,6 +679,9 @@ msgstr "Effimero"
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
@@ -670,6 +690,10 @@ msgstr "Effimero"
|
||||
msgid "Error"
|
||||
msgstr "Errore"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Example:"
|
||||
msgstr "Esempio:"
|
||||
|
||||
#. placeholder {0}: alert.value
|
||||
#. placeholder {1}: info.unit
|
||||
#. placeholder {2}: alert.min
|
||||
@@ -727,6 +751,10 @@ msgstr "Autenticazione fallita"
|
||||
msgid "Failed to save settings"
|
||||
msgstr "Salvataggio delle impostazioni fallito"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Failed to send heartbeat"
|
||||
msgstr "Invio heartbeat fallito"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Failed to send test notification"
|
||||
msgstr "Invio della notifica di test fallito"
|
||||
@@ -806,6 +834,18 @@ msgstr "Griglia"
|
||||
msgid "Health"
|
||||
msgstr "Stato"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Heartbeat"
|
||||
msgstr "Heartbeat"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat Monitoring"
|
||||
msgstr "Monitoraggio Heartbeat"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat sent successfully"
|
||||
msgstr "Heartbeat inviato con successo"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
@@ -816,6 +856,14 @@ msgstr "Comando Homebrew"
|
||||
msgid "Host / IP"
|
||||
msgstr "Host / IP"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP Method"
|
||||
msgstr "Metodo HTTP"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP method: POST, GET, or HEAD (default: POST)"
|
||||
msgstr "Metodo HTTP: POST, GET o HEAD (predefinito: POST)"
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Idle"
|
||||
@@ -834,6 +882,10 @@ msgstr "Immagine"
|
||||
msgid "Inactive"
|
||||
msgstr "Inattivo"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Interval"
|
||||
msgstr "Intervallo"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Invalid email address."
|
||||
msgstr "Indirizzo email non valido."
|
||||
@@ -993,7 +1045,7 @@ msgstr "Unità rete"
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "No"
|
||||
msgstr ""
|
||||
msgstr "No"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
@@ -1110,6 +1162,10 @@ msgstr "In pausa"
|
||||
msgid "Paused ({pausedSystemsLength})"
|
||||
msgstr "In pausa ({pausedSystemsLength})"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Payload format"
|
||||
msgstr "Formato del payload"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Per-core average utilization"
|
||||
@@ -1307,6 +1363,10 @@ msgstr "Cerca"
|
||||
msgid "Search for systems or settings..."
|
||||
msgstr "Cerca sistemi o impostazioni..."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Seconds between pings (default: 60)"
|
||||
msgstr "Secondi tra i ping (predefinito: 60)"
|
||||
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "Vedi <0>impostazioni di notifica</0> per configurare come ricevere gli avvisi."
|
||||
@@ -1315,6 +1375,18 @@ msgstr "Vedi <0>impostazioni di notifica</0> per configurare come ricevere gli a
|
||||
msgid "Select {foo}"
|
||||
msgstr "Seleziona {foo}"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send a single heartbeat ping to verify your endpoint is working."
|
||||
msgstr "Invia un singolo ping di heartbeat per verificare che l'endpoint funzioni."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send periodic outbound pings to an external monitoring service so you can monitor Beszel without exposing it to the internet."
|
||||
msgstr "Invia ping in uscita periodici a un servizio di monitoraggio esterno in modo da poter monitorare Beszel senza esporlo a Internet."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send test heartbeat"
|
||||
msgstr "Invia heartbeat di prova"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "Inviato"
|
||||
@@ -1335,6 +1407,10 @@ msgstr "Servizi"
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "Imposta le soglie percentuali per i colori dei contatori."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Set the following environment variables on your Beszel hub to enable heartbeat monitoring:"
|
||||
msgstr "Imposta le seguenti variabili d'ambiente sul tuo Beszel hub per abilitare il monitoraggio heartbeat:"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
@@ -1452,10 +1528,18 @@ msgstr "Temperature dei sensori di sistema"
|
||||
msgid "Test <0>URL</0>"
|
||||
msgstr "Test <0>URL</0>"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Test heartbeat"
|
||||
msgstr "Test heartbeat"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Test notification sent"
|
||||
msgstr "Notifica di test inviata"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "The overall status is <0>ok</0> when all systems are up, <1>warn</1> when alerts are triggered, and <2>error</2> when any system is down."
|
||||
msgstr "Lo stato generale è <0>ok</0> quando tutti i sistemi sono attivi, <1>avviso</1> quando gli avvisi sono attivati e <2>errore</2> quando un sistema è inattivo."
|
||||
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
msgid "Then log into the backend and reset your user account password in the users table."
|
||||
msgstr "Quindi accedi al backend e reimposta la password del tuo account utente nella tabella degli utenti."
|
||||
@@ -1642,8 +1726,9 @@ msgid "Upload"
|
||||
msgstr "Carica"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Uptime"
|
||||
msgstr "Tempo di attività"
|
||||
msgstr "Uptime"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
@@ -1716,6 +1801,10 @@ msgstr "Notifiche Webhook / Push"
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation."
|
||||
msgstr "Quando abilitato, questo token consente agli agenti di registrarsi automaticamente senza creazione preventiva del sistema."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "When using POST, each heartbeat includes a JSON payload with system status summary, list of down systems, and triggered alerts."
|
||||
msgstr "Quando si usa POST, ogni heartbeat include un payload JSON con il riepilogo dello stato del sistema, l'elenco dei sistemi inattivi e gli avvisi attivati."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
|
||||
@@ -93,6 +93,7 @@ msgstr "アクション"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Active"
|
||||
msgstr "アクティブ"
|
||||
@@ -140,6 +141,10 @@ msgstr "管理者"
|
||||
msgid "After"
|
||||
msgstr "後"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "After setting the environment variables, restart your Beszel hub for changes to take effect."
|
||||
msgstr "環境変数を設定した後、変更を有効にするために Beszel ハブを再起動してください。"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "エージェント"
|
||||
@@ -350,6 +355,10 @@ msgstr "{email}を確認してリセットリンクを探してください。"
|
||||
msgid "Check logs for more details."
|
||||
msgstr "詳細についてはログを確認してください。"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Check your monitoring service"
|
||||
msgstr "監視サービスを確認する"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Check your notification service"
|
||||
msgstr "通知サービスを確認してください"
|
||||
@@ -457,7 +466,7 @@ msgstr "YAMLをコピー"
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "CPU"
|
||||
msgstr ""
|
||||
msgstr "CPU"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "CPU Cores"
|
||||
@@ -643,6 +652,14 @@ msgstr "空"
|
||||
msgid "End Time"
|
||||
msgstr "終了時間"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL"
|
||||
msgstr "エンドポイント URL"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL to ping (required)"
|
||||
msgstr "ping するエンドポイント URL (必須)"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Enter email address to reset password"
|
||||
msgstr "パスワードをリセットするためにメールアドレスを入力してください"
|
||||
@@ -662,6 +679,9 @@ msgstr "一時的"
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
@@ -670,6 +690,10 @@ msgstr "一時的"
|
||||
msgid "Error"
|
||||
msgstr "エラー"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Example:"
|
||||
msgstr "例:"
|
||||
|
||||
#. placeholder {0}: alert.value
|
||||
#. placeholder {1}: info.unit
|
||||
#. placeholder {2}: alert.min
|
||||
@@ -727,6 +751,10 @@ msgstr "認証に失敗しました"
|
||||
msgid "Failed to save settings"
|
||||
msgstr "設定の保存に失敗しました"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Failed to send heartbeat"
|
||||
msgstr "ハートビートの送信に失敗しました"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Failed to send test notification"
|
||||
msgstr "テスト通知の送信に失敗しました"
|
||||
@@ -806,6 +834,18 @@ msgstr "グリッド"
|
||||
msgid "Health"
|
||||
msgstr "ヘルス"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Heartbeat"
|
||||
msgstr "ハートビート"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat Monitoring"
|
||||
msgstr "ハートビート監視"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat sent successfully"
|
||||
msgstr "ハートビートが正常に送信されました"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
@@ -816,6 +856,14 @@ msgstr "Homebrew コマンド"
|
||||
msgid "Host / IP"
|
||||
msgstr "ホスト / IP"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP Method"
|
||||
msgstr "HTTP メソッド"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP method: POST, GET, or HEAD (default: POST)"
|
||||
msgstr "HTTP メソッド: POST、GET、または HEAD (デフォルト: POST)"
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Idle"
|
||||
@@ -834,6 +882,10 @@ msgstr "イメージ"
|
||||
msgid "Inactive"
|
||||
msgstr "非アクティブ"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Interval"
|
||||
msgstr "間隔"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Invalid email address."
|
||||
msgstr "無効なメールアドレスです。"
|
||||
@@ -1110,6 +1162,10 @@ msgstr "一時停止中"
|
||||
msgid "Paused ({pausedSystemsLength})"
|
||||
msgstr "一時停止 ({pausedSystemsLength})"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Payload format"
|
||||
msgstr "ペイロード形式"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Per-core average utilization"
|
||||
@@ -1307,6 +1363,10 @@ msgstr "検索"
|
||||
msgid "Search for systems or settings..."
|
||||
msgstr "システムまたは設定を検索..."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Seconds between pings (default: 60)"
|
||||
msgstr "ping 間の秒数 (デフォルト: 60)"
|
||||
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "アラートの受信方法を設定するには<0>通知設定</0>を参照してください。"
|
||||
@@ -1315,6 +1375,18 @@ msgstr "アラートの受信方法を設定するには<0>通知設定</0>を
|
||||
msgid "Select {foo}"
|
||||
msgstr "{foo}を選択"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send a single heartbeat ping to verify your endpoint is working."
|
||||
msgstr "エンドポイントが機能していることを確認するために、単一のハートビート ping を送信します。"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send periodic outbound pings to an external monitoring service so you can monitor Beszel without exposing it to the internet."
|
||||
msgstr "外部監視サービスに定期的にアウトバウンド ping を送信することで、Beszel をインターネットに公開せずに監視できます。"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send test heartbeat"
|
||||
msgstr "テストハートビートを送信"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "送信"
|
||||
@@ -1335,6 +1407,10 @@ msgstr "サービス"
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "メーターの色を変更するしきい値(パーセンテージ)を設定します。"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Set the following environment variables on your Beszel hub to enable heartbeat monitoring:"
|
||||
msgstr "ハートビート監視を有効にするには、Beszel ハブで次の環境変数を設定します。"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
@@ -1452,10 +1528,18 @@ msgstr "システムセンサーの温度"
|
||||
msgid "Test <0>URL</0>"
|
||||
msgstr "テスト<0>URL</0>"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Test heartbeat"
|
||||
msgstr "テストハートビート"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Test notification sent"
|
||||
msgstr "テスト通知が送信されました"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "The overall status is <0>ok</0> when all systems are up, <1>warn</1> when alerts are triggered, and <2>error</2> when any system is down."
|
||||
msgstr "全体的なステータスは、すべてのシステムが稼働している場合は <0>ok</0>、アラートがトリガーされた場合は <1>警告</1>、いずれかのシステムがダウンしている場合は <2>エラー</2> になります。"
|
||||
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
msgid "Then log into the backend and reset your user account password in the users table."
|
||||
msgstr "その後、バックエンドにログインして、ユーザーテーブルでユーザーアカウントのパスワードをリセットしてください。"
|
||||
@@ -1642,6 +1726,7 @@ msgid "Upload"
|
||||
msgstr "アップロード"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Uptime"
|
||||
msgstr "稼働時間"
|
||||
|
||||
@@ -1716,6 +1801,10 @@ msgstr "Webhook / プッシュ通知"
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation."
|
||||
msgstr "有効にすると、このトークンによりエージェントは事前のシステム作成なしで自己登録できます。"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "When using POST, each heartbeat includes a JSON payload with system status summary, list of down systems, and triggered alerts."
|
||||
msgstr "POST を使用する場合、各ハートビートには、システムステータスの概要、ダウンしているシステムのリスト、およびトリガーされたアラートを含む JSON ペイロードが含まれます。"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
|
||||
@@ -93,6 +93,7 @@ msgstr "작업"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Active"
|
||||
msgstr "활성"
|
||||
@@ -140,6 +141,10 @@ msgstr "관리자"
|
||||
msgid "After"
|
||||
msgstr "이후"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "After setting the environment variables, restart your Beszel hub for changes to take effect."
|
||||
msgstr "환경 변수를 설정한 후, 변경 사항을 적용하려면 Beszel 허브를 재시작하세요."
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "에이전트"
|
||||
@@ -350,6 +355,10 @@ msgstr "{email}에서 재설정 링크를 확인하세요."
|
||||
msgid "Check logs for more details."
|
||||
msgstr "자세한 내용은 로그를 확인하세요."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Check your monitoring service"
|
||||
msgstr "모니터링 서비스 확인"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Check your notification service"
|
||||
msgstr "알림 서비스를 확인하세요."
|
||||
@@ -643,6 +652,14 @@ msgstr "빔"
|
||||
msgid "End Time"
|
||||
msgstr "종료 시간"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL"
|
||||
msgstr "엔드포인트 URL"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL to ping (required)"
|
||||
msgstr "핑을 보낼 엔드포인트 URL (필수)"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Enter email address to reset password"
|
||||
msgstr "비밀번호를 재설정하려면 이메일 주소를 입력하세요"
|
||||
@@ -662,6 +679,9 @@ msgstr "일시적"
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
@@ -670,6 +690,10 @@ msgstr "일시적"
|
||||
msgid "Error"
|
||||
msgstr "오류"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Example:"
|
||||
msgstr "예시:"
|
||||
|
||||
#. placeholder {0}: alert.value
|
||||
#. placeholder {1}: info.unit
|
||||
#. placeholder {2}: alert.min
|
||||
@@ -679,7 +703,7 @@ msgstr "마지막 {2, plural, one {# 분} other {# 분}} 동안 {0}{1} 초과"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Exec main PID"
|
||||
msgstr ""
|
||||
msgstr "실행 메인 PID"
|
||||
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
|
||||
@@ -727,6 +751,10 @@ msgstr "인증 실패"
|
||||
msgid "Failed to save settings"
|
||||
msgstr "설정 저장 실패"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Failed to send heartbeat"
|
||||
msgstr "하트비트 전송 실패"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Failed to send test notification"
|
||||
msgstr "테스트 알림 전송 실패"
|
||||
@@ -806,6 +834,18 @@ msgstr "그리드"
|
||||
msgid "Health"
|
||||
msgstr "상태"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Heartbeat"
|
||||
msgstr "하트비트"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat Monitoring"
|
||||
msgstr "하트비트 모니터링"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat sent successfully"
|
||||
msgstr "하트비트 전송 성공"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
@@ -816,6 +856,14 @@ msgstr "Homebrew 명령어"
|
||||
msgid "Host / IP"
|
||||
msgstr "호스트 / IP"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP Method"
|
||||
msgstr "HTTP 메서드"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP method: POST, GET, or HEAD (default: POST)"
|
||||
msgstr "HTTP 메서드: POST, GET 또는 HEAD (기본값: POST)"
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Idle"
|
||||
@@ -834,6 +882,10 @@ msgstr "이미지"
|
||||
msgid "Inactive"
|
||||
msgstr "비활성"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Interval"
|
||||
msgstr "간격"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Invalid email address."
|
||||
msgstr "잘못된 이메일 주소입니다."
|
||||
@@ -858,7 +910,7 @@ msgstr "생명주기"
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "limit"
|
||||
msgstr ""
|
||||
msgstr "제한"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Load Average"
|
||||
@@ -914,7 +966,7 @@ msgstr "알림을 생성하려 하시나요? 시스템 테이블의 종 <0/> 아
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Main PID"
|
||||
msgstr ""
|
||||
msgstr "메인 PID"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Manage display and notification preferences."
|
||||
@@ -1110,6 +1162,10 @@ msgstr "일시 정지됨"
|
||||
msgid "Paused ({pausedSystemsLength})"
|
||||
msgstr "일시 정지됨 ({pausedSystemsLength})"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Payload format"
|
||||
msgstr "페이로드 형식"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Per-core average utilization"
|
||||
@@ -1248,7 +1304,7 @@ msgstr "재개"
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgctxt "Root disk label"
|
||||
msgid "Root"
|
||||
msgstr ""
|
||||
msgstr "루트"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Rotate token"
|
||||
@@ -1307,6 +1363,10 @@ msgstr "검색"
|
||||
msgid "Search for systems or settings..."
|
||||
msgstr "시스템 또는 설정 검색..."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Seconds between pings (default: 60)"
|
||||
msgstr "핑 사이 시간(초) (기본값: 60)"
|
||||
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "알림을 받는 방법을 구성하려면 <0>알림 설정</0>을 참조하세요."
|
||||
@@ -1315,6 +1375,18 @@ msgstr "알림을 받는 방법을 구성하려면 <0>알림 설정</0>을 참
|
||||
msgid "Select {foo}"
|
||||
msgstr "{foo} 선택"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send a single heartbeat ping to verify your endpoint is working."
|
||||
msgstr "엔드포인트가 작동하는지 확인하기 위해 단일 하트비트 핑을 보냅니다."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send periodic outbound pings to an external monitoring service so you can monitor Beszel without exposing it to the internet."
|
||||
msgstr "외부 모니터링 서비스에 주기적으로 아웃바운드 핑을 보내 인터넷에 노출하지 않고도 Beszel을 모니터링할 수 있습니다."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send test heartbeat"
|
||||
msgstr "테스트 하트비트 전송"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "보냄"
|
||||
@@ -1335,6 +1407,10 @@ msgstr "서비스"
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "그래프 미터 색상의 백분율 임계값을 설정합니다."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Set the following environment variables on your Beszel hub to enable heartbeat monitoring:"
|
||||
msgstr "하트비트 모니터링을 활성화하려면 Beszel 허브에 다음 환경 변수를 설정하세요:"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
@@ -1452,10 +1528,18 @@ msgstr "시스템 센서의 온도"
|
||||
msgid "Test <0>URL</0>"
|
||||
msgstr "테스트 <0>URL</0>"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Test heartbeat"
|
||||
msgstr "테스트 하트비트"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Test notification sent"
|
||||
msgstr "테스트 알림이 전송되었습니다."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "The overall status is <0>ok</0> when all systems are up, <1>warn</1> when alerts are triggered, and <2>error</2> when any system is down."
|
||||
msgstr "모든 시스템이 정상이면 <0>ok</0>, 알림이 트리거되면 <1>경고</1>, 시스템이 다운되면 <2>오류</2> 상태가 됩니다."
|
||||
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
msgid "Then log into the backend and reset your user account password in the users table."
|
||||
msgstr "그런 다음 백엔드에 로그인하여 사용자 테이블에서 사용자 계정 비밀번호를 재설정하세요."
|
||||
@@ -1591,7 +1675,7 @@ msgstr "유형"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Unit file"
|
||||
msgstr ""
|
||||
msgstr "유닛 파일"
|
||||
|
||||
#. Temperature / network units
|
||||
#: src/components/routes/settings/general.tsx
|
||||
@@ -1642,8 +1726,9 @@ msgid "Upload"
|
||||
msgstr "업로드"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Uptime"
|
||||
msgstr "가동 시간"
|
||||
msgstr "가동시간"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
@@ -1716,6 +1801,10 @@ msgstr "Webhook / 푸시 알림"
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation."
|
||||
msgstr "활성화되면 이 토큰은 사전 시스템 생성 없이 에이전트가 자체 등록할 수 있도록 합니다."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "When using POST, each heartbeat includes a JSON payload with system status summary, list of down systems, and triggered alerts."
|
||||
msgstr "POST를 사용할 때 각 하트비트에는 시스템 상태 요약, 다운된 시스템 목록 및 트리거된 알림이 포함된 JSON 페이로드가 포함됩니다."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: nl\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-01-31 21:16\n"
|
||||
"PO-Revision-Date: 2026-02-19 19:40\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Dutch\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -59,7 +59,7 @@ msgstr "1 minuut"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 week"
|
||||
msgstr "1 week"
|
||||
msgstr "1 Maand"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "12 hours"
|
||||
@@ -93,6 +93,7 @@ msgstr "Acties"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Active"
|
||||
msgstr "Actief"
|
||||
@@ -140,6 +141,10 @@ msgstr "Administrator"
|
||||
msgid "After"
|
||||
msgstr "Na"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "After setting the environment variables, restart your Beszel hub for changes to take effect."
|
||||
msgstr "Start na het instellen van de omgevingsvariabelen je Beszel-hub opnieuw op om de wijzigingen door te voeren."
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Agent"
|
||||
@@ -350,6 +355,10 @@ msgstr "Controleer {email} op een reset link."
|
||||
msgid "Check logs for more details."
|
||||
msgstr "Controleer de logs voor meer details."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Check your monitoring service"
|
||||
msgstr "Controleer je monitoringservice"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Check your notification service"
|
||||
msgstr "Controleer je meldingsservice"
|
||||
@@ -643,6 +652,14 @@ msgstr "Leeg"
|
||||
msgid "End Time"
|
||||
msgstr "Eindtijd"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL"
|
||||
msgstr "Endpoint-URL"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL to ping (required)"
|
||||
msgstr "Endpoint-URL om te pingen (vereist)"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Enter email address to reset password"
|
||||
msgstr "Voer een e-mailadres in om het wachtwoord opnieuw in te stellen"
|
||||
@@ -662,6 +679,9 @@ msgstr "Tijdelijk"
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
@@ -670,6 +690,10 @@ msgstr "Tijdelijk"
|
||||
msgid "Error"
|
||||
msgstr "Fout"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Example:"
|
||||
msgstr "Voorbeeld:"
|
||||
|
||||
#. placeholder {0}: alert.value
|
||||
#. placeholder {1}: info.unit
|
||||
#. placeholder {2}: alert.min
|
||||
@@ -727,6 +751,10 @@ msgstr "Authenticatie mislukt"
|
||||
msgid "Failed to save settings"
|
||||
msgstr "Instellingen opslaan mislukt"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Failed to send heartbeat"
|
||||
msgstr "Verzenden van heartbeat mislukt"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Failed to send test notification"
|
||||
msgstr "Versturen test notificatie mislukt"
|
||||
@@ -806,6 +834,18 @@ msgstr "Raster"
|
||||
msgid "Health"
|
||||
msgstr "Gezondheid"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Heartbeat"
|
||||
msgstr "Heartbeat"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat Monitoring"
|
||||
msgstr "Heartbeat-monitoring"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat sent successfully"
|
||||
msgstr "Heartbeat succesvol verzonden"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
@@ -816,6 +856,14 @@ msgstr "Homebrew-commando"
|
||||
msgid "Host / IP"
|
||||
msgstr "Host / IP-adres"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP Method"
|
||||
msgstr "HTTP-methode"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP method: POST, GET, or HEAD (default: POST)"
|
||||
msgstr "HTTP-methode: POST, GET of HEAD (standaard: POST)"
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Idle"
|
||||
@@ -834,6 +882,10 @@ msgstr "Afbeelding"
|
||||
msgid "Inactive"
|
||||
msgstr "Inactief"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Interval"
|
||||
msgstr "Interval"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Invalid email address."
|
||||
msgstr "Ongeldig e-mailadres."
|
||||
@@ -1110,6 +1162,10 @@ msgstr "Gepauzeerd"
|
||||
msgid "Paused ({pausedSystemsLength})"
|
||||
msgstr "Gepauzeerd ({pausedSystemsLength})"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Payload format"
|
||||
msgstr "Payload-indeling"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Per-core average utilization"
|
||||
@@ -1307,6 +1363,10 @@ msgstr "Zoeken"
|
||||
msgid "Search for systems or settings..."
|
||||
msgstr "Zoek naar systemen of instellingen..."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Seconds between pings (default: 60)"
|
||||
msgstr "Seconden tussen pings (standaard: 60)"
|
||||
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "Zie <0>notificatie-instellingen</0> om te configureren hoe je meldingen ontvangt."
|
||||
@@ -1315,6 +1375,18 @@ msgstr "Zie <0>notificatie-instellingen</0> om te configureren hoe je meldingen
|
||||
msgid "Select {foo}"
|
||||
msgstr "Selecteer {foo}"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send a single heartbeat ping to verify your endpoint is working."
|
||||
msgstr "Stuur een enkele heartbeat-ping om te controleren of je endpoint werkt."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send periodic outbound pings to an external monitoring service so you can monitor Beszel without exposing it to the internet."
|
||||
msgstr "Stuur periodieke uitgaande pings naar een externe monitoringservice, zodat je Beszel kunt monitoren zonder het aan het internet bloot te stellen."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send test heartbeat"
|
||||
msgstr "Stuur test-heartbeat"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "Verzonden"
|
||||
@@ -1335,6 +1407,10 @@ msgstr "Services"
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "Stel percentagedrempels in voor meterkleuren."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Set the following environment variables on your Beszel hub to enable heartbeat monitoring:"
|
||||
msgstr "Stel de volgende omgevingsvariabelen in op je Beszel-hub om heartbeat-monitoring in te schakelen:"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
@@ -1452,10 +1528,18 @@ msgstr "Temperatuur van systeem sensoren"
|
||||
msgid "Test <0>URL</0>"
|
||||
msgstr "Test <0>URL</0>"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Test heartbeat"
|
||||
msgstr "Test-heartbeat"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Test notification sent"
|
||||
msgstr "Testmelding verzonden"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "The overall status is <0>ok</0> when all systems are up, <1>warn</1> when alerts are triggered, and <2>error</2> when any system is down."
|
||||
msgstr "De algehele status is <0>ok</0> wanneer alle systemen in de lucht zijn, <1>waarschuwing</1> wanneer meldingen zijn geactiveerd, en <2>fout</2> wanneer een systeem plat ligt."
|
||||
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
msgid "Then log into the backend and reset your user account password in the users table."
|
||||
msgstr "Log vervolgens in op de backend en reset het wachtwoord van je gebruikersaccount in het gebruikersoverzicht."
|
||||
@@ -1642,6 +1726,7 @@ msgid "Upload"
|
||||
msgstr "Uploaden"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Uptime"
|
||||
msgstr "Actief"
|
||||
|
||||
@@ -1716,6 +1801,10 @@ msgstr "Webhook / Pushmeldingen"
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation."
|
||||
msgstr "Indien ingeschakeld, stelt deze token agenten in staat zich zelf te registreren zonder voorafgaande systeemcreatie."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "When using POST, each heartbeat includes a JSON payload with system status summary, list of down systems, and triggered alerts."
|
||||
msgstr "Bij gebruik van POST bevat elke heartbeat een JSON-payload met een samenvatting van de systeemstatus, een lijst met uitgevallen systemen en geactiveerde meldingen."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
|
||||
@@ -93,6 +93,7 @@ msgstr "Handlinger"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Active"
|
||||
msgstr "Aktiv"
|
||||
@@ -140,6 +141,10 @@ msgstr "Admin"
|
||||
msgid "After"
|
||||
msgstr "Etter"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "After setting the environment variables, restart your Beszel hub for changes to take effect."
|
||||
msgstr "Etter å ha angitt miljøvariablene, start Beszel-huben på nytt for at endringene skal tre i kraft."
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Agent"
|
||||
@@ -350,6 +355,10 @@ msgstr "Sjekk {email} for en nullstillings-link."
|
||||
msgid "Check logs for more details."
|
||||
msgstr "Sjekk loggene for flere detaljer."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Check your monitoring service"
|
||||
msgstr "Sjekk overvåkingstjenesten din"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Check your notification service"
|
||||
msgstr "Sjekk din meldingstjeneste"
|
||||
@@ -643,6 +652,14 @@ msgstr "Tom"
|
||||
msgid "End Time"
|
||||
msgstr "Sluttid"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL"
|
||||
msgstr "Endepunkt-URL"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL to ping (required)"
|
||||
msgstr "Endepunkt-URL som skal pinges (påkrevd)"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Enter email address to reset password"
|
||||
msgstr "Skriv inn e-postadresse for å nullstille passordet"
|
||||
@@ -662,6 +679,9 @@ msgstr "Flyktig"
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
@@ -670,6 +690,10 @@ msgstr "Flyktig"
|
||||
msgid "Error"
|
||||
msgstr "Feil"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Example:"
|
||||
msgstr "Eksempel:"
|
||||
|
||||
#. placeholder {0}: alert.value
|
||||
#. placeholder {1}: info.unit
|
||||
#. placeholder {2}: alert.min
|
||||
@@ -727,6 +751,10 @@ msgstr "Autentisering mislyktes"
|
||||
msgid "Failed to save settings"
|
||||
msgstr "Kunne ikke lagre innstillingene"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Failed to send heartbeat"
|
||||
msgstr "Kunne ikke sende heartbeat"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Failed to send test notification"
|
||||
msgstr "Kunne ikke sende test-varsling"
|
||||
@@ -806,6 +834,18 @@ msgstr "Rutenett"
|
||||
msgid "Health"
|
||||
msgstr "Helse"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Heartbeat"
|
||||
msgstr "Heartbeat"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat Monitoring"
|
||||
msgstr "Heartbeat-overvåking"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat sent successfully"
|
||||
msgstr "Heartbeat sendt"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
@@ -816,6 +856,14 @@ msgstr "Homebrew-kommando"
|
||||
msgid "Host / IP"
|
||||
msgstr "Vert / IP"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP Method"
|
||||
msgstr "HTTP-metode"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP method: POST, GET, or HEAD (default: POST)"
|
||||
msgstr "HTTP-metode: POST, GET eller HEAD (standard: POST)"
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Idle"
|
||||
@@ -834,6 +882,10 @@ msgstr "Image"
|
||||
msgid "Inactive"
|
||||
msgstr "Inaktiv"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Interval"
|
||||
msgstr "Intervall"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Invalid email address."
|
||||
msgstr "Ugyldig e-postadresse."
|
||||
@@ -1110,6 +1162,10 @@ msgstr "Satt på Pause"
|
||||
msgid "Paused ({pausedSystemsLength})"
|
||||
msgstr "Pauset ({pausedSystemsLength})"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Payload format"
|
||||
msgstr "Nyttelastformat"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Per-core average utilization"
|
||||
@@ -1307,6 +1363,10 @@ msgstr "Søk"
|
||||
msgid "Search for systems or settings..."
|
||||
msgstr "Søk etter systemer eller innstillinger..."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Seconds between pings (default: 60)"
|
||||
msgstr "Sekunder mellom pinger (standard: 60)"
|
||||
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "Se <0>varslingsinnstillingene</0> for å konfigurere hvordan du vil motta varsler."
|
||||
@@ -1315,6 +1375,18 @@ msgstr "Se <0>varslingsinnstillingene</0> for å konfigurere hvordan du vil mott
|
||||
msgid "Select {foo}"
|
||||
msgstr "Velg {foo}"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send a single heartbeat ping to verify your endpoint is working."
|
||||
msgstr "Send en enkelt heartbeat-ping for å bekrefte at endepunktet fungerer."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send periodic outbound pings to an external monitoring service so you can monitor Beszel without exposing it to the internet."
|
||||
msgstr "Send periodiske utgående pinger til en ekstern overvåkingstjeneste slik at du kan overvåke Beszel uten å eksponere den for internett."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send test heartbeat"
|
||||
msgstr "Send test-heartbeat"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "Sendt"
|
||||
@@ -1335,6 +1407,10 @@ msgstr "Tjenester"
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "Angi prosentvise terskler for målerfarger."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Set the following environment variables on your Beszel hub to enable heartbeat monitoring:"
|
||||
msgstr "Angi følgende miljøvariabler på Beszel-huben din for å aktivere heartbeat-overvåking:"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
@@ -1452,10 +1528,18 @@ msgstr "Temperaturer på system-sensorer"
|
||||
msgid "Test <0>URL</0>"
|
||||
msgstr "Test <0>URL</0>"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Test heartbeat"
|
||||
msgstr "Test-heartbeat"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Test notification sent"
|
||||
msgstr "Test-varsling sendt"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "The overall status is <0>ok</0> when all systems are up, <1>warn</1> when alerts are triggered, and <2>error</2> when any system is down."
|
||||
msgstr "Den generelle statusen er <0>ok</0> når alle systemer er oppe, <1>varsel</1> når varsler utløses, og <2>feil</2> når et system er nede."
|
||||
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
msgid "Then log into the backend and reset your user account password in the users table."
|
||||
msgstr "Logg deretter inn i backend og nullstill passordet på din konto i users-tabellen."
|
||||
@@ -1642,6 +1726,7 @@ msgid "Upload"
|
||||
msgstr "Last opp"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Uptime"
|
||||
msgstr "Oppetid"
|
||||
|
||||
@@ -1716,6 +1801,10 @@ msgstr "Webhook / Push-varslinger"
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation."
|
||||
msgstr "Når aktivert, tillater denne tokenen agenter å registrere seg selv uten forutgående systemskapelse."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "When using POST, each heartbeat includes a JSON payload with system status summary, list of down systems, and triggered alerts."
|
||||
msgstr "Ved bruk av POST inkluderer hver heartbeat en JSON-nyttelast med systemstatussammendrag, liste over nede systemer og utløste varsler."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
|
||||
@@ -93,6 +93,7 @@ msgstr "Akcje"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Active"
|
||||
msgstr "Aktywny"
|
||||
@@ -140,6 +141,10 @@ msgstr "Admin"
|
||||
msgid "After"
|
||||
msgstr "Po"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "After setting the environment variables, restart your Beszel hub for changes to take effect."
|
||||
msgstr "Po ustawieniu zmiennych środowiskowych zrestartuj hub Beszel, aby zmiany weszły w życie."
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Agent"
|
||||
@@ -350,6 +355,10 @@ msgstr "Sprawdź {email}, aby uzyskać link do resetowania."
|
||||
msgid "Check logs for more details."
|
||||
msgstr "Sprawdź logi, aby uzyskać więcej informacji."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Check your monitoring service"
|
||||
msgstr "Sprawdź usługę monitorowania"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Check your notification service"
|
||||
msgstr "Sprawdź swój serwis powiadomień"
|
||||
@@ -643,6 +652,14 @@ msgstr "Pusta"
|
||||
msgid "End Time"
|
||||
msgstr "Czas zakończenia"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL"
|
||||
msgstr "Adres URL punktu końcowego"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL to ping (required)"
|
||||
msgstr "Adres URL punktu końcowego do pingowania (wymagany)"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Enter email address to reset password"
|
||||
msgstr "Wprowadź adres e-mail, aby zresetować hasło"
|
||||
@@ -662,6 +679,9 @@ msgstr "Tymczasowy"
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
@@ -670,6 +690,10 @@ msgstr "Tymczasowy"
|
||||
msgid "Error"
|
||||
msgstr "Błąd"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Example:"
|
||||
msgstr "Przykład:"
|
||||
|
||||
#. placeholder {0}: alert.value
|
||||
#. placeholder {1}: info.unit
|
||||
#. placeholder {2}: alert.min
|
||||
@@ -727,6 +751,10 @@ msgstr "Błąd autoryzacji"
|
||||
msgid "Failed to save settings"
|
||||
msgstr "Nie udało się zapisać ustawień"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Failed to send heartbeat"
|
||||
msgstr "Nie udało się wysłać heartbeat"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Failed to send test notification"
|
||||
msgstr "Nie udało się wysłać powiadomienia testowego"
|
||||
@@ -806,6 +834,18 @@ msgstr "Siatka"
|
||||
msgid "Health"
|
||||
msgstr "Kondycja"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Heartbeat"
|
||||
msgstr "Heartbeat"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat Monitoring"
|
||||
msgstr "Monitorowanie Heartbeat"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat sent successfully"
|
||||
msgstr "Heartbeat wysłany pomyślnie"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
@@ -816,6 +856,14 @@ msgstr "Polecenie Homebrew"
|
||||
msgid "Host / IP"
|
||||
msgstr "Host / adres IP"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP Method"
|
||||
msgstr "Metoda HTTP"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP method: POST, GET, or HEAD (default: POST)"
|
||||
msgstr "Metoda HTTP: POST, GET lub HEAD (domyślnie: POST)"
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Idle"
|
||||
@@ -834,6 +882,10 @@ msgstr "Obraz"
|
||||
msgid "Inactive"
|
||||
msgstr "Nieaktywny"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Interval"
|
||||
msgstr "Interwał"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Invalid email address."
|
||||
msgstr "Nieprawidłowy adres e-mail."
|
||||
@@ -1110,6 +1162,10 @@ msgstr "Wstrzymane"
|
||||
msgid "Paused ({pausedSystemsLength})"
|
||||
msgstr "Wstrzymane ({pausedSystemsLength})"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Payload format"
|
||||
msgstr "Format ładunku"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Per-core average utilization"
|
||||
@@ -1307,6 +1363,10 @@ msgstr "Szukaj"
|
||||
msgid "Search for systems or settings..."
|
||||
msgstr "Szukaj systemów lub ustawień..."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Seconds between pings (default: 60)"
|
||||
msgstr "Sekundy między pingami (domyślnie: 60)"
|
||||
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "Zobacz <0>ustawienia powiadomień</0>, aby skonfigurować sposób, w jaki otrzymujesz powiadomienia."
|
||||
@@ -1315,6 +1375,18 @@ msgstr "Zobacz <0>ustawienia powiadomień</0>, aby skonfigurować sposób, w jak
|
||||
msgid "Select {foo}"
|
||||
msgstr "Wybierz {foo}"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send a single heartbeat ping to verify your endpoint is working."
|
||||
msgstr "Wyślij pojedynczy ping heartbeat, aby sprawdzić, czy punkt końcowy działa."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send periodic outbound pings to an external monitoring service so you can monitor Beszel without exposing it to the internet."
|
||||
msgstr "Wysyłaj okresowe pingi wychodzące do zewnętrznej usługi monitorowania, dzięki czemu możesz monitorować Beszel bez wystawiania go na działanie Internetu."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send test heartbeat"
|
||||
msgstr "Wyślij testowy heartbeat"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "Wysłane"
|
||||
@@ -1335,6 +1407,10 @@ msgstr "Usługi"
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "Ustaw progi procentowe dla kolorów mierników."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Set the following environment variables on your Beszel hub to enable heartbeat monitoring:"
|
||||
msgstr "Ustaw następujące zmienne środowiskowe w hubie Beszel, aby włączyć monitorowanie heartbeat:"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
@@ -1452,10 +1528,18 @@ msgstr "Temperatury czujników systemowych."
|
||||
msgid "Test <0>URL</0>"
|
||||
msgstr "Test <0>URL</0>"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Test heartbeat"
|
||||
msgstr "Testuj heartbeat"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Test notification sent"
|
||||
msgstr "Testowe powiadomienie wysłane."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "The overall status is <0>ok</0> when all systems are up, <1>warn</1> when alerts are triggered, and <2>error</2> when any system is down."
|
||||
msgstr "Ogólny status to <0>ok</0>, gdy wszystkie systemy działają, <1>ostrzeżenie</1>, gdy wyzwalane są alerty, oraz <2>błąd</2>, gdy którykolwiek system nie działa."
|
||||
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
msgid "Then log into the backend and reset your user account password in the users table."
|
||||
msgstr "Następnie zaloguj się do panelu administracyjnego i zresetuj hasło do konta użytkownika w tabeli użytkowników."
|
||||
@@ -1642,8 +1726,9 @@ msgid "Upload"
|
||||
msgstr "Wysyłanie"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Uptime"
|
||||
msgstr "Czas pracy"
|
||||
msgstr "Uptime"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
@@ -1716,6 +1801,10 @@ msgstr "Webhook / Powiadomienia push"
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation."
|
||||
msgstr "Gdy jest włączony, ten token pozwala agentom na samodzielną rejestrację bez wcześniejszego tworzenia systemu."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "When using POST, each heartbeat includes a JSON payload with system status summary, list of down systems, and triggered alerts."
|
||||
msgstr "W przypadku korzystania z POST każdy heartbeat zawiera ładunek JSON z podsumowaniem statusu systemu, listą wyłączonych systemów i wyzwolonymi alertami."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
|
||||
@@ -42,7 +42,7 @@ msgstr "{count, plural, one {{countString} minuto} other {{countString} minutos}
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "{threads, plural, one {# thread} other {# threads}}"
|
||||
msgstr ""
|
||||
msgstr "{threads, plural, one {# thread} other {# threads}}"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 hour"
|
||||
@@ -93,6 +93,7 @@ msgstr "Ações"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Active"
|
||||
msgstr "Ativo"
|
||||
@@ -140,6 +141,10 @@ msgstr "Admin"
|
||||
msgid "After"
|
||||
msgstr "Depois"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "After setting the environment variables, restart your Beszel hub for changes to take effect."
|
||||
msgstr "Após configurar as variáveis de ambiente, reinicie o hub do Beszel para que as alterações entrem em vigor."
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Agente"
|
||||
@@ -231,7 +236,7 @@ msgstr "Largura de Banda"
|
||||
#. Battery label in systems table header
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Bat"
|
||||
msgstr ""
|
||||
msgstr "Bat"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/lib/alerts.ts
|
||||
@@ -350,6 +355,10 @@ msgstr "Verifique {email} para um link de redefinição."
|
||||
msgid "Check logs for more details."
|
||||
msgstr "Verifique os logs para mais detalhes."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Check your monitoring service"
|
||||
msgstr "Verifique o seu serviço de monitorização"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Check your notification service"
|
||||
msgstr "Verifique seu serviço de notificação"
|
||||
@@ -358,7 +367,7 @@ msgstr "Verifique seu serviço de notificação"
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Clear"
|
||||
msgstr ""
|
||||
msgstr "Limpar"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
msgid "Click on a container to view more information."
|
||||
@@ -641,7 +650,15 @@ msgstr "Vazia"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "End Time"
|
||||
msgstr ""
|
||||
msgstr "Hora de Fim"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL"
|
||||
msgstr "URL do Endpoint"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL to ping (required)"
|
||||
msgstr "URL do Endpoint para ping (obrigatório)"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Enter email address to reset password"
|
||||
@@ -662,6 +679,9 @@ msgstr "Efêmero"
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
@@ -670,6 +690,10 @@ msgstr "Efêmero"
|
||||
msgid "Error"
|
||||
msgstr "Erro"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Example:"
|
||||
msgstr "Exemplo:"
|
||||
|
||||
#. placeholder {0}: alert.value
|
||||
#. placeholder {1}: info.unit
|
||||
#. placeholder {2}: alert.min
|
||||
@@ -727,6 +751,10 @@ msgstr "Falha na autenticação"
|
||||
msgid "Failed to save settings"
|
||||
msgstr "Falha ao guardar as definições"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Failed to send heartbeat"
|
||||
msgstr "Falha ao enviar o batimento cardíaco"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Failed to send test notification"
|
||||
msgstr "Falha ao enviar notificação de teste"
|
||||
@@ -806,6 +834,18 @@ msgstr "Grade"
|
||||
msgid "Health"
|
||||
msgstr "Saúde"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Heartbeat"
|
||||
msgstr "Heartbeat"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat Monitoring"
|
||||
msgstr "Monitorização de Heartbeat"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat sent successfully"
|
||||
msgstr "Heartbeat enviado com sucesso"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
@@ -816,6 +856,14 @@ msgstr "Comando Homebrew"
|
||||
msgid "Host / IP"
|
||||
msgstr "Host / IP"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP Method"
|
||||
msgstr "Método HTTP"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP method: POST, GET, or HEAD (default: POST)"
|
||||
msgstr "Método HTTP: POST, GET ou HEAD (predefinido: POST)"
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Idle"
|
||||
@@ -834,6 +882,10 @@ msgstr "Imagem"
|
||||
msgid "Inactive"
|
||||
msgstr "Inativo"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Interval"
|
||||
msgstr "Intervalo"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Invalid email address."
|
||||
msgstr "Endereço de email inválido."
|
||||
@@ -1110,6 +1162,10 @@ msgstr "Pausado"
|
||||
msgid "Paused ({pausedSystemsLength})"
|
||||
msgstr "Pausado ({pausedSystemsLength})"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Payload format"
|
||||
msgstr "Formato do payload"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Per-core average utilization"
|
||||
@@ -1248,7 +1304,7 @@ msgstr "Retomar"
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgctxt "Root disk label"
|
||||
msgid "Root"
|
||||
msgstr ""
|
||||
msgstr "Root"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Rotate token"
|
||||
@@ -1307,13 +1363,29 @@ msgstr "Pesquisar"
|
||||
msgid "Search for systems or settings..."
|
||||
msgstr "Pesquisar por sistemas ou configurações..."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Seconds between pings (default: 60)"
|
||||
msgstr "Segundos entre pings (predefinido: 60)"
|
||||
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "Veja <0>configurações de notificação</0> para configurar como você recebe alertas."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Select {foo}"
|
||||
msgstr ""
|
||||
msgstr "Selecionar {foo}"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send a single heartbeat ping to verify your endpoint is working."
|
||||
msgstr "Envie um único ping de heartbeat para verificar se o seu endpoint está a funcionar."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send periodic outbound pings to an external monitoring service so you can monitor Beszel without exposing it to the internet."
|
||||
msgstr "Envie pings de saída periódicos para um serviço de monitorização externo para que possa monitorizar o Beszel sem o expor à internet."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send test heartbeat"
|
||||
msgstr "Enviar heartbeat de teste"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
@@ -1335,6 +1407,10 @@ msgstr "Serviços"
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "Defina os limiares de porcentagem para as cores do medidor."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Set the following environment variables on your Beszel hub to enable heartbeat monitoring:"
|
||||
msgstr "Defina as seguintes variáveis de ambiente no seu hub do Beszel para ativar a monitorização de heartbeat:"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
@@ -1362,7 +1438,7 @@ msgstr "Ordenar Por"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Start Time"
|
||||
msgstr ""
|
||||
msgstr "Hora de Início"
|
||||
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
@@ -1452,10 +1528,18 @@ msgstr "Temperaturas dos sensores do sistema"
|
||||
msgid "Test <0>URL</0>"
|
||||
msgstr "Testar <0>URL</0>"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Test heartbeat"
|
||||
msgstr "Testar heartbeat"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Test notification sent"
|
||||
msgstr "Notificação de teste enviada"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "The overall status is <0>ok</0> when all systems are up, <1>warn</1> when alerts are triggered, and <2>error</2> when any system is down."
|
||||
msgstr "O estado geral é <0>ok</0> quando todos os sistemas estão ativos, <1>aviso</1> quando os alertas são acionados e <2>erro</2> quando qualquer sistema está inativo."
|
||||
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
msgid "Then log into the backend and reset your user account password in the users table."
|
||||
msgstr "Em seguida, faça login no backend e redefina a senha da sua conta de usuário na tabela de usuários."
|
||||
@@ -1529,7 +1613,7 @@ msgstr "Dados totais enviados para cada interface"
|
||||
#. placeholder {0}: data.length
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Total: {0}"
|
||||
msgstr ""
|
||||
msgstr "Total: {0}"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Triggered by"
|
||||
@@ -1625,7 +1709,7 @@ msgstr "Ativo ({upSystemsLength})"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Update"
|
||||
msgstr ""
|
||||
msgstr "Atualizar"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
@@ -1642,8 +1726,9 @@ msgid "Upload"
|
||||
msgstr "Carregar"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Uptime"
|
||||
msgstr "Tempo de Atividade"
|
||||
msgstr "Uptime"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
@@ -1716,6 +1801,10 @@ msgstr "Notificações Webhook / Push"
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation."
|
||||
msgstr "Quando ativado, este token permite que os agentes se registrem automaticamente sem criação prévia do sistema."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "When using POST, each heartbeat includes a JSON payload with system status summary, list of down systems, and triggered alerts."
|
||||
msgstr "Ao usar POST, cada heartbeat inclui um payload JSON com o resumo do estado do sistema, a lista de sistemas inativos e os alertas acionados."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
|
||||
@@ -93,6 +93,7 @@ msgstr "Действия"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Active"
|
||||
msgstr "Активно"
|
||||
@@ -140,6 +141,10 @@ msgstr "Администратор"
|
||||
msgid "After"
|
||||
msgstr "После"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "After setting the environment variables, restart your Beszel hub for changes to take effect."
|
||||
msgstr "После установки переменных окружения перезапустите хаб Beszel, чтобы изменения вступили в силу."
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Агент"
|
||||
@@ -350,6 +355,10 @@ msgstr "Проверьте {email} для получения ссылки на
|
||||
msgid "Check logs for more details."
|
||||
msgstr "Проверьте журналы для получения более подробной информации."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Check your monitoring service"
|
||||
msgstr "Проверьте ваш сервис мониторинга"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Check your notification service"
|
||||
msgstr "Проверьте ваш сервис уведомлений"
|
||||
@@ -643,6 +652,14 @@ msgstr "Пустая"
|
||||
msgid "End Time"
|
||||
msgstr "Время окончания"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL"
|
||||
msgstr "URL-адрес конечной точки"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL to ping (required)"
|
||||
msgstr "URL-адрес конечной точки для пинга (обязательно)"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Enter email address to reset password"
|
||||
msgstr "Введите адрес электронной почты для сброса пароля"
|
||||
@@ -662,6 +679,9 @@ msgstr "Эфемерный"
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
@@ -670,6 +690,10 @@ msgstr "Эфемерный"
|
||||
msgid "Error"
|
||||
msgstr "Ошибка"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Example:"
|
||||
msgstr "Пример:"
|
||||
|
||||
#. placeholder {0}: alert.value
|
||||
#. placeholder {1}: info.unit
|
||||
#. placeholder {2}: alert.min
|
||||
@@ -727,6 +751,10 @@ msgstr "Не удалось аутентифицировать"
|
||||
msgid "Failed to save settings"
|
||||
msgstr "Не удалось сохранить настройки"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Failed to send heartbeat"
|
||||
msgstr "Не удалось отправить heartbeat"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Failed to send test notification"
|
||||
msgstr "Не удалось отправить тестовое уведомление"
|
||||
@@ -806,6 +834,18 @@ msgstr "Сетка"
|
||||
msgid "Health"
|
||||
msgstr "Здоровье"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Heartbeat"
|
||||
msgstr "Heartbeat"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat Monitoring"
|
||||
msgstr "Мониторинг Heartbeat"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat sent successfully"
|
||||
msgstr "Heartbeat успешно отправлен"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
@@ -816,6 +856,14 @@ msgstr "Команда Homebrew"
|
||||
msgid "Host / IP"
|
||||
msgstr "Хост / IP"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP Method"
|
||||
msgstr "HTTP-метод"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP method: POST, GET, or HEAD (default: POST)"
|
||||
msgstr "HTTP-метод: POST, GET или HEAD (по умолчанию: POST)"
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Idle"
|
||||
@@ -834,6 +882,10 @@ msgstr "Образ"
|
||||
msgid "Inactive"
|
||||
msgstr "Неактивно"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Interval"
|
||||
msgstr "Интервал"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Invalid email address."
|
||||
msgstr "Неверный адрес электронной почты."
|
||||
@@ -1110,6 +1162,10 @@ msgstr "Пауза"
|
||||
msgid "Paused ({pausedSystemsLength})"
|
||||
msgstr "Пауза ({pausedSystemsLength})"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Payload format"
|
||||
msgstr "Формат полезной нагрузки"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Per-core average utilization"
|
||||
@@ -1307,6 +1363,10 @@ msgstr "Поиск"
|
||||
msgid "Search for systems or settings..."
|
||||
msgstr "Поиск систем или настроек..."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Seconds between pings (default: 60)"
|
||||
msgstr "Секунды между пингами (по умолчанию: 60)"
|
||||
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "Смотрите <0>настройки уведомлений</0>, чтобы настроить, как вы получаете оповещения."
|
||||
@@ -1315,6 +1375,18 @@ msgstr "Смотрите <0>настройки уведомлений</0>, чт
|
||||
msgid "Select {foo}"
|
||||
msgstr "Выбрать {foo}"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send a single heartbeat ping to verify your endpoint is working."
|
||||
msgstr "Отправьте одиночный пинг heartbeat, чтобы проверить работоспособность конечной точки."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send periodic outbound pings to an external monitoring service so you can monitor Beszel without exposing it to the internet."
|
||||
msgstr "Отправляйте периодические исходящие пинги на внешний сервис мониторинга, чтобы вы могли мониторить Beszel без его публикации в интернете."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send test heartbeat"
|
||||
msgstr "Отправить тестовый heartbeat"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "Отправлено"
|
||||
@@ -1335,6 +1407,10 @@ msgstr "Службы"
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "Установите процентные пороги для цветов счетчиков."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Set the following environment variables on your Beszel hub to enable heartbeat monitoring:"
|
||||
msgstr "Установите следующие переменные окружения в хабе Beszel для включения мониторинга heartbeat:"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
@@ -1452,10 +1528,18 @@ msgstr "Температуры датчиков системы"
|
||||
msgid "Test <0>URL</0>"
|
||||
msgstr "Тест <0>URL</0>"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Test heartbeat"
|
||||
msgstr "Тестовый heartbeat"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Test notification sent"
|
||||
msgstr "Тестовое уведомление отправлено"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "The overall status is <0>ok</0> when all systems are up, <1>warn</1> when alerts are triggered, and <2>error</2> when any system is down."
|
||||
msgstr "Общий статус: <0>ok</0>, когда все системы работают, <1>предупреждение</1>, когда сработали оповещения, и <2>ошибка</2>, когда какая-либо система отключена."
|
||||
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
msgid "Then log into the backend and reset your user account password in the users table."
|
||||
msgstr "Затем войдите в бэкенд и сбросьте пароль вашей учетной записи в таблице пользователей."
|
||||
@@ -1642,8 +1726,9 @@ msgid "Upload"
|
||||
msgstr "Отдача"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Uptime"
|
||||
msgstr "Время работы"
|
||||
msgstr "Uptime"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
@@ -1716,6 +1801,10 @@ msgstr "Webhook / Push уведомления"
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation."
|
||||
msgstr "При включении этот токен позволяет агентам самостоятельно регистрироваться без предварительного создания системы."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "When using POST, each heartbeat includes a JSON payload with system status summary, list of down systems, and triggered alerts."
|
||||
msgstr "При использовании POST каждый heartbeat содержит полезную нагрузку JSON с кратким отчетом о состоянии системы, списком отключенных систем и сработавшими оповещениями."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
|
||||
@@ -93,6 +93,7 @@ msgstr "Dejanja"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Active"
|
||||
msgstr "Aktivno"
|
||||
@@ -140,9 +141,13 @@ msgstr "Administrator"
|
||||
msgid "After"
|
||||
msgstr "Po"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "After setting the environment variables, restart your Beszel hub for changes to take effect."
|
||||
msgstr "Po nastavitvi okoljskih spremenljivk ponovno zaženite vozlišče Beszel, da spremembe začnejo veljati."
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr ""
|
||||
msgstr "Agent"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
@@ -194,7 +199,7 @@ msgstr "Povprečna izkoriščenost procesorja kontejnerjev"
|
||||
#. placeholder {0}: alertData.unit
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "Average drops below <0>{value}{0}</0>"
|
||||
msgstr ""
|
||||
msgstr "Povprečje pade pod <0>{value}{0}</0>"
|
||||
|
||||
#. placeholder {0}: alertData.unit
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
@@ -231,7 +236,7 @@ msgstr "Pasovna širina"
|
||||
#. Battery label in systems table header
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Bat"
|
||||
msgstr ""
|
||||
msgstr "Bat"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/lib/alerts.ts
|
||||
@@ -255,7 +260,7 @@ msgstr "Pred"
|
||||
#. placeholder {2}: alert.min
|
||||
#: src/components/active-alerts.tsx
|
||||
msgid "Below {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
|
||||
msgstr ""
|
||||
msgstr "Pod {0}{1} v zadnjih {2, plural, one {# minuti} two {# minutah} few {# minutah} other {# minutah}}"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Beszel supports OpenID Connect and many OAuth2 authentication providers."
|
||||
@@ -350,6 +355,10 @@ msgstr "Preverite {email} za povezavo za ponastavitev."
|
||||
msgid "Check logs for more details."
|
||||
msgstr "Za več podrobnosti preverite dnevnike."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Check your monitoring service"
|
||||
msgstr "Preverite svojo storitev za spremljanje"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Check your notification service"
|
||||
msgstr "Preverite storitev obveščanja"
|
||||
@@ -457,7 +466,7 @@ msgstr "Kopiraj YAML"
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "CPU"
|
||||
msgstr ""
|
||||
msgstr "CPE"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "CPU Cores"
|
||||
@@ -556,11 +565,11 @@ msgstr "Prazni se"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Disk"
|
||||
msgstr ""
|
||||
msgstr "Disk"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Disk I/O"
|
||||
msgstr ""
|
||||
msgstr "Disk V/I"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Disk unit"
|
||||
@@ -643,6 +652,14 @@ msgstr "Prazna"
|
||||
msgid "End Time"
|
||||
msgstr "Čas konca"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL"
|
||||
msgstr "URL končne točke"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL to ping (required)"
|
||||
msgstr "URL končne točke za ping (zahtevano)"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Enter email address to reset password"
|
||||
msgstr "Vnesite e-poštni naslov za ponastavitev gesla"
|
||||
@@ -662,6 +679,9 @@ msgstr "Prehodni"
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
@@ -670,6 +690,10 @@ msgstr "Prehodni"
|
||||
msgid "Error"
|
||||
msgstr "Napaka"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Example:"
|
||||
msgstr "Primer:"
|
||||
|
||||
#. placeholder {0}: alert.value
|
||||
#. placeholder {1}: info.unit
|
||||
#. placeholder {2}: alert.min
|
||||
@@ -679,7 +703,7 @@ msgstr "Preseženo {0}{1} v zadnjih {2, plural, one {# minuti} other {# minutah}
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Exec main PID"
|
||||
msgstr ""
|
||||
msgstr "Glavni PID izvajanja"
|
||||
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
|
||||
@@ -707,7 +731,7 @@ msgstr "Izvozi trenutne nastavitve sistema."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Fahrenheit (°F)"
|
||||
msgstr ""
|
||||
msgstr "Fahrenheit (°F)"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Failed"
|
||||
@@ -727,6 +751,10 @@ msgstr "Preverjanje pristnosti ni uspelo"
|
||||
msgid "Failed to save settings"
|
||||
msgstr "Shranjevanje nastavitev ni uspelo"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Failed to send heartbeat"
|
||||
msgstr "Slanje srčnega utripa (heartbeat) ni uspelo"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Failed to send test notification"
|
||||
msgstr "Pošiljanje testnega obvestila ni uspelo"
|
||||
@@ -806,6 +834,18 @@ msgstr "Mreža"
|
||||
msgid "Health"
|
||||
msgstr "Zdravje"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Heartbeat"
|
||||
msgstr "Srčni utrip (Heartbeat)"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat Monitoring"
|
||||
msgstr "Spremljanje srčnega utripa"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat sent successfully"
|
||||
msgstr "Srčni utrip uspešno poslan"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
@@ -816,6 +856,14 @@ msgstr "Ukaz Homebrew"
|
||||
msgid "Host / IP"
|
||||
msgstr "Gostitelj / IP"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP Method"
|
||||
msgstr "Metoda HTTP"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP method: POST, GET, or HEAD (default: POST)"
|
||||
msgstr "Metoda HTTP: POST, GET ali HEAD (privzeto: POST)"
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Idle"
|
||||
@@ -834,6 +882,10 @@ msgstr "Slika"
|
||||
msgid "Inactive"
|
||||
msgstr "Neaktivno"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Interval"
|
||||
msgstr "Interval"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Invalid email address."
|
||||
msgstr "Napačen e-poštni naslov."
|
||||
@@ -958,7 +1010,7 @@ msgstr "Poraba pomnilnika docker kontejnerjev"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Model"
|
||||
msgstr ""
|
||||
msgstr "Model"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
@@ -1110,6 +1162,10 @@ msgstr "Zaustavljeno"
|
||||
msgid "Paused ({pausedSystemsLength})"
|
||||
msgstr "Pavzirano za {pausedSystemsLength}"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Payload format"
|
||||
msgstr "Oblika tovora (payload)"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Per-core average utilization"
|
||||
@@ -1307,6 +1363,10 @@ msgstr "Iskanje"
|
||||
msgid "Search for systems or settings..."
|
||||
msgstr "Iskanje sistemov ali nastavitev..."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Seconds between pings (default: 60)"
|
||||
msgstr "Sekunde med pingi (privzeto: 60)"
|
||||
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "Glejte <0>nastavitve obvestil</0>, da nastavite način prejemanja opozoril."
|
||||
@@ -1315,6 +1375,18 @@ msgstr "Glejte <0>nastavitve obvestil</0>, da nastavite način prejemanja opozor
|
||||
msgid "Select {foo}"
|
||||
msgstr "Izberi {foo}"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send a single heartbeat ping to verify your endpoint is working."
|
||||
msgstr "Pošljite en srčni utrip (ping), da preverite, ali vaša končna točka deluje."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send periodic outbound pings to an external monitoring service so you can monitor Beszel without exposing it to the internet."
|
||||
msgstr "Pošiljajte občasne odhodne pinge zunanji storitvi za spremljanje, da boste lahko spremljali Beszel, ne da bi ga izpostavili internetu."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send test heartbeat"
|
||||
msgstr "Pošlji testni srčni utrip"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "Poslano"
|
||||
@@ -1335,6 +1407,10 @@ msgstr "Storitve"
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "Nastavite odstotne pragove za barve merilnikov."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Set the following environment variables on your Beszel hub to enable heartbeat monitoring:"
|
||||
msgstr "Na svojem vozlišču Beszel nastavite naslednje okoljske spremenljivke, da omogočite spremljanje srčnega utripa:"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
@@ -1377,7 +1453,7 @@ msgstr "Stanje"
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Status"
|
||||
msgstr ""
|
||||
msgstr "Stanje"
|
||||
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Sub State"
|
||||
@@ -1452,10 +1528,18 @@ msgstr "Temperature sistemskih senzorjev"
|
||||
msgid "Test <0>URL</0>"
|
||||
msgstr "Preveri <0>URL</0>"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Test heartbeat"
|
||||
msgstr "Testni srčni utrip"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Test notification sent"
|
||||
msgstr "Testno obvestilo je poslano"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "The overall status is <0>ok</0> when all systems are up, <1>warn</1> when alerts are triggered, and <2>error</2> when any system is down."
|
||||
msgstr "Splošno stanje je <0>v redu</0>, ko vsi sistemi delujejo, <1>opozorilo</1>, ko se sprožijo opozorila, in <2>napaka</2>, ko kateri koli sistem ne deluje."
|
||||
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
msgid "Then log into the backend and reset your user account password in the users table."
|
||||
msgstr "Nato se prijavite v zaledni sistem in ponastavite geslo svojega uporabniškega računa v tabeli uporabnikov."
|
||||
@@ -1557,7 +1641,7 @@ msgstr "Sproži se, ko kateri koli senzor preseže prag"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when battery charge drops below a threshold"
|
||||
msgstr ""
|
||||
msgstr "Sproži se, ko napolnjenost baterije pade pod prag"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when combined up/down exceeds a threshold"
|
||||
@@ -1642,8 +1726,9 @@ msgid "Upload"
|
||||
msgstr "Naloži"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Uptime"
|
||||
msgstr "Čas delovanja"
|
||||
msgstr "Uptime"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
@@ -1716,6 +1801,10 @@ msgstr "Webhook / potisna obvestila"
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation."
|
||||
msgstr "Ko je omogočen, ta žeton omogoča agentom samoregistracijo brez predhodnega ustvarjanja sistema."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "When using POST, each heartbeat includes a JSON payload with system status summary, list of down systems, and triggered alerts."
|
||||
msgstr "Pri uporabi POST vsak srčni utrip vključuje tovor JSON s povzetkom stanja sistema, seznamom nedelujočih sistemov in sproženimi opozorili."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: sr\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-01-31 21:16\n"
|
||||
"PO-Revision-Date: 2026-02-03 15:27\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Serbian (Cyrillic)\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
|
||||
@@ -93,6 +93,7 @@ msgstr "Акције"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Active"
|
||||
msgstr "Активно"
|
||||
@@ -140,6 +141,10 @@ msgstr "Администратор"
|
||||
msgid "After"
|
||||
msgstr "После"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "After setting the environment variables, restart your Beszel hub for changes to take effect."
|
||||
msgstr "Nakon podešavanja promenljivih okruženja, ponovo pokrenite Beszel hub da bi promene stupile na snagu."
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Агент"
|
||||
@@ -231,7 +236,7 @@ msgstr "Пропусни опсег"
|
||||
#. Battery label in systems table header
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Bat"
|
||||
msgstr ""
|
||||
msgstr "Бат"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/lib/alerts.ts
|
||||
@@ -350,6 +355,10 @@ msgstr "Проверите {email} за линк за ресетовање."
|
||||
msgid "Check logs for more details."
|
||||
msgstr "Проверите логове за више детаља."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Check your monitoring service"
|
||||
msgstr "Proverite svoju uslugu monitoringa"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Check your notification service"
|
||||
msgstr "Проверите ваш сервис за обавештавања"
|
||||
@@ -643,6 +652,14 @@ msgstr "Празнo"
|
||||
msgid "End Time"
|
||||
msgstr "Време завршетка"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL"
|
||||
msgstr "URL krajnje tačke"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL to ping (required)"
|
||||
msgstr "URL krajnje tačke za ping (obavezno)"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Enter email address to reset password"
|
||||
msgstr "Унесите адресу е-поште за ресетовање лозинке"
|
||||
@@ -662,6 +679,9 @@ msgstr "Ефемеран"
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
@@ -670,6 +690,10 @@ msgstr "Ефемеран"
|
||||
msgid "Error"
|
||||
msgstr "Грешка"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Example:"
|
||||
msgstr "Primer:"
|
||||
|
||||
#. placeholder {0}: alert.value
|
||||
#. placeholder {1}: info.unit
|
||||
#. placeholder {2}: alert.min
|
||||
@@ -727,6 +751,10 @@ msgstr "Неуспела аутентификација"
|
||||
msgid "Failed to save settings"
|
||||
msgstr "Неуспело чување подешавања"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Failed to send heartbeat"
|
||||
msgstr "Slanje heartbeat-a nije uspelo"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Failed to send test notification"
|
||||
msgstr "Неуспело слање тест обавештења"
|
||||
@@ -806,6 +834,18 @@ msgstr "Мрежа"
|
||||
msgid "Health"
|
||||
msgstr "Здравље"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Heartbeat"
|
||||
msgstr "Heartbeat"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat Monitoring"
|
||||
msgstr "Monitoring heartbeat-a"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat sent successfully"
|
||||
msgstr "Heartbeat uspešno poslat"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
@@ -816,6 +856,14 @@ msgstr "Homebrew команда"
|
||||
msgid "Host / IP"
|
||||
msgstr "Хост / IP"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP Method"
|
||||
msgstr "HTTP metod"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP method: POST, GET, or HEAD (default: POST)"
|
||||
msgstr "HTTP metod: POST, GET ili HEAD (podrazumevano: POST)"
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Idle"
|
||||
@@ -834,6 +882,10 @@ msgstr "Слика"
|
||||
msgid "Inactive"
|
||||
msgstr "Неактивно"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Interval"
|
||||
msgstr "Interval"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Invalid email address."
|
||||
msgstr "Неважећа имејл адреса."
|
||||
@@ -1110,6 +1162,10 @@ msgstr "Паузирано"
|
||||
msgid "Paused ({pausedSystemsLength})"
|
||||
msgstr "Паузирано ({pausedSystemsLength})"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Payload format"
|
||||
msgstr "Format korisnog opterećenja (payload)"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Per-core average utilization"
|
||||
@@ -1307,6 +1363,10 @@ msgstr "Претрага"
|
||||
msgid "Search for systems or settings..."
|
||||
msgstr "Претражите системе или подешавања..."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Seconds between pings (default: 60)"
|
||||
msgstr "Sekunde između pingova (podrazumevano: 60)"
|
||||
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "Погледајте <0>подешавања обавештења</0> да конфигуришете како primate упозорења."
|
||||
@@ -1315,6 +1375,18 @@ msgstr "Погледајте <0>подешавања обавештења</0> д
|
||||
msgid "Select {foo}"
|
||||
msgstr "Изаберите {foo}"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send a single heartbeat ping to verify your endpoint is working."
|
||||
msgstr "Pošaljite jedan heartbeat ping da biste proverili da li krajnja tačka radi."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send periodic outbound pings to an external monitoring service so you can monitor Beszel without exposing it to the internet."
|
||||
msgstr "Šaljite periodične odlazne pingove spoljnoj usluzi monitoringa kako biste mogli da nadgledate Beszel bez izlaganja internetu."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send test heartbeat"
|
||||
msgstr "Pošalji testni heartbeat"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "Послато"
|
||||
@@ -1335,6 +1407,10 @@ msgstr "Сервиси"
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "Подесите процентуалне прагове за боје мерача."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Set the following environment variables on your Beszel hub to enable heartbeat monitoring:"
|
||||
msgstr "Podesite sledeće promenljive okruženja na svom Beszel hub-u da biste omogućili monitoring heartbeat-a:"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
@@ -1452,10 +1528,18 @@ msgstr "Температуре системских сензора"
|
||||
msgid "Test <0>URL</0>"
|
||||
msgstr "Тестирај <0>URL</0>"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Test heartbeat"
|
||||
msgstr "Testiraj heartbeat"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Test notification sent"
|
||||
msgstr "Тест обавештење послато"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "The overall status is <0>ok</0> when all systems are up, <1>warn</1> when alerts are triggered, and <2>error</2> when any system is down."
|
||||
msgstr "Ukupan status je <0>ok</0> kada su svi sistemi aktivni, <1>upozorenje</1> kada se aktiviraju upozorenja i <2>greška</2> kada je bilo koji sistem isključen."
|
||||
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
msgid "Then log into the backend and reset your user account password in the users table."
|
||||
msgstr "Затим се пријавите на backend и ресетујте лозинку корисничког налога у табели корисника."
|
||||
@@ -1642,8 +1726,9 @@ msgid "Upload"
|
||||
msgstr "Отпреми"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Uptime"
|
||||
msgstr "Време рада"
|
||||
msgstr "Uptime"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
@@ -1716,6 +1801,10 @@ msgstr "Webhook / Push обавештења"
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation."
|
||||
msgstr "Када је омогућен, овај токен омогућава агентима да се сами региструју без претходног креирања система."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "When using POST, each heartbeat includes a JSON payload with system status summary, list of down systems, and triggered alerts."
|
||||
msgstr "Kada koristite POST, svaki heartbeat uključuje JSON payload sa rezimeom statusa sistema, listom isključenih sistema i aktiviranim upozorenjima."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
|
||||
@@ -93,6 +93,7 @@ msgstr "Åtgärder"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Active"
|
||||
msgstr "Aktiv"
|
||||
@@ -140,6 +141,10 @@ msgstr "Admin"
|
||||
msgid "After"
|
||||
msgstr "Efter"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "After setting the environment variables, restart your Beszel hub for changes to take effect."
|
||||
msgstr "Efter att du har ställt in miljövariablerna, starta om din Beszel-hubb för att ändringarna ska träda i kraft."
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Agent"
|
||||
@@ -231,7 +236,7 @@ msgstr "Bandbredd"
|
||||
#. Battery label in systems table header
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Bat"
|
||||
msgstr ""
|
||||
msgstr "Bat"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/lib/alerts.ts
|
||||
@@ -307,7 +312,7 @@ msgstr "Avbryt"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Capabilities"
|
||||
msgstr ""
|
||||
msgstr "Capabilities"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Capacity"
|
||||
@@ -350,6 +355,10 @@ msgstr "Kontrollera {email} för en återställningslänk."
|
||||
msgid "Check logs for more details."
|
||||
msgstr "Kontrollera loggarna för mer information."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Check your monitoring service"
|
||||
msgstr "Kontrollera din övervakningstjänst"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Check your notification service"
|
||||
msgstr "Kontrollera din aviseringstjänst"
|
||||
@@ -465,11 +474,11 @@ msgstr "CPU-kärnor"
|
||||
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "CPU Peak"
|
||||
msgstr ""
|
||||
msgstr "CPU-topp"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "CPU time"
|
||||
msgstr ""
|
||||
msgstr "CPU-tid"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "CPU Time Breakdown"
|
||||
@@ -560,7 +569,7 @@ msgstr "Disk"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Disk I/O"
|
||||
msgstr "Disk-I/O"
|
||||
msgstr "Disk I/O"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Disk unit"
|
||||
@@ -643,6 +652,14 @@ msgstr "Tom"
|
||||
msgid "End Time"
|
||||
msgstr "Sluttid"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL"
|
||||
msgstr "Slutpunkts-URL"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL to ping (required)"
|
||||
msgstr "Slutpunkts-URL att pinga (krävs)"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Enter email address to reset password"
|
||||
msgstr "Ange e-postadress för att återställa lösenord"
|
||||
@@ -662,6 +679,9 @@ msgstr "Flyktig"
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
@@ -670,6 +690,10 @@ msgstr "Flyktig"
|
||||
msgid "Error"
|
||||
msgstr "Fel"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Example:"
|
||||
msgstr "Exempel:"
|
||||
|
||||
#. placeholder {0}: alert.value
|
||||
#. placeholder {1}: info.unit
|
||||
#. placeholder {2}: alert.min
|
||||
@@ -679,7 +703,7 @@ msgstr "Överskrider {0}{1} under de senaste {2, plural, one {# minuten} other {
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Exec main PID"
|
||||
msgstr ""
|
||||
msgstr "Exec-huvud-PID"
|
||||
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
|
||||
@@ -727,6 +751,10 @@ msgstr "Autentisering misslyckades"
|
||||
msgid "Failed to save settings"
|
||||
msgstr "Kunde inte spara inställningar"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Failed to send heartbeat"
|
||||
msgstr "Misslyckades med att skicka heartbeat"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Failed to send test notification"
|
||||
msgstr "Kunde inte skicka testavisering"
|
||||
@@ -806,6 +834,18 @@ msgstr "Rutnät"
|
||||
msgid "Health"
|
||||
msgstr "Hälsa"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Heartbeat"
|
||||
msgstr "Heartbeat"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat Monitoring"
|
||||
msgstr "Heartbeat-övervakning"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat sent successfully"
|
||||
msgstr "Heartbeat skickades framgångsrikt"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
@@ -816,6 +856,14 @@ msgstr "Homebrew-kommando"
|
||||
msgid "Host / IP"
|
||||
msgstr "Värd / IP"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP Method"
|
||||
msgstr "HTTP-metod"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP method: POST, GET, or HEAD (default: POST)"
|
||||
msgstr "HTTP-metod: POST, GET eller HEAD (standard: POST)"
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Idle"
|
||||
@@ -834,6 +882,10 @@ msgstr "Avbild"
|
||||
msgid "Inactive"
|
||||
msgstr "Inaktiv"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Interval"
|
||||
msgstr "Intervall"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Invalid email address."
|
||||
msgstr "Ogiltig e-postadress."
|
||||
@@ -858,7 +910,7 @@ msgstr "Livscykel"
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "limit"
|
||||
msgstr ""
|
||||
msgstr "gräns"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Load Average"
|
||||
@@ -914,7 +966,7 @@ msgstr "Letar du istället efter var du skapar larm? Klicka på klockikonerna <0
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Main PID"
|
||||
msgstr ""
|
||||
msgstr "Huvud-PID"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Manage display and notification preferences."
|
||||
@@ -945,7 +997,7 @@ msgstr "Minnesgräns"
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Memory Peak"
|
||||
msgstr ""
|
||||
msgstr "Minnestopp"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/lib/alerts.ts
|
||||
@@ -1110,6 +1162,10 @@ msgstr "Pausad"
|
||||
msgid "Paused ({pausedSystemsLength})"
|
||||
msgstr "Pausad ({pausedSystemsLength})"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Payload format"
|
||||
msgstr "Nyttolastformat"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Per-core average utilization"
|
||||
@@ -1121,7 +1177,7 @@ msgstr "Procentandel av tid spenderad i varje tillstånd"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Permanent"
|
||||
msgstr ""
|
||||
msgstr "Permanent"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Persistence"
|
||||
@@ -1248,7 +1304,7 @@ msgstr "Återuppta"
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgctxt "Root disk label"
|
||||
msgid "Root"
|
||||
msgstr ""
|
||||
msgstr "Root"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Rotate token"
|
||||
@@ -1307,6 +1363,10 @@ msgstr "Sök"
|
||||
msgid "Search for systems or settings..."
|
||||
msgstr "Sök efter system eller inställningar..."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Seconds between pings (default: 60)"
|
||||
msgstr "Sekunder mellan pingar (standard: 60)"
|
||||
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "Se <0>aviseringsinställningar</0> för att konfigurera hur du tar emot larm."
|
||||
@@ -1315,6 +1375,18 @@ msgstr "Se <0>aviseringsinställningar</0> för att konfigurera hur du tar emot
|
||||
msgid "Select {foo}"
|
||||
msgstr "Välj {foo}"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send a single heartbeat ping to verify your endpoint is working."
|
||||
msgstr "Skicka en enstaka heartbeat-ping för att verifiera att din slutpunkt fungerar."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send periodic outbound pings to an external monitoring service so you can monitor Beszel without exposing it to the internet."
|
||||
msgstr "Skicka periodiska utgående pingar till en extern övervakningstjänst så att du kan övervaka Beszel utan att exponera den för internet."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send test heartbeat"
|
||||
msgstr "Skicka test-heartbeat"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "Skickat"
|
||||
@@ -1335,6 +1407,10 @@ msgstr "Tjänster"
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "Ställ in procentuella tröskelvärden för mätarfärger."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Set the following environment variables on your Beszel hub to enable heartbeat monitoring:"
|
||||
msgstr "Ställ in följande miljövariabler på din Beszel-hubb för att aktivera heartbeat-övervakning:"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
@@ -1411,7 +1487,7 @@ msgstr "Systembelastning över tid"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Systemd Services"
|
||||
msgstr ""
|
||||
msgstr "Systemd-tjänster"
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Systems"
|
||||
@@ -1452,10 +1528,18 @@ msgstr "Temperaturer för systemsensorer"
|
||||
msgid "Test <0>URL</0>"
|
||||
msgstr "Testa <0>URL</0>"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Test heartbeat"
|
||||
msgstr "Testa heartbeat"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Test notification sent"
|
||||
msgstr "Testavisering skickad"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "The overall status is <0>ok</0> when all systems are up, <1>warn</1> when alerts are triggered, and <2>error</2> when any system is down."
|
||||
msgstr "Den övergripande statusen är <0>ok</0> när alla system är uppe, <1>varning</1> när larm utlöses och <2>fel</2> när något system är nere."
|
||||
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
msgid "Then log into the backend and reset your user account password in the users table."
|
||||
msgstr "Logga sedan in på backend och återställ ditt användarkontos lösenord i användartabellen."
|
||||
@@ -1591,7 +1675,7 @@ msgstr "Typ"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Unit file"
|
||||
msgstr ""
|
||||
msgstr "Unit-fil"
|
||||
|
||||
#. Temperature / network units
|
||||
#: src/components/routes/settings/general.tsx
|
||||
@@ -1611,7 +1695,7 @@ msgstr "Okänd"
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Unlimited"
|
||||
msgstr ""
|
||||
msgstr "Obegränsad"
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
@@ -1642,6 +1726,7 @@ msgid "Upload"
|
||||
msgstr "Ladda upp"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Uptime"
|
||||
msgstr "Drifttid"
|
||||
|
||||
@@ -1716,6 +1801,10 @@ msgstr "Webhook / Push-aviseringar"
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation."
|
||||
msgstr "När aktiverad tillåter denna token agenter att självregistrera utan föregående systemskapande."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "When using POST, each heartbeat includes a JSON payload with system status summary, list of down systems, and triggered alerts."
|
||||
msgstr "När POST används inkluderar varje heartbeat en JSON-nyttolast med systemstatussammanfattning, lista över nere system och utlösta larm."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
|
||||
@@ -93,6 +93,7 @@ msgstr "Eylemler"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Active"
|
||||
msgstr "Aktif"
|
||||
@@ -140,6 +141,10 @@ msgstr "Yönetici"
|
||||
msgid "After"
|
||||
msgstr "Sonra"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "After setting the environment variables, restart your Beszel hub for changes to take effect."
|
||||
msgstr "Ortam değişkenlerini ayarladıktan sonra, değişikliklerin etkili olması için Beszel hub'ınızı yeniden başlatın."
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Aracı"
|
||||
@@ -350,6 +355,10 @@ msgstr "Sıfırlama bağlantısı için {email} kontrol edin."
|
||||
msgid "Check logs for more details."
|
||||
msgstr "Daha fazla ayrıntı için günlükleri kontrol edin."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Check your monitoring service"
|
||||
msgstr "İzleme servisinizi kontrol edin"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Check your notification service"
|
||||
msgstr "Bildirim hizmetinizi kontrol edin"
|
||||
@@ -457,7 +466,7 @@ msgstr "YAML'ı kopyala"
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "CPU"
|
||||
msgstr ""
|
||||
msgstr "CPU"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "CPU Cores"
|
||||
@@ -556,7 +565,7 @@ msgstr "Boşalıyor"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Disk"
|
||||
msgstr ""
|
||||
msgstr "Disk"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Disk I/O"
|
||||
@@ -643,6 +652,14 @@ msgstr "Boş"
|
||||
msgid "End Time"
|
||||
msgstr "Bitiş Saati"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL"
|
||||
msgstr "Uç Nokta URL'si"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Endpoint URL to ping (required)"
|
||||
msgstr "Ping atılacak uç nokta URL'si (gerekli)"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Enter email address to reset password"
|
||||
msgstr "Şifreyi sıfırlamak için e-posta adresini girin"
|
||||
@@ -662,6 +679,9 @@ msgstr "Geçici"
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
@@ -670,6 +690,10 @@ msgstr "Geçici"
|
||||
msgid "Error"
|
||||
msgstr "Hata"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Example:"
|
||||
msgstr "Örnek:"
|
||||
|
||||
#. placeholder {0}: alert.value
|
||||
#. placeholder {1}: info.unit
|
||||
#. placeholder {2}: alert.min
|
||||
@@ -727,6 +751,10 @@ msgstr "Kimlik doğrulama başarısız"
|
||||
msgid "Failed to save settings"
|
||||
msgstr "Ayarlar kaydedilemedi"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Failed to send heartbeat"
|
||||
msgstr "Heartbeat gönderilemedi"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Failed to send test notification"
|
||||
msgstr "Test bildirimi gönderilemedi"
|
||||
@@ -806,6 +834,18 @@ msgstr "Izgara"
|
||||
msgid "Health"
|
||||
msgstr "Sağlık"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Heartbeat"
|
||||
msgstr "Heartbeat"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat Monitoring"
|
||||
msgstr "Heartbeat İzleme"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat sent successfully"
|
||||
msgstr "Heartbeat başarıyla gönderildi"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
@@ -816,6 +856,14 @@ msgstr "Homebrew komutu"
|
||||
msgid "Host / IP"
|
||||
msgstr "Ana Bilgisayar / IP"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP Method"
|
||||
msgstr "HTTP Yöntemi"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP method: POST, GET, or HEAD (default: POST)"
|
||||
msgstr "HTTP yöntemi: POST, GET veya HEAD (varsayılan: POST)"
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Idle"
|
||||
@@ -834,6 +882,10 @@ msgstr "İmaj"
|
||||
msgid "Inactive"
|
||||
msgstr "Pasif"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Interval"
|
||||
msgstr "Aralık"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Invalid email address."
|
||||
msgstr "Geçersiz e-posta adresi."
|
||||
@@ -958,7 +1010,7 @@ msgstr "Docker konteynerlerinin bellek kullanımı"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Model"
|
||||
msgstr ""
|
||||
msgstr "Model"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
@@ -1096,7 +1148,7 @@ msgstr "Şifre sıfırlama isteği alındı"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Past"
|
||||
msgstr ""
|
||||
msgstr "Geçmiş"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Pause"
|
||||
@@ -1110,6 +1162,10 @@ msgstr "Duraklatıldı"
|
||||
msgid "Paused ({pausedSystemsLength})"
|
||||
msgstr "Duraklatıldı ({pausedSystemsLength})"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Payload format"
|
||||
msgstr "Yük (Payload) formatı"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Per-core average utilization"
|
||||
@@ -1162,7 +1218,7 @@ msgstr "Lütfen hesabınıza giriş yapın"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Port"
|
||||
msgstr ""
|
||||
msgstr "Port"
|
||||
|
||||
#. Power On Time
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
@@ -1307,6 +1363,10 @@ msgstr "Ara"
|
||||
msgid "Search for systems or settings..."
|
||||
msgstr "Sistemler veya ayarlar için ara..."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Seconds between pings (default: 60)"
|
||||
msgstr "Pingler arasındaki saniye (varsayılan: 60)"
|
||||
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "Uyarıları nasıl alacağınızı yapılandırmak için <0>bildirim ayarlarını</0> inceleyin."
|
||||
@@ -1315,6 +1375,18 @@ msgstr "Uyarıları nasıl alacağınızı yapılandırmak için <0>bildirim aya
|
||||
msgid "Select {foo}"
|
||||
msgstr "Seç {foo}"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send a single heartbeat ping to verify your endpoint is working."
|
||||
msgstr "Uç noktanızın çalıştığını doğrulamak için tek bir heartbeat ping'i gönderin."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send periodic outbound pings to an external monitoring service so you can monitor Beszel without exposing it to the internet."
|
||||
msgstr "Beszel'i internete maruz bırakmadan izleyebilmeniz için harici bir izleme servisine periyodik giden pingler gönderin."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Send test heartbeat"
|
||||
msgstr "Test heartbeat gönder"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "Gönderildi"
|
||||
@@ -1335,6 +1407,10 @@ msgstr "Hizmetler"
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "Sayaç renkleri için yüzde eşiklerini ayarlayın."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Set the following environment variables on your Beszel hub to enable heartbeat monitoring:"
|
||||
msgstr "Heartbeat izlemeyi etkinleştirmek için Beszel hub'ınızda aşağıdaki ortam değişkenlerini ayarlayın:"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
@@ -1452,10 +1528,18 @@ msgstr "Sistem sensörlerinin sıcaklıkları"
|
||||
msgid "Test <0>URL</0>"
|
||||
msgstr "<0>URL</0>'yi Test Et"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Test heartbeat"
|
||||
msgstr "Test heartbeat"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Test notification sent"
|
||||
msgstr "Test bildirimi gönderildi"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "The overall status is <0>ok</0> when all systems are up, <1>warn</1> when alerts are triggered, and <2>error</2> when any system is down."
|
||||
msgstr "Tüm sistemler çalışırken genel durum <0>ok</0>, uyarılar tetiklendiğinde <1>uyarı</1> ve herhangi bir sistem çöktüğünde <2>hata</2> şeklindedir."
|
||||
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
msgid "Then log into the backend and reset your user account password in the users table."
|
||||
msgstr "Ardından arka uca giriş yapın ve kullanıcılar tablosunda kullanıcı hesabı şifrenizi sıfırlayın."
|
||||
@@ -1497,7 +1581,7 @@ msgstr "Temayı değiştir"
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Token"
|
||||
msgstr ""
|
||||
msgstr "Token"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
@@ -1642,8 +1726,9 @@ msgid "Upload"
|
||||
msgstr "Yükle"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Uptime"
|
||||
msgstr "Çalışma Süresi"
|
||||
msgstr "Uptime"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
@@ -1716,6 +1801,10 @@ msgstr "Webhook / Anlık bildirimler"
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation."
|
||||
msgstr "Etkinleştirildiğinde, bu token aracıların önceden sistem oluşturmadan kendilerini kaydetmelerine izin verir."
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "When using POST, each heartbeat includes a JSON payload with system status summary, list of down systems, and triggered alerts."
|
||||
msgstr "POST kullanırken, her heartbeat sistem durumu özeti, çöken sistemlerin listesi ve tetiklenen uyarıları içeren bir JSON yükü içerir."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user