mirror of
https://github.com/henrygd/beszel.git
synced 2026-03-22 13:36:16 +01:00
Compare commits
32 Commits
docker-24-
...
3af3049816
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3af3049816 | ||
|
|
bd51580d74 | ||
|
|
76a1145611 | ||
|
|
916503579d | ||
|
|
412f8c6bf5 | ||
|
|
444d080d1d | ||
|
|
54e2e8cfd9 | ||
|
|
902657815c | ||
|
|
94c89a8fdf | ||
|
|
4c92c89fd0 | ||
|
|
9b42dc301a | ||
|
|
f05cce662b | ||
|
|
a3b2530fb4 | ||
|
|
69d9b6c696 | ||
|
|
5da4afb657 | ||
|
|
eafb67ea04 | ||
|
|
ce249b620c | ||
|
|
ce7b56bdd2 | ||
|
|
9cad5a33e5 | ||
|
|
b7b95b9bb0 | ||
|
|
8f5632808a | ||
|
|
002badbe4f | ||
|
|
3b385aff85 | ||
|
|
49a2241033 | ||
|
|
8b1450fe32 | ||
|
|
4964a3c55f | ||
|
|
af57c76fd0 | ||
|
|
e6fc6906d5 | ||
|
|
1aef193b36 | ||
|
|
7522d31ae1 | ||
|
|
761da743b6 | ||
|
|
c779008340 |
@@ -5,8 +5,11 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"log/slog"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -16,6 +19,7 @@ import (
|
||||
"github.com/henrygd/beszel/agent/deltatracker"
|
||||
"github.com/henrygd/beszel/internal/common"
|
||||
"github.com/henrygd/beszel/internal/entities/system"
|
||||
"github.com/shirou/gopsutil/v4/host"
|
||||
gossh "golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
@@ -61,7 +65,7 @@ func NewAgent(dataDir ...string) (agent *Agent, err error) {
|
||||
agent.netIoStats = make(map[uint16]system.NetIoStats)
|
||||
agent.netInterfaceDeltaTrackers = make(map[uint16]*deltatracker.DeltaTracker[string, uint64])
|
||||
|
||||
agent.dataDir, err = GetDataDir(dataDir...)
|
||||
agent.dataDir, err = getDataDir(dataDir...)
|
||||
if err != nil {
|
||||
slog.Warn("Data directory not found")
|
||||
} else {
|
||||
@@ -224,12 +228,38 @@ func (a *Agent) gatherStats(options common.DataRequestOptions) *system.CombinedD
|
||||
return data
|
||||
}
|
||||
|
||||
// Start initializes and starts the agent with optional WebSocket connection
|
||||
// StartAgent initializes and starts the agent with optional WebSocket connection
|
||||
func (a *Agent) Start(serverOptions ServerOptions) error {
|
||||
a.keys = serverOptions.Keys
|
||||
return a.connectionManager.Start(serverOptions)
|
||||
}
|
||||
|
||||
func (a *Agent) getFingerprint() string {
|
||||
return GetFingerprint(a.dataDir, a.systemDetails.Hostname, a.systemDetails.CpuModel)
|
||||
// first look for a fingerprint in the data directory
|
||||
if a.dataDir != "" {
|
||||
if fp, err := os.ReadFile(filepath.Join(a.dataDir, "fingerprint")); err == nil {
|
||||
return string(fp)
|
||||
}
|
||||
}
|
||||
|
||||
// if no fingerprint is found, generate one
|
||||
fingerprint, err := host.HostID()
|
||||
// we ignore a commonly known "product_uuid" known not to be unique
|
||||
if err != nil || fingerprint == "" || fingerprint == "03000200-0400-0500-0006-000700080009" {
|
||||
fingerprint = a.systemDetails.Hostname + a.systemDetails.CpuModel
|
||||
}
|
||||
|
||||
// hash fingerprint
|
||||
sum := sha256.Sum256([]byte(fingerprint))
|
||||
fingerprint = hex.EncodeToString(sum[:24])
|
||||
|
||||
// save fingerprint to data directory
|
||||
if a.dataDir != "" {
|
||||
err = os.WriteFile(filepath.Join(a.dataDir, "fingerprint"), []byte(fingerprint), 0644)
|
||||
if err != nil {
|
||||
slog.Warn("Failed to save fingerprint", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
return fingerprint
|
||||
}
|
||||
|
||||
@@ -8,10 +8,10 @@ import (
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// GetDataDir returns the path to the data directory for the agent and an error
|
||||
// getDataDir returns the path to the data directory for the agent and an error
|
||||
// if the directory is not valid. Attempts to find the optimal data directory if
|
||||
// no data directories are provided.
|
||||
func GetDataDir(dataDirs ...string) (string, error) {
|
||||
func getDataDir(dataDirs ...string) (string, error) {
|
||||
if len(dataDirs) > 0 {
|
||||
return testDataDirs(dataDirs)
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ func TestGetDataDir(t *testing.T) {
|
||||
// Test with explicit dataDir parameter
|
||||
t.Run("explicit data dir", func(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
result, err := GetDataDir(tempDir)
|
||||
result, err := getDataDir(tempDir)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tempDir, result)
|
||||
})
|
||||
@@ -26,7 +26,7 @@ func TestGetDataDir(t *testing.T) {
|
||||
t.Run("explicit data dir - create new", func(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
newDir := filepath.Join(tempDir, "new-data-dir")
|
||||
result, err := GetDataDir(newDir)
|
||||
result, err := getDataDir(newDir)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, newDir, result)
|
||||
|
||||
@@ -52,7 +52,7 @@ func TestGetDataDir(t *testing.T) {
|
||||
|
||||
os.Setenv("BESZEL_AGENT_DATA_DIR", tempDir)
|
||||
|
||||
result, err := GetDataDir()
|
||||
result, err := getDataDir()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tempDir, result)
|
||||
})
|
||||
@@ -60,7 +60,7 @@ func TestGetDataDir(t *testing.T) {
|
||||
// Test with invalid explicit dataDir
|
||||
t.Run("invalid explicit data dir", func(t *testing.T) {
|
||||
invalidPath := "/invalid/path/that/cannot/be/created"
|
||||
_, err := GetDataDir(invalidPath)
|
||||
_, err := getDataDir(invalidPath)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
@@ -79,7 +79,7 @@ func TestGetDataDir(t *testing.T) {
|
||||
|
||||
// This will try platform-specific defaults, which may or may not work
|
||||
// We're mainly testing that it doesn't panic and returns some result
|
||||
result, err := GetDataDir()
|
||||
result, err := getDataDir()
|
||||
// We don't assert success/failure here since it depends on system permissions
|
||||
// Just verify we get a string result if no error
|
||||
if err == nil {
|
||||
|
||||
@@ -26,15 +26,6 @@ func parseFilesystemEntry(entry string) (device, customName string) {
|
||||
return device, customName
|
||||
}
|
||||
|
||||
func isDockerSpecialMountpoint(mountpoint string) bool {
|
||||
switch mountpoint {
|
||||
case "/etc/hosts", "/etc/resolv.conf", "/etc/hostname":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Sets up the filesystems to monitor for disk usage and I/O.
|
||||
func (a *Agent) initializeDiskInfo() {
|
||||
filesystem, _ := GetEnv("FILESYSTEM")
|
||||
@@ -78,15 +69,11 @@ 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.
|
||||
// Check if root device is in /proc/diskstats, use fallback if not
|
||||
if _, ioMatch = diskIoCounters[key]; !ioMatch {
|
||||
if matchedKey, match := findIoDevice(filesystem, diskIoCounters); match {
|
||||
key = matchedKey
|
||||
ioMatch = true
|
||||
} else {
|
||||
slog.Warn("Root I/O unmapped; set FILESYSTEM", "device", device, "mountpoint", mountpoint)
|
||||
key, ioMatch = findIoDevice(filesystem, diskIoCounters, a.fsStats)
|
||||
if !ioMatch {
|
||||
slog.Info("Using I/O fallback", "device", device, "mountpoint", mountpoint, "fallback", key)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -154,8 +141,8 @@ func (a *Agent) initializeDiskInfo() {
|
||||
for _, p := range partitions {
|
||||
// fmt.Println(p.Device, p.Mountpoint)
|
||||
// Binary root fallback or docker root fallback
|
||||
if !hasRoot && (p.Mountpoint == rootMountPoint || (isDockerSpecialMountpoint(p.Mountpoint) && strings.HasPrefix(p.Device, "/dev"))) {
|
||||
fs, match := findIoDevice(filepath.Base(p.Device), diskIoCounters)
|
||||
if !hasRoot && (p.Mountpoint == rootMountPoint || (p.Mountpoint == "/etc/hosts" && strings.HasPrefix(p.Device, "/dev"))) {
|
||||
fs, match := findIoDevice(filepath.Base(p.Device), diskIoCounters, a.fsStats)
|
||||
if match {
|
||||
addFsStat(fs, p.Mountpoint, true)
|
||||
hasRoot = true
|
||||
@@ -189,26 +176,33 @@ func (a *Agent) initializeDiskInfo() {
|
||||
|
||||
// If no root filesystem set, use fallback
|
||||
if !hasRoot {
|
||||
rootKey := filepath.Base(rootMountPoint)
|
||||
if _, exists := a.fsStats[rootKey]; exists {
|
||||
rootKey = "root"
|
||||
}
|
||||
slog.Warn("Root device not detected; root I/O disabled", "mountpoint", rootMountPoint)
|
||||
a.fsStats[rootKey] = &system.FsStats{Root: true, Mountpoint: rootMountPoint}
|
||||
rootDevice, _ := findIoDevice(filepath.Base(filesystem), diskIoCounters, a.fsStats)
|
||||
slog.Info("Root disk", "mountpoint", rootMountPoint, "io", rootDevice)
|
||||
a.fsStats[rootDevice] = &system.FsStats{Root: true, Mountpoint: rootMountPoint}
|
||||
}
|
||||
|
||||
a.initializeDiskIoStats(diskIoCounters)
|
||||
}
|
||||
|
||||
// Returns matching device from /proc/diskstats.
|
||||
// Returns matching device from /proc/diskstats,
|
||||
// or the device with the most reads if no match is found.
|
||||
// bool is true if a match was found.
|
||||
func findIoDevice(filesystem string, diskIoCounters map[string]disk.IOCountersStat) (string, bool) {
|
||||
func findIoDevice(filesystem string, diskIoCounters map[string]disk.IOCountersStat, fsStats map[string]*system.FsStats) (string, bool) {
|
||||
var maxReadBytes uint64
|
||||
maxReadDevice := "/"
|
||||
for _, d := range diskIoCounters {
|
||||
if d.Name == filesystem || (d.Label != "" && d.Label == filesystem) {
|
||||
return d.Name, true
|
||||
}
|
||||
if d.ReadBytes > maxReadBytes {
|
||||
// don't use if device already exists in fsStats
|
||||
if _, exists := fsStats[d.Name]; !exists {
|
||||
maxReadBytes = d.ReadBytes
|
||||
maxReadDevice = d.Name
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
return maxReadDevice, false
|
||||
}
|
||||
|
||||
// Sets start values for disk I/O stats.
|
||||
|
||||
@@ -94,62 +94,6 @@ func TestParseFilesystemEntry(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestFindIoDevice(t *testing.T) {
|
||||
t.Run("matches by device name", func(t *testing.T) {
|
||||
ioCounters := map[string]disk.IOCountersStat{
|
||||
"sda": {Name: "sda"},
|
||||
"sdb": {Name: "sdb"},
|
||||
}
|
||||
|
||||
device, ok := findIoDevice("sdb", ioCounters)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, "sdb", device)
|
||||
})
|
||||
|
||||
t.Run("matches by device label", func(t *testing.T) {
|
||||
ioCounters := map[string]disk.IOCountersStat{
|
||||
"sda": {Name: "sda", Label: "rootfs"},
|
||||
"sdb": {Name: "sdb"},
|
||||
}
|
||||
|
||||
device, ok := findIoDevice("rootfs", ioCounters)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, "sda", device)
|
||||
})
|
||||
|
||||
t.Run("returns no fallback when not found", func(t *testing.T) {
|
||||
ioCounters := map[string]disk.IOCountersStat{
|
||||
"sda": {Name: "sda"},
|
||||
"sdb": {Name: "sdb"},
|
||||
}
|
||||
|
||||
device, ok := findIoDevice("nvme0n1p1", ioCounters)
|
||||
assert.False(t, ok)
|
||||
assert.Equal(t, "", device)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIsDockerSpecialMountpoint(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
mountpoint string
|
||||
expected bool
|
||||
}{
|
||||
{name: "hosts", mountpoint: "/etc/hosts", expected: true},
|
||||
{name: "resolv", mountpoint: "/etc/resolv.conf", expected: true},
|
||||
{name: "hostname", mountpoint: "/etc/hostname", expected: true},
|
||||
{name: "root", mountpoint: "/", expected: false},
|
||||
{name: "passwd", mountpoint: "/etc/passwd", expected: false},
|
||||
{name: "extra-filesystem", mountpoint: "/extra-filesystems/sda1", expected: false},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
assert.Equal(t, tc.expected, isDockerSpecialMountpoint(tc.mountpoint))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInitializeDiskInfoWithCustomNames(t *testing.T) {
|
||||
// Set up environment variables
|
||||
oldEnv := os.Getenv("EXTRA_FILESYSTEMS")
|
||||
|
||||
@@ -32,10 +32,6 @@ var ansiEscapePattern = regexp.MustCompile(`\x1b\[[0-9;]*[a-zA-Z]|\x1b\][^\x07]*
|
||||
const (
|
||||
// Docker API timeout in milliseconds
|
||||
dockerTimeoutMs = 2100
|
||||
// Number of consecutive /containers/json failures before forcing a client reset on old Docker versions
|
||||
dockerClientResetFailureThreshold = 3
|
||||
// Minimum time between Docker client resets to avoid reset flapping
|
||||
dockerClientResetCooldown = 30 * time.Second
|
||||
// Maximum realistic network speed (5 GB/s) to detect bad deltas
|
||||
maxNetworkSpeedBps uint64 = 5e9
|
||||
// Maximum conceivable memory usage of a container (100TB) to detect bad memory stats
|
||||
@@ -59,16 +55,12 @@ type dockerManager struct {
|
||||
containerStatsMap map[string]*container.Stats // Keeps track of container stats
|
||||
validIds map[string]struct{} // Map of valid container ids, used to prune invalid containers from containerStatsMap
|
||||
goodDockerVersion bool // Whether docker version is at least 25.0.0 (one-shot works correctly)
|
||||
versionChecked bool // Whether docker version detection completed successfully
|
||||
isWindows bool // Whether the Docker Engine API is running on Windows
|
||||
buf *bytes.Buffer // Buffer to store and read response bodies
|
||||
decoder *json.Decoder // Reusable JSON decoder that reads from buf
|
||||
apiStats *container.ApiStats // Reusable API stats object
|
||||
excludeContainers []string // Patterns to exclude containers by name
|
||||
usingPodman bool // Whether the Docker Engine API is running on Podman
|
||||
transport *http.Transport // Base transport used by client for connection resets
|
||||
consecutiveListFailures int // Number of consecutive /containers/json request failures
|
||||
lastClientReset time.Time // Last time the Docker client connections were reset
|
||||
|
||||
// Cache-time-aware tracking for CPU stats (similar to cpu.go)
|
||||
// Maps cache time intervals to container-specific CPU usage tracking
|
||||
@@ -127,10 +119,8 @@ func (dm *dockerManager) shouldExcludeContainer(name string) bool {
|
||||
func (dm *dockerManager) getDockerStats(cacheTimeMs uint16) ([]*container.Stats, error) {
|
||||
resp, err := dm.client.Get("http://localhost/containers/json")
|
||||
if err != nil {
|
||||
dm.handleContainerListError(err)
|
||||
return nil, err
|
||||
}
|
||||
dm.consecutiveListFailures = 0
|
||||
|
||||
dm.apiContainerList = dm.apiContainerList[:0]
|
||||
if err := dm.decode(resp, &dm.apiContainerList); err != nil {
|
||||
@@ -214,50 +204,6 @@ func (dm *dockerManager) getDockerStats(cacheTimeMs uint16) ([]*container.Stats,
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
func (dm *dockerManager) handleContainerListError(err error) {
|
||||
dm.consecutiveListFailures++
|
||||
if !dm.shouldResetDockerClient(err) {
|
||||
return
|
||||
}
|
||||
dm.resetDockerClientConnections()
|
||||
}
|
||||
|
||||
func (dm *dockerManager) shouldResetDockerClient(err error) bool {
|
||||
if !dm.versionChecked || dm.goodDockerVersion {
|
||||
return false
|
||||
}
|
||||
if dm.consecutiveListFailures < dockerClientResetFailureThreshold {
|
||||
return false
|
||||
}
|
||||
if !dm.lastClientReset.IsZero() && time.Since(dm.lastClientReset) < dockerClientResetCooldown {
|
||||
return false
|
||||
}
|
||||
return isDockerApiOverloadError(err)
|
||||
}
|
||||
|
||||
func isDockerApiOverloadError(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
if errors.Is(err, context.DeadlineExceeded) {
|
||||
return true
|
||||
}
|
||||
msg := err.Error()
|
||||
return strings.Contains(msg, "Client.Timeout exceeded") ||
|
||||
strings.Contains(msg, "request canceled") ||
|
||||
strings.Contains(msg, "context deadline exceeded") ||
|
||||
strings.Contains(msg, "EOF")
|
||||
}
|
||||
|
||||
func (dm *dockerManager) resetDockerClientConnections() {
|
||||
if dm.transport == nil {
|
||||
return
|
||||
}
|
||||
dm.transport.CloseIdleConnections()
|
||||
dm.lastClientReset = time.Now()
|
||||
slog.Warn("Reset Docker client connections after repeated /containers/json failures", "failures", dm.consecutiveListFailures)
|
||||
}
|
||||
|
||||
// initializeCpuTracking initializes CPU tracking maps for a specific cache time interval
|
||||
func (dm *dockerManager) initializeCpuTracking(cacheTimeMs uint16) {
|
||||
// Initialize cache time maps if they don't exist
|
||||
@@ -607,7 +553,6 @@ func newDockerManager() *dockerManager {
|
||||
Timeout: timeout,
|
||||
Transport: userAgentTransport,
|
||||
},
|
||||
transport: transport,
|
||||
containerStatsMap: make(map[string]*container.Stats),
|
||||
sem: make(chan struct{}, 5),
|
||||
apiContainerList: []*container.ApiInfo{},
|
||||
@@ -666,7 +611,6 @@ func (dm *dockerManager) checkDockerVersion() {
|
||||
if err := dm.decode(resp, &versionInfo); err != nil {
|
||||
return
|
||||
}
|
||||
dm.versionChecked = true
|
||||
// if version > 24, one-shot works correctly and we can limit concurrent operations
|
||||
if dockerVersion, err := semver.Parse(versionInfo.Version); err == nil && dockerVersion.Major > 24 {
|
||||
dm.goodDockerVersion = true
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/shirou/gopsutil/v4/cpu"
|
||||
"github.com/shirou/gopsutil/v4/host"
|
||||
)
|
||||
|
||||
const fingerprintFileName = "fingerprint"
|
||||
|
||||
// knownBadUUID is a commonly known "product_uuid" that is not unique across systems.
|
||||
const knownBadUUID = "03000200-0400-0500-0006-000700080009"
|
||||
|
||||
// GetFingerprint returns the agent fingerprint. It first tries to read a saved
|
||||
// fingerprint from the data directory. If not found (or dataDir is empty), it
|
||||
// generates one from system properties. The hostname and cpuModel parameters are
|
||||
// used as fallback material if host.HostID() fails. If either is empty, they
|
||||
// are fetched from the system automatically.
|
||||
//
|
||||
// If a new fingerprint is generated and a dataDir is provided, it is saved.
|
||||
func GetFingerprint(dataDir, hostname, cpuModel string) string {
|
||||
if dataDir != "" {
|
||||
if fp, err := readFingerprint(dataDir); err == nil {
|
||||
return fp
|
||||
}
|
||||
}
|
||||
fp := generateFingerprint(hostname, cpuModel)
|
||||
if dataDir != "" {
|
||||
_ = SaveFingerprint(dataDir, fp)
|
||||
}
|
||||
return fp
|
||||
}
|
||||
|
||||
// generateFingerprint creates a fingerprint from system properties.
|
||||
// It tries host.HostID() first, falling back to hostname + cpuModel.
|
||||
// If hostname or cpuModel are empty, they are fetched from the system.
|
||||
func generateFingerprint(hostname, cpuModel string) string {
|
||||
fingerprint, err := host.HostID()
|
||||
if err != nil || fingerprint == "" || fingerprint == knownBadUUID {
|
||||
if hostname == "" {
|
||||
hostname, _ = os.Hostname()
|
||||
}
|
||||
if cpuModel == "" {
|
||||
if info, err := cpu.Info(); err == nil && len(info) > 0 {
|
||||
cpuModel = info[0].ModelName
|
||||
}
|
||||
}
|
||||
fingerprint = hostname + cpuModel
|
||||
}
|
||||
|
||||
sum := sha256.Sum256([]byte(fingerprint))
|
||||
return hex.EncodeToString(sum[:24])
|
||||
}
|
||||
|
||||
// readFingerprint reads the saved fingerprint from the data directory.
|
||||
func readFingerprint(dataDir string) (string, error) {
|
||||
fp, err := os.ReadFile(filepath.Join(dataDir, fingerprintFileName))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
s := strings.TrimSpace(string(fp))
|
||||
if s == "" {
|
||||
return "", errors.New("fingerprint file is empty")
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// SaveFingerprint writes the fingerprint to the data directory.
|
||||
func SaveFingerprint(dataDir, fingerprint string) error {
|
||||
return os.WriteFile(filepath.Join(dataDir, fingerprintFileName), []byte(fingerprint), 0o644)
|
||||
}
|
||||
|
||||
// DeleteFingerprint removes the saved fingerprint file from the data directory.
|
||||
// Returns nil if the file does not exist (idempotent).
|
||||
func DeleteFingerprint(dataDir string) error {
|
||||
err := os.Remove(filepath.Join(dataDir, fingerprintFileName))
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
//go:build testing
|
||||
// +build testing
|
||||
|
||||
package agent
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGetFingerprint(t *testing.T) {
|
||||
t.Run("reads existing fingerprint from file", func(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
expected := "abc123def456"
|
||||
err := os.WriteFile(filepath.Join(dir, fingerprintFileName), []byte(expected), 0644)
|
||||
require.NoError(t, err)
|
||||
|
||||
fp := GetFingerprint(dir, "", "")
|
||||
assert.Equal(t, expected, fp)
|
||||
})
|
||||
|
||||
t.Run("trims whitespace from file", func(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
err := os.WriteFile(filepath.Join(dir, fingerprintFileName), []byte(" abc123 \n"), 0644)
|
||||
require.NoError(t, err)
|
||||
|
||||
fp := GetFingerprint(dir, "", "")
|
||||
assert.Equal(t, "abc123", fp)
|
||||
})
|
||||
|
||||
t.Run("generates fingerprint when file does not exist", func(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
fp := GetFingerprint(dir, "", "")
|
||||
assert.NotEmpty(t, fp)
|
||||
})
|
||||
|
||||
t.Run("generates fingerprint when dataDir is empty", func(t *testing.T) {
|
||||
fp := GetFingerprint("", "", "")
|
||||
assert.NotEmpty(t, fp)
|
||||
})
|
||||
|
||||
t.Run("generates consistent fingerprint for same inputs", func(t *testing.T) {
|
||||
fp1 := GetFingerprint("", "myhost", "mycpu")
|
||||
fp2 := GetFingerprint("", "myhost", "mycpu")
|
||||
assert.Equal(t, fp1, fp2)
|
||||
})
|
||||
|
||||
t.Run("prefers saved fingerprint over generated", func(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
require.NoError(t, SaveFingerprint(dir, "saved-fp"))
|
||||
|
||||
fp := GetFingerprint(dir, "anyhost", "anycpu")
|
||||
assert.Equal(t, "saved-fp", fp)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSaveFingerprint(t *testing.T) {
|
||||
t.Run("saves fingerprint to file", func(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
err := SaveFingerprint(dir, "abc123")
|
||||
require.NoError(t, err)
|
||||
|
||||
content, err := os.ReadFile(filepath.Join(dir, fingerprintFileName))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "abc123", string(content))
|
||||
})
|
||||
|
||||
t.Run("overwrites existing fingerprint", func(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
require.NoError(t, SaveFingerprint(dir, "old"))
|
||||
require.NoError(t, SaveFingerprint(dir, "new"))
|
||||
|
||||
content, err := os.ReadFile(filepath.Join(dir, fingerprintFileName))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "new", string(content))
|
||||
})
|
||||
}
|
||||
|
||||
func TestDeleteFingerprint(t *testing.T) {
|
||||
t.Run("deletes existing fingerprint", func(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
fp := filepath.Join(dir, fingerprintFileName)
|
||||
err := os.WriteFile(fp, []byte("abc123"), 0644)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = DeleteFingerprint(dir)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify file is gone
|
||||
_, err = os.Stat(fp)
|
||||
assert.True(t, os.IsNotExist(err))
|
||||
})
|
||||
|
||||
t.Run("no error when file does not exist", func(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
err := DeleteFingerprint(dir)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
}
|
||||
@@ -36,9 +36,6 @@ var hubVersions map[string]semver.Version
|
||||
// and begins listening for connections. Returns an error if the server
|
||||
// is already running or if there's an issue starting the server.
|
||||
func (a *Agent) StartServer(opts ServerOptions) error {
|
||||
if disableSSH, _ := GetEnv("DISABLE_SSH"); disableSSH == "true" {
|
||||
return errors.New("SSH disabled")
|
||||
}
|
||||
if a.server != nil {
|
||||
return errors.New("server already started")
|
||||
}
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
//go:build testing
|
||||
// +build testing
|
||||
|
||||
package agent
|
||||
|
||||
import (
|
||||
@@ -183,23 +180,6 @@ func TestStartServer(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestStartServerDisableSSH(t *testing.T) {
|
||||
os.Setenv("BESZEL_AGENT_DISABLE_SSH", "true")
|
||||
defer os.Unsetenv("BESZEL_AGENT_DISABLE_SSH")
|
||||
|
||||
agent, err := NewAgent("")
|
||||
require.NoError(t, err)
|
||||
|
||||
opts := ServerOptions{
|
||||
Network: "tcp",
|
||||
Addr: ":45990",
|
||||
}
|
||||
|
||||
err = agent.StartServer(opts)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "SSH disabled")
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
//////////////////// ParseKeys Tests ////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
@@ -19,6 +18,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/henrygd/beszel/internal/entities/smart"
|
||||
|
||||
"log/slog"
|
||||
)
|
||||
|
||||
// SmartManager manages data collection for SMART devices
|
||||
@@ -1124,6 +1125,7 @@ func NewSmartManager() (*SmartManager, error) {
|
||||
sm.refreshExcludedDevices()
|
||||
path, err := sm.detectSmartctl()
|
||||
if err != nil {
|
||||
slog.Debug(err.Error())
|
||||
return nil, err
|
||||
}
|
||||
slog.Debug("smartctl", "path", path)
|
||||
|
||||
@@ -63,9 +63,9 @@ func detectRestarter() restarter {
|
||||
if path, err := exec.LookPath("rc-service"); err == nil {
|
||||
return &openRCRestarter{cmd: path}
|
||||
}
|
||||
if path, err := exec.LookPath("procd"); err == nil {
|
||||
return &openWRTRestarter{cmd: path}
|
||||
}
|
||||
if path, err := exec.LookPath("procd"); err == nil {
|
||||
return &openWRTRestarter{cmd: path}
|
||||
}
|
||||
if path, err := exec.LookPath("service"); err == nil {
|
||||
if runtime.GOOS == "freebsd" {
|
||||
return &freeBSDRestarter{cmd: path}
|
||||
@@ -79,7 +79,7 @@ func detectRestarter() restarter {
|
||||
func Update(useMirror bool) error {
|
||||
exePath, _ := os.Executable()
|
||||
|
||||
dataDir, err := GetDataDir()
|
||||
dataDir, err := getDataDir()
|
||||
if err != nil {
|
||||
dataDir = os.TempDir()
|
||||
}
|
||||
@@ -125,3 +125,4 @@ func Update(useMirror bool) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -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.2"
|
||||
// AppName is the name of the application.
|
||||
AppName = "beszel"
|
||||
)
|
||||
|
||||
26
go.mod
26
go.mod
@@ -1,10 +1,10 @@
|
||||
module github.com/henrygd/beszel
|
||||
|
||||
go 1.25.7
|
||||
go 1.25.5
|
||||
|
||||
require (
|
||||
github.com/blang/semver v3.5.1+incompatible
|
||||
github.com/coreos/go-systemd/v22 v22.7.0
|
||||
github.com/coreos/go-systemd/v22 v22.6.0
|
||||
github.com/distatus/battery v0.11.0
|
||||
github.com/ebitengine/purego v0.9.1
|
||||
github.com/fxamacker/cbor/v2 v2.9.0
|
||||
@@ -13,14 +13,14 @@ require (
|
||||
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/shirou/gopsutil/v4 v4.26.1
|
||||
github.com/pocketbase/pocketbase v0.35.1
|
||||
github.com/shirou/gopsutil/v4 v4.25.12
|
||||
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/crypto v0.46.0
|
||||
golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93
|
||||
golang.org/x/sys v0.40.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
@@ -34,15 +34,15 @@ require (
|
||||
github.com/domodwyer/mailyak/v3 v3.6.2 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/fatih/color v1.18.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.13 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.12 // indirect
|
||||
github.com/ganigeorgiev/fexpr v0.5.0 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/go-ozzo/ozzo-validation/v4 v4.3.0 // indirect
|
||||
github.com/go-sql-driver/mysql v1.9.1 // indirect
|
||||
github.com/godbus/dbus/v5 v5.2.2 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/klauspost/compress v1.18.3 // indirect
|
||||
github.com/klauspost/compress v1.18.2 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // 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/image v0.34.0 // indirect
|
||||
golang.org/x/net v0.48.0 // indirect
|
||||
golang.org/x/oauth2 v0.34.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
|
||||
howett.net/plist v1.0.1 // indirect
|
||||
modernc.org/libc v1.67.6 // indirect
|
||||
modernc.org/libc v1.66.10 // 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.43.0 // indirect
|
||||
)
|
||||
|
||||
62
go.sum
62
go.sum
@@ -9,8 +9,8 @@ github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3d
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
||||
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
|
||||
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||
github.com/coreos/go-systemd/v22 v22.7.0 h1:LAEzFkke61DFROc7zNLX/WA2i5J8gYqe0rSj9KI28KA=
|
||||
github.com/coreos/go-systemd/v22 v22.7.0/go.mod h1:xNUYtjHu2EDXbsxz1i41wouACIwT7Ybq9o0BQhMwD0w=
|
||||
github.com/coreos/go-systemd/v22 v22.6.0 h1:aGVa/v8B7hpb0TKl0MWoAavPDmHvobFe5R5zn0bCJWo=
|
||||
github.com/coreos/go-systemd/v22 v22.6.0/go.mod h1:iG+pp635Fo7ZmV/j14KUcmEyWF+0X7Lua8rrTWzYgWU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
@@ -33,8 +33,8 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
|
||||
github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
|
||||
github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM=
|
||||
github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
||||
github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw=
|
||||
github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
||||
github.com/ganigeorgiev/fexpr v0.5.0 h1:XA9JxtTE/Xm+g/JFI6RfZEHSiQlk+1glLvRK1Lpv/Tk=
|
||||
github.com/ganigeorgiev/fexpr v0.5.0/go.mod h1:RyGiGqmeXhEQ6+mlGdnUleLHgtzzu/VGO2WtJkF5drE=
|
||||
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
|
||||
@@ -53,13 +53,13 @@ github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1v
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/godbus/dbus/v5 v5.2.2 h1:TUR3TgtSVDmjiXOgAAyaZbYmIeP3DPkld3jgKGV8mXQ=
|
||||
github.com/godbus/dbus/v5 v5.2.2/go.mod h1:3AAv2+hPq5rdnr5txxxRwiGjPXamgoIHgz9FPBfOp3c=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83 h1:z2ogiKUYzX5Is6zr/vP9vJGqPwcdqsWjOt+V8J7+bTc=
|
||||
github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI=
|
||||
github.com/google/pprof v0.0.0-20251114195745-4902fdda35c8 h1:3DsUAV+VNEQa2CUVLxCY3f87278uWfIDhJnbdvDjvmE=
|
||||
github.com/google/pprof v0.0.0-20251114195745-4902fdda35c8/go.mod h1:I6V7YzU0XDpsHqbsyrghnFZLO1gwK6NPTNvmetQIk9U=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||
@@ -69,8 +69,8 @@ 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.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=
|
||||
github.com/klauspost/compress v1.18.2/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=
|
||||
@@ -96,8 +96,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
|
||||
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/pocketbase v0.35.1 h1:Cd5ivUThTw29myY/tYa2cb0elkScBMseG6fExZsIQB8=
|
||||
github.com/pocketbase/pocketbase v0.35.1/go.mod h1:yQnh1o1Aq6wVuqcmZbRbDmIhc31AME/F5pnPR0Bdtmg=
|
||||
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=
|
||||
@@ -105,8 +105,8 @@ github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qq
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/shirou/gopsutil/v4 v4.26.1 h1:TOkEyriIXk2HX9d4isZJtbjXbEjf5qyKPAzbzY0JWSo=
|
||||
github.com/shirou/gopsutil/v4 v4.26.1/go.mod h1:medLI9/UNAb0dOI9Q3/7yWSqKkj00u+1tgY8nvv41pc=
|
||||
github.com/shirou/gopsutil/v4 v4.25.12 h1:e7PvW/0RmJ8p8vPGJH4jvNkOyLmbkXgXW4m6ZPic6CY=
|
||||
github.com/shirou/gopsutil/v4 v4.25.12/go.mod h1:EivAfP5x2EhLp2ovdpKSozecVXn1TmuG7SMzs/Wh4PU=
|
||||
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
|
||||
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
|
||||
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
|
||||
@@ -129,18 +129,18 @@ 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.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
|
||||
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
|
||||
golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0=
|
||||
golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU=
|
||||
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.34.0 h1:33gCkyw9hmwbZJeZkct8XyR11yH889EQt/QH4VmXMn8=
|
||||
golang.org/x/image v0.34.0/go.mod h1:2RNFBZRB+vnwwFil8GkMdRvrJOFd1AzdZI6vOY+eJVU=
|
||||
golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=
|
||||
golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=
|
||||
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/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
|
||||
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
||||
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/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
@@ -159,8 +159,8 @@ 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/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.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA=
|
||||
golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc=
|
||||
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=
|
||||
@@ -185,8 +185,10 @@ modernc.org/gc/v3 v3.1.1 h1:k8T3gkXWY9sEiytKhcgyiZ2L0DTyCQ/nvX+LoCljoRE=
|
||||
modernc.org/gc/v3 v3.1.1/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
|
||||
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
|
||||
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
|
||||
modernc.org/libc v1.67.6 h1:eVOQvpModVLKOdT+LvBPjdQqfrZq+pC39BygcT+E7OI=
|
||||
modernc.org/libc v1.67.6/go.mod h1:JAhxUVlolfYDErnwiqaLvUqc8nfb2r6S6slAgZOnaiE=
|
||||
modernc.org/libc v1.66.10 h1:yZkb3YeLx4oynyR+iUsXsybsX4Ubx7MQlSYEw4yj59A=
|
||||
modernc.org/libc v1.66.10/go.mod h1:8vGSEwvoUoltr4dlywvHqjtAqHBaw0j1jI7iFBTAr2I=
|
||||
modernc.org/libc v1.67.4 h1:zZGmCMUVPORtKv95c2ReQN5VDjvkoRm9GWPTEPuvlWg=
|
||||
modernc.org/libc v1.67.4/go.mod h1:QvvnnJ5P7aitu0ReNpVIEyesuhmDLQ8kaEoyMjIFZJA=
|
||||
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
|
||||
@@ -195,8 +197,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.43.0 h1:8YqiFx3G1VhHTXO2Q00bl1Wz9KhS9Q5okwfp9Y97VnA=
|
||||
modernc.org/sqlite v1.43.0/go.mod h1:+VkC6v3pLOAE0A0uVucQEcbVW0I5nHCeDaBf+DpsQT8=
|
||||
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=
|
||||
|
||||
@@ -31,6 +31,9 @@ func (opts *cmdOptions) parse() bool {
|
||||
|
||||
// Subcommands that don't require any pflag parsing
|
||||
switch subcommand {
|
||||
case "-v", "version":
|
||||
fmt.Println(beszel.AppName+"-agent", beszel.Version)
|
||||
return true
|
||||
case "health":
|
||||
err := health.Check()
|
||||
if err != nil {
|
||||
@@ -38,9 +41,6 @@ func (opts *cmdOptions) parse() bool {
|
||||
}
|
||||
fmt.Print("ok")
|
||||
return true
|
||||
case "fingerprint":
|
||||
handleFingerprint()
|
||||
return true
|
||||
}
|
||||
|
||||
// pflag.CommandLine.ParseErrorsWhitelist.UnknownFlags = true
|
||||
@@ -49,7 +49,6 @@ func (opts *cmdOptions) parse() bool {
|
||||
pflag.StringVarP(&opts.hubURL, "url", "u", "", "URL of the Beszel hub")
|
||||
pflag.StringVarP(&opts.token, "token", "t", "", "Token to use for authentication")
|
||||
chinaMirrors := pflag.BoolP("china-mirrors", "c", false, "Use mirror for update (gh.beszel.dev) instead of GitHub")
|
||||
version := pflag.BoolP("version", "v", false, "Show version information")
|
||||
help := pflag.BoolP("help", "h", false, "Show this help message")
|
||||
|
||||
// Convert old single-dash long flags to double-dash for backward compatibility
|
||||
@@ -74,9 +73,9 @@ func (opts *cmdOptions) parse() bool {
|
||||
builder.WriteString(os.Args[0])
|
||||
builder.WriteString(" [command] [flags]\n")
|
||||
builder.WriteString("\nCommands:\n")
|
||||
builder.WriteString(" fingerprint View or reset the agent fingerprint\n")
|
||||
builder.WriteString(" health Check if the agent is running\n")
|
||||
builder.WriteString(" update Update to the latest version\n")
|
||||
builder.WriteString(" health Check if the agent is running\n")
|
||||
// builder.WriteString(" help Display this help message\n")
|
||||
builder.WriteString(" update Update to the latest version\n")
|
||||
builder.WriteString("\nFlags:\n")
|
||||
fmt.Print(builder.String())
|
||||
pflag.PrintDefaults()
|
||||
@@ -87,9 +86,6 @@ func (opts *cmdOptions) parse() bool {
|
||||
|
||||
// Must run after pflag.Parse()
|
||||
switch {
|
||||
case *version:
|
||||
fmt.Println(beszel.AppName+"-agent", beszel.Version)
|
||||
return true
|
||||
case *help || subcommand == "help":
|
||||
pflag.Usage()
|
||||
return true
|
||||
@@ -137,38 +133,6 @@ func (opts *cmdOptions) getAddress() string {
|
||||
return agent.GetAddress(opts.listen)
|
||||
}
|
||||
|
||||
// handleFingerprint handles the "fingerprint" command with subcommands "view" and "reset".
|
||||
func handleFingerprint() {
|
||||
subCmd := ""
|
||||
if len(os.Args) > 2 {
|
||||
subCmd = os.Args[2]
|
||||
}
|
||||
|
||||
switch subCmd {
|
||||
case "", "view":
|
||||
dataDir, _ := agent.GetDataDir()
|
||||
fp := agent.GetFingerprint(dataDir, "", "")
|
||||
fmt.Println(fp)
|
||||
case "help", "-h", "--help":
|
||||
fmt.Print(fingerprintUsage())
|
||||
case "reset":
|
||||
dataDir, err := agent.GetDataDir()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if err := agent.DeleteFingerprint(dataDir); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println("Fingerprint reset. A new one will be generated on next start.")
|
||||
default:
|
||||
log.Fatalf("Unknown command: %q\n\n%s", subCmd, fingerprintUsage())
|
||||
}
|
||||
}
|
||||
|
||||
func fingerprintUsage() string {
|
||||
return fmt.Sprintf("Usage: %s fingerprint [view|reset]\n\nCommands:\n view Print fingerprint (default)\n reset Reset saved fingerprint\n", os.Args[0])
|
||||
}
|
||||
|
||||
func main() {
|
||||
var opts cmdOptions
|
||||
subcommandHandled := opts.parse()
|
||||
|
||||
@@ -190,8 +190,6 @@ func (rm *RecordManager) AverageSystemStats(db dbx.Builder, records RecordIds) *
|
||||
id := record.Id
|
||||
// clear global statsRecord for reuse
|
||||
statsRecord.Stats = statsRecord.Stats[:0]
|
||||
// reset tempStats each iteration to avoid omitzero fields retaining stale values
|
||||
*stats = system.Stats{}
|
||||
|
||||
queryParams["id"] = id
|
||||
db.NewQuery("SELECT stats FROM system_stats WHERE id = {:id}").Bind(queryParams).One(&statsRecord)
|
||||
@@ -446,11 +444,9 @@ func (rm *RecordManager) AverageContainerStats(db dbx.Builder, records RecordIds
|
||||
|
||||
for i := range records {
|
||||
id := records[i].Id
|
||||
// clear global statsRecord for reuse
|
||||
// clear global statsRecord and containerStats for reuse
|
||||
statsRecord.Stats = statsRecord.Stats[:0]
|
||||
// must set to nil (not [:0]) to avoid json.Unmarshal reusing backing array
|
||||
// which causes omitzero fields to inherit stale values from previous iterations
|
||||
containerStats = nil
|
||||
containerStats = containerStats[:0]
|
||||
|
||||
queryParams["id"] = id
|
||||
db.NewQuery("SELECT stats FROM container_stats WHERE id = {:id}").Bind(queryParams).One(&statsRecord)
|
||||
|
||||
58
internal/site/package-lock.json
generated
58
internal/site/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "beszel",
|
||||
"version": "0.18.3",
|
||||
"version": "0.18.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "beszel",
|
||||
"version": "0.18.3",
|
||||
"version": "0.18.2",
|
||||
"dependencies": {
|
||||
"@henrygd/queue": "^1.0.7",
|
||||
"@henrygd/semaphore": "^0.0.2",
|
||||
@@ -111,6 +111,7 @@
|
||||
"integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@ampproject/remapping": "^2.2.0",
|
||||
"@babel/code-frame": "^7.27.1",
|
||||
@@ -1137,6 +1138,7 @@
|
||||
"integrity": "sha512-9IO+PDvdneY8OCI8zvI1oDXpzryTMtyRv7uq9O0U1mFCvIPVd5dWQKQDu/CpgpYAc2+JG/izn5PNl9xzPc6ckw==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.20.12",
|
||||
"@babel/runtime": "^7.20.13",
|
||||
@@ -1290,6 +1292,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@lingui/core/-/core-5.4.1.tgz",
|
||||
"integrity": "sha512-4FeIh56PH5vziPg2BYo4XYWWOHE4XaY/XR8Jakwn0/qwtLpydWMNVpZOpGWi7nfPZtcLaJLmZKup6UNxEl1Pfw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@lingui/message-utils": "5.4.1"
|
||||
@@ -3485,6 +3488,7 @@
|
||||
"integrity": "sha512-lr3jdBw/BGj49Eps7EvqlUaoeA0xpj3pc0RoJkHpYaCHkVK7i28dKyImLQb3JVlqs3aYSXf7qYuWOW/fgZnTXQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
@@ -3495,6 +3499,7 @@
|
||||
"integrity": "sha512-i5ZzwYpqjmrKenzkoLM2Ibzt6mAsM7pxB6BCIouEVVmgiqaMj1TjaK7hnA36hbW5aZv20kx7Lw6hWzPWg0Rurw==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"peerDependencies": {
|
||||
"@types/react": "^19.0.0"
|
||||
}
|
||||
@@ -3699,6 +3704,7 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"caniuse-lite": "^1.0.30001726",
|
||||
"electron-to-chromium": "^1.5.173",
|
||||
@@ -5072,9 +5078,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash": {
|
||||
"version": "4.17.23",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
|
||||
"integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.sortby": {
|
||||
@@ -5316,9 +5322,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/minizlib": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz",
|
||||
"integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==",
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz",
|
||||
"integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -5328,6 +5334,22 @@
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/mkdirp": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz",
|
||||
"integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"mkdirp": "dist/cjs/src/bin.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/moo": {
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmjs.org/moo/-/moo-0.5.2.tgz",
|
||||
@@ -5371,6 +5393,7 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^18.0.0 || >=20.0.0"
|
||||
}
|
||||
@@ -5580,6 +5603,7 @@
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -5725,6 +5749,7 @@
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-19.1.2.tgz",
|
||||
"integrity": "sha512-MdWVitvLbQULD+4DP8GYjZUrepGW7d+GQkNVqJEzNxE+e9WIa4egVFE/RDfVb1u9u/Jw7dNMmPB4IqxzbFYJ0w==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -5734,6 +5759,7 @@
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.2.tgz",
|
||||
"integrity": "sha512-dEoydsCp50i7kS1xHOmPXq4zQYoGWedUsvqv9H6zdif2r7yLHygyfP9qou71TulRN0d6ng9EbRVsQhSqfUc19g==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"scheduler": "^0.26.0"
|
||||
},
|
||||
@@ -6273,7 +6299,8 @@
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.12.tgz",
|
||||
"integrity": "sha512-DzFtxOi+7NsFf7DBtI3BJsynR+0Yp6etH+nRPTbpWnS2pZBaSksv/JGctNwSWzbFjp0vxSqknaUylseZqMDGrA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/tapable": {
|
||||
"version": "2.2.3",
|
||||
@@ -6290,16 +6317,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/tar": {
|
||||
"version": "7.5.7",
|
||||
"resolved": "https://registry.npmjs.org/tar/-/tar-7.5.7.tgz",
|
||||
"integrity": "sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ==",
|
||||
"version": "7.4.3",
|
||||
"resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz",
|
||||
"integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@isaacs/fs-minipass": "^4.0.0",
|
||||
"chownr": "^3.0.0",
|
||||
"minipass": "^7.1.2",
|
||||
"minizlib": "^3.1.0",
|
||||
"minizlib": "^3.0.1",
|
||||
"mkdirp": "^3.0.1",
|
||||
"yallist": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -6394,6 +6422,7 @@
|
||||
"integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
@@ -6628,6 +6657,7 @@
|
||||
"integrity": "sha512-uzcxnSDVjAopEUjljkWh8EIrg6tlzrjFUfMcR1EVsRDGwf/ccef0qQPRyOrROwhrTDaApueq+ja+KLPlzR/zdg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.25.0",
|
||||
"fdir": "^6.5.0",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "beszel",
|
||||
"private": true,
|
||||
"version": "0.18.3",
|
||||
"version": "0.18.2",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite --host",
|
||||
|
||||
@@ -38,23 +38,6 @@ export default memo(function ContainerChart({
|
||||
|
||||
const isNetChart = chartType === ChartType.Network
|
||||
|
||||
// Filter with set lookup
|
||||
const filteredKeys = useMemo(() => {
|
||||
if (!filter) {
|
||||
return new Set<string>()
|
||||
}
|
||||
const filterTerms = filter
|
||||
.toLowerCase()
|
||||
.split(" ")
|
||||
.filter((term) => term.length > 0)
|
||||
return new Set(
|
||||
Object.keys(chartConfig).filter((key) => {
|
||||
const keyLower = key.toLowerCase()
|
||||
return !filterTerms.some((term) => keyLower.includes(term))
|
||||
})
|
||||
)
|
||||
}, [chartConfig, filter])
|
||||
|
||||
// biome-ignore lint/correctness/useExhaustiveDependencies: not necessary
|
||||
const { toolTipFormatter, dataFunction, tickFormatter } = useMemo(() => {
|
||||
const obj = {} as {
|
||||
@@ -102,14 +85,10 @@ export default memo(function ContainerChart({
|
||||
let totalSent = 0
|
||||
let totalRecv = 0
|
||||
const payloadData = item?.payload && typeof item.payload === "object" ? item.payload : {}
|
||||
for (const [containerKey, value] of Object.entries(payloadData)) {
|
||||
for (const value of Object.values(payloadData)) {
|
||||
if (!value || typeof value !== "object") {
|
||||
continue
|
||||
}
|
||||
// Skip filtered out containers
|
||||
if (filteredKeys.has(containerKey)) {
|
||||
continue
|
||||
}
|
||||
const [sent, recv] = getRxTxBytes(value as { b?: [number, number]; ns?: number; nr?: number })
|
||||
totalSent += sent
|
||||
totalRecv += recv
|
||||
@@ -145,7 +124,24 @@ export default memo(function ContainerChart({
|
||||
obj.dataFunction = (key: string, data: any) => data[key]?.[dataKey] ?? null
|
||||
}
|
||||
return obj
|
||||
}, [filteredKeys])
|
||||
}, [])
|
||||
|
||||
// Filter with set lookup
|
||||
const filteredKeys = useMemo(() => {
|
||||
if (!filter) {
|
||||
return new Set<string>()
|
||||
}
|
||||
const filterTerms = filter
|
||||
.toLowerCase()
|
||||
.split(" ")
|
||||
.filter((term) => term.length > 0)
|
||||
return new Set(
|
||||
Object.keys(chartConfig).filter((key) => {
|
||||
const keyLower = key.toLowerCase()
|
||||
return !filterTerms.some((term) => keyLower.includes(term))
|
||||
})
|
||||
)
|
||||
}, [chartConfig, filter])
|
||||
|
||||
// console.log('rendered at', new Date())
|
||||
|
||||
|
||||
@@ -75,7 +75,9 @@ export default function Navbar() {
|
||||
<HardDriveIcon className="h-[1.2rem] w-[1.2rem]" strokeWidth={1.5} />
|
||||
</Link>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>S.M.A.R.T.</TooltipContent>
|
||||
<TooltipContent>
|
||||
<Trans>S.M.A.R.T.</Trans>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
<LangToggle />
|
||||
<ModeToggle />
|
||||
|
||||
@@ -8,7 +8,6 @@ import type { ClassValue } from "clsx"
|
||||
import {
|
||||
ArrowUpDownIcon,
|
||||
ChevronRightSquareIcon,
|
||||
ClockArrowUp,
|
||||
CopyIcon,
|
||||
CpuIcon,
|
||||
HardDriveIcon,
|
||||
@@ -35,7 +34,6 @@ import {
|
||||
formatTemperature,
|
||||
getMeterState,
|
||||
parseSemVer,
|
||||
secondsToString,
|
||||
} from "@/lib/utils"
|
||||
import { batteryStateTranslations } from "@/lib/i18n"
|
||||
import type { SystemRecord } from "@/types"
|
||||
@@ -375,29 +373,6 @@ export function SystemsTableColumns(viewMode: "table" | "grid"): ColumnDef<Syste
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorFn: ({ info }) => info.u || undefined,
|
||||
id: "uptime",
|
||||
name: () => t`Uptime`,
|
||||
size: 50,
|
||||
Icon: ClockArrowUp,
|
||||
header: sortableHeader,
|
||||
cell(info) {
|
||||
const uptime = info.getValue() as number
|
||||
if (!uptime) {
|
||||
return null
|
||||
}
|
||||
let formatted: string
|
||||
if (uptime < 3600) {
|
||||
formatted = secondsToString(uptime, "minute")
|
||||
} else if (uptime < 360000) {
|
||||
formatted = secondsToString(uptime, "hour")
|
||||
} else {
|
||||
formatted = secondsToString(uptime, "day")
|
||||
}
|
||||
return <span className="tabular-nums whitespace-nowrap">{formatted}</span>
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorFn: ({ info }) => info.v,
|
||||
id: "agent",
|
||||
|
||||
@@ -157,7 +157,6 @@ msgstr "التنبيهات"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/containers.tsx
|
||||
msgid "All Containers"
|
||||
msgstr "جميع الحاويات"
|
||||
@@ -838,7 +837,6 @@ msgstr "غير نشط"
|
||||
msgid "Invalid email address."
|
||||
msgstr "عنوان البريد الإشباكي غير صالح."
|
||||
|
||||
#: src/components/lang-toggle.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "اللغة"
|
||||
@@ -1337,7 +1335,6 @@ msgstr "تعيين عتبات النسبة المئوية لألوان العد
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Settings"
|
||||
@@ -1489,7 +1486,6 @@ msgstr "إلى البريد الإشباكي"
|
||||
msgid "Toggle grid"
|
||||
msgstr "تبديل الشبكة"
|
||||
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Toggle theme"
|
||||
msgstr "تبديل السمة"
|
||||
@@ -1745,3 +1741,4 @@ msgstr "نعم"
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Your user settings have been updated."
|
||||
msgstr "تم تحديث إعدادات المستخدم الخاصة بك."
|
||||
|
||||
|
||||
@@ -157,7 +157,6 @@ msgstr "Тревоги"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/containers.tsx
|
||||
msgid "All Containers"
|
||||
msgstr "Всички контейнери"
|
||||
@@ -838,7 +837,6 @@ msgstr "Неактивен"
|
||||
msgid "Invalid email address."
|
||||
msgstr "Невалиден имейл адрес."
|
||||
|
||||
#: src/components/lang-toggle.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "Език"
|
||||
@@ -1337,7 +1335,6 @@ msgstr "Задайте процентни прагове за цветовете
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Settings"
|
||||
@@ -1489,7 +1486,6 @@ msgstr "До имейл(ите)"
|
||||
msgid "Toggle grid"
|
||||
msgstr "Превключване на мрежа"
|
||||
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Toggle theme"
|
||||
msgstr "Включи тема"
|
||||
@@ -1745,3 +1741,4 @@ msgstr "Да"
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Your user settings have been updated."
|
||||
msgstr "Настройките за потребителя ти са обновени."
|
||||
|
||||
|
||||
@@ -157,7 +157,6 @@ msgstr "Výstrahy"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/containers.tsx
|
||||
msgid "All Containers"
|
||||
msgstr "Všechny kontejnery"
|
||||
@@ -838,7 +837,6 @@ msgstr "Neaktivní"
|
||||
msgid "Invalid email address."
|
||||
msgstr "Neplatná e-mailová adresa."
|
||||
|
||||
#: src/components/lang-toggle.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "Jazyk"
|
||||
@@ -1337,7 +1335,6 @@ msgstr "Nastavte procentuální prahové hodnoty pro barvy měřičů."
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Settings"
|
||||
@@ -1489,7 +1486,6 @@ msgstr "Na email(y)"
|
||||
msgid "Toggle grid"
|
||||
msgstr "Přepnout mřížku"
|
||||
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Toggle theme"
|
||||
msgstr "Přepnout motiv"
|
||||
@@ -1745,3 +1741,4 @@ msgstr "Ano"
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Your user settings have been updated."
|
||||
msgstr "Vaše uživatelská nastavení byla aktualizována."
|
||||
|
||||
|
||||
@@ -157,7 +157,6 @@ msgstr "Alarmer"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/containers.tsx
|
||||
msgid "All Containers"
|
||||
msgstr "Alle containere"
|
||||
@@ -838,7 +837,6 @@ msgstr "Inaktiv"
|
||||
msgid "Invalid email address."
|
||||
msgstr "Ugyldig email adresse."
|
||||
|
||||
#: src/components/lang-toggle.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "Sprog"
|
||||
@@ -1337,7 +1335,6 @@ msgstr "Indstil procentvise tærskler for målerfarver."
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Settings"
|
||||
@@ -1489,7 +1486,6 @@ msgstr "Til email(s)"
|
||||
msgid "Toggle grid"
|
||||
msgstr "Slå gitter til/fra"
|
||||
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Toggle theme"
|
||||
msgstr "Skift tema"
|
||||
@@ -1745,3 +1741,4 @@ msgstr "Ja"
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Your user settings have been updated."
|
||||
msgstr "Dine brugerindstillinger er opdateret."
|
||||
|
||||
|
||||
@@ -157,7 +157,6 @@ msgstr "Warnungen"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/containers.tsx
|
||||
msgid "All Containers"
|
||||
msgstr "Alle Container"
|
||||
@@ -838,7 +837,6 @@ msgstr "Inaktiv"
|
||||
msgid "Invalid email address."
|
||||
msgstr "Ungültige E-Mail-Adresse."
|
||||
|
||||
#: src/components/lang-toggle.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "Sprache"
|
||||
@@ -1337,7 +1335,6 @@ msgstr "Prozentuale Schwellenwerte für Zählerfarben festlegen."
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Settings"
|
||||
@@ -1489,7 +1486,6 @@ msgstr "An E-Mail(s)"
|
||||
msgid "Toggle grid"
|
||||
msgstr "Raster umschalten"
|
||||
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Toggle theme"
|
||||
msgstr "Darstellung umschalten"
|
||||
@@ -1745,3 +1741,4 @@ msgstr "Ja"
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Your user settings have been updated."
|
||||
msgstr "Deine Benutzereinstellungen wurden aktualisiert."
|
||||
|
||||
|
||||
@@ -152,7 +152,6 @@ msgstr "Alerts"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/containers.tsx
|
||||
msgid "All Containers"
|
||||
msgstr "All Containers"
|
||||
@@ -833,7 +832,6 @@ msgstr "Inactive"
|
||||
msgid "Invalid email address."
|
||||
msgstr "Invalid email address."
|
||||
|
||||
#: src/components/lang-toggle.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "Language"
|
||||
@@ -1332,7 +1330,6 @@ msgstr "Set percentage thresholds for meter colors."
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Settings"
|
||||
@@ -1484,7 +1481,6 @@ msgstr "To email(s)"
|
||||
msgid "Toggle grid"
|
||||
msgstr "Toggle grid"
|
||||
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Toggle theme"
|
||||
msgstr "Toggle theme"
|
||||
|
||||
@@ -157,7 +157,6 @@ msgstr "Alertas"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/containers.tsx
|
||||
msgid "All Containers"
|
||||
msgstr "Todos los contenedores"
|
||||
@@ -838,7 +837,6 @@ msgstr "Inactivo"
|
||||
msgid "Invalid email address."
|
||||
msgstr "Dirección de correo electrónico no válida."
|
||||
|
||||
#: src/components/lang-toggle.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "Idioma"
|
||||
@@ -1337,7 +1335,6 @@ msgstr "Establecer umbrales de porcentaje para los colores de los medidores."
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Settings"
|
||||
@@ -1489,7 +1486,6 @@ msgstr "A correo(s)"
|
||||
msgid "Toggle grid"
|
||||
msgstr "Alternar cuadrícula"
|
||||
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Toggle theme"
|
||||
msgstr "Alternar tema"
|
||||
@@ -1745,3 +1741,4 @@ msgstr "Sí"
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Your user settings have been updated."
|
||||
msgstr "Tu configuración de usuario ha sido actualizada."
|
||||
|
||||
|
||||
@@ -157,7 +157,6 @@ msgstr "هشدارها"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/containers.tsx
|
||||
msgid "All Containers"
|
||||
msgstr "همه کانتینرها"
|
||||
@@ -838,7 +837,6 @@ msgstr "غیرفعال"
|
||||
msgid "Invalid email address."
|
||||
msgstr "آدرس ایمیل نامعتبر است."
|
||||
|
||||
#: src/components/lang-toggle.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "زبان"
|
||||
@@ -1337,7 +1335,6 @@ msgstr "آستانه های درصدی را برای رنگ های متر تنظ
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Settings"
|
||||
@@ -1489,7 +1486,6 @@ msgstr "به ایمیل(ها)"
|
||||
msgid "Toggle grid"
|
||||
msgstr "تغییر نمایش جدول"
|
||||
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Toggle theme"
|
||||
msgstr "تغییر تم"
|
||||
@@ -1745,3 +1741,4 @@ msgstr "بله"
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Your user settings have been updated."
|
||||
msgstr "تنظیمات کاربری شما بهروزرسانی شد."
|
||||
|
||||
|
||||
@@ -157,7 +157,6 @@ msgstr "Alertes"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/containers.tsx
|
||||
msgid "All Containers"
|
||||
msgstr "Tous les conteneurs"
|
||||
@@ -838,7 +837,6 @@ msgstr "Inactif"
|
||||
msgid "Invalid email address."
|
||||
msgstr "Adresse email invalide."
|
||||
|
||||
#: src/components/lang-toggle.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "Langue"
|
||||
@@ -1337,7 +1335,6 @@ msgstr "Définir des seuils de pourcentage pour les couleurs des compteurs."
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Settings"
|
||||
@@ -1489,7 +1486,6 @@ msgstr "Aux email(s)"
|
||||
msgid "Toggle grid"
|
||||
msgstr "Basculer la grille"
|
||||
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Toggle theme"
|
||||
msgstr "Changer le thème"
|
||||
@@ -1745,3 +1741,4 @@ msgstr "Oui"
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Your user settings have been updated."
|
||||
msgstr "Vos paramètres utilisateur ont été mis à jour."
|
||||
|
||||
|
||||
@@ -157,7 +157,6 @@ msgstr "התראות"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/containers.tsx
|
||||
msgid "All Containers"
|
||||
msgstr "כל הקונטיינרים"
|
||||
@@ -838,7 +837,6 @@ msgstr "לא פעיל"
|
||||
msgid "Invalid email address."
|
||||
msgstr "כתובת אימייל לא תקינה."
|
||||
|
||||
#: src/components/lang-toggle.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "שפה"
|
||||
@@ -1337,7 +1335,6 @@ msgstr "הגדר סף אחוזים עבור צבעי מד."
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Settings"
|
||||
@@ -1489,7 +1486,6 @@ msgstr "לאימייל(ים)"
|
||||
msgid "Toggle grid"
|
||||
msgstr "החלף רשת"
|
||||
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Toggle theme"
|
||||
msgstr "החלף ערכת נושא"
|
||||
@@ -1745,3 +1741,4 @@ msgstr "כן"
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Your user settings have been updated."
|
||||
msgstr "הגדרות המשתמש שלך עודכנו."
|
||||
|
||||
|
||||
@@ -157,7 +157,6 @@ msgstr "Upozorenja"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/containers.tsx
|
||||
msgid "All Containers"
|
||||
msgstr "Svi spremnici"
|
||||
@@ -838,7 +837,6 @@ msgstr "Neaktivno"
|
||||
msgid "Invalid email address."
|
||||
msgstr "Nevažeća email adresa."
|
||||
|
||||
#: src/components/lang-toggle.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "Jezik"
|
||||
@@ -1337,7 +1335,6 @@ msgstr "Postavite pragove postotka za boje mjerača."
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Settings"
|
||||
@@ -1489,7 +1486,6 @@ msgstr "Primaoci e-pošte"
|
||||
msgid "Toggle grid"
|
||||
msgstr "Uključi/isključi rešetku"
|
||||
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Toggle theme"
|
||||
msgstr "Uključi/isključi temu"
|
||||
@@ -1745,3 +1741,4 @@ msgstr "Da"
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Your user settings have been updated."
|
||||
msgstr "Vaše korisničke postavke su ažurirane."
|
||||
|
||||
|
||||
@@ -157,7 +157,6 @@ msgstr "Riasztások"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/containers.tsx
|
||||
msgid "All Containers"
|
||||
msgstr "Minden konténer"
|
||||
@@ -838,7 +837,6 @@ msgstr "Inaktív"
|
||||
msgid "Invalid email address."
|
||||
msgstr "Érvénytelen e-mail cím."
|
||||
|
||||
#: src/components/lang-toggle.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "Nyelv"
|
||||
@@ -1337,7 +1335,6 @@ msgstr "Százalékos küszöbértékek beállítása a mérőszínekhez."
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Settings"
|
||||
@@ -1489,7 +1486,6 @@ msgstr "E-mailben"
|
||||
msgid "Toggle grid"
|
||||
msgstr "Rács ki- és bekapcsolása"
|
||||
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Toggle theme"
|
||||
msgstr "Téma váltása"
|
||||
@@ -1745,3 +1741,4 @@ msgstr "Igen"
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Your user settings have been updated."
|
||||
msgstr "A felhasználói beállítások frissítésre kerültek."
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: id\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-01-31 21:16\n"
|
||||
"PO-Revision-Date: 2026-01-31 22:39\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Indonesian\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
@@ -1745,3 +1745,4 @@ msgstr "Ya"
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Your user settings have been updated."
|
||||
msgstr "Pengaturan pengguna anda telah diperbarui."
|
||||
|
||||
|
||||
@@ -157,7 +157,6 @@ msgstr "Avvisi"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/containers.tsx
|
||||
msgid "All Containers"
|
||||
msgstr "Tutti i contenitori"
|
||||
@@ -838,7 +837,6 @@ msgstr "Inattivo"
|
||||
msgid "Invalid email address."
|
||||
msgstr "Indirizzo email non valido."
|
||||
|
||||
#: src/components/lang-toggle.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "Lingua"
|
||||
@@ -1337,7 +1335,6 @@ msgstr "Imposta le soglie percentuali per i colori dei contatori."
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Settings"
|
||||
@@ -1489,7 +1486,6 @@ msgstr "A email(s)"
|
||||
msgid "Toggle grid"
|
||||
msgstr "Attiva/disattiva griglia"
|
||||
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Toggle theme"
|
||||
msgstr "Attiva/disattiva tema"
|
||||
@@ -1745,3 +1741,4 @@ msgstr "Sì"
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Your user settings have been updated."
|
||||
msgstr "Le impostazioni utente sono state aggiornate."
|
||||
|
||||
|
||||
@@ -157,7 +157,6 @@ msgstr "アラート"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/containers.tsx
|
||||
msgid "All Containers"
|
||||
msgstr "すべてのコンテナ"
|
||||
@@ -838,7 +837,6 @@ msgstr "非アクティブ"
|
||||
msgid "Invalid email address."
|
||||
msgstr "無効なメールアドレスです。"
|
||||
|
||||
#: src/components/lang-toggle.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "言語"
|
||||
@@ -1337,7 +1335,6 @@ msgstr "メーターの色を変更するしきい値(パーセンテージ)
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Settings"
|
||||
@@ -1489,7 +1486,6 @@ msgstr "宛先メールアドレス"
|
||||
msgid "Toggle grid"
|
||||
msgstr "グリッドを切り替え"
|
||||
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Toggle theme"
|
||||
msgstr "テーマを切り替え"
|
||||
@@ -1745,3 +1741,4 @@ msgstr "はい"
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Your user settings have been updated."
|
||||
msgstr "ユーザー設定が更新されました。"
|
||||
|
||||
|
||||
@@ -157,7 +157,6 @@ msgstr "알림"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/containers.tsx
|
||||
msgid "All Containers"
|
||||
msgstr "모든 컨테이너"
|
||||
@@ -838,7 +837,6 @@ msgstr "비활성"
|
||||
msgid "Invalid email address."
|
||||
msgstr "잘못된 이메일 주소입니다."
|
||||
|
||||
#: src/components/lang-toggle.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "언어"
|
||||
@@ -1337,7 +1335,6 @@ msgstr "그래프 미터 색상의 백분율 임계값을 설정합니다."
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Settings"
|
||||
@@ -1489,7 +1486,6 @@ msgstr "받는사람(들)"
|
||||
msgid "Toggle grid"
|
||||
msgstr "그리드 전환"
|
||||
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Toggle theme"
|
||||
msgstr "테마 전환"
|
||||
@@ -1745,3 +1741,4 @@ msgstr "예"
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Your user settings have been updated."
|
||||
msgstr "사용자 설정이 업데이트되었습니다."
|
||||
|
||||
|
||||
@@ -157,7 +157,6 @@ msgstr "Waarschuwingen"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/containers.tsx
|
||||
msgid "All Containers"
|
||||
msgstr "Alle containers"
|
||||
@@ -838,7 +837,6 @@ msgstr "Inactief"
|
||||
msgid "Invalid email address."
|
||||
msgstr "Ongeldig e-mailadres."
|
||||
|
||||
#: src/components/lang-toggle.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "Taal"
|
||||
@@ -1337,7 +1335,6 @@ msgstr "Stel percentagedrempels in voor meterkleuren."
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Settings"
|
||||
@@ -1489,7 +1486,6 @@ msgstr "Naar e-mail(s)"
|
||||
msgid "Toggle grid"
|
||||
msgstr "Schakel raster"
|
||||
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Toggle theme"
|
||||
msgstr "Schakel thema"
|
||||
@@ -1745,3 +1741,4 @@ msgstr "Ja"
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Your user settings have been updated."
|
||||
msgstr "Je gebruikersinstellingen zijn bijgewerkt."
|
||||
|
||||
|
||||
@@ -157,7 +157,6 @@ msgstr "Alarmer"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/containers.tsx
|
||||
msgid "All Containers"
|
||||
msgstr "Alle containere"
|
||||
@@ -838,7 +837,6 @@ msgstr "Inaktiv"
|
||||
msgid "Invalid email address."
|
||||
msgstr "Ugyldig e-postadresse."
|
||||
|
||||
#: src/components/lang-toggle.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "Språk"
|
||||
@@ -1337,7 +1335,6 @@ msgstr "Angi prosentvise terskler for målerfarger."
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Settings"
|
||||
@@ -1489,7 +1486,6 @@ msgstr "Til e-postadresse(r)"
|
||||
msgid "Toggle grid"
|
||||
msgstr "Rutenett av/på"
|
||||
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Toggle theme"
|
||||
msgstr "Tema av/på"
|
||||
@@ -1745,3 +1741,4 @@ msgstr "Ja"
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Your user settings have been updated."
|
||||
msgstr "Dine brukerinnstillinger har blitt oppdatert."
|
||||
|
||||
|
||||
@@ -157,7 +157,6 @@ msgstr "Alerty"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/containers.tsx
|
||||
msgid "All Containers"
|
||||
msgstr "Wszystkie kontenery"
|
||||
@@ -838,7 +837,6 @@ msgstr "Nieaktywny"
|
||||
msgid "Invalid email address."
|
||||
msgstr "Nieprawidłowy adres e-mail."
|
||||
|
||||
#: src/components/lang-toggle.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "Język"
|
||||
@@ -1337,7 +1335,6 @@ msgstr "Ustaw progi procentowe dla kolorów mierników."
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Settings"
|
||||
@@ -1489,7 +1486,6 @@ msgstr "Do e-mail(ów)"
|
||||
msgid "Toggle grid"
|
||||
msgstr "Przełącz widok"
|
||||
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Toggle theme"
|
||||
msgstr "Zmień motyw"
|
||||
@@ -1745,3 +1741,4 @@ msgstr "Tak"
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Your user settings have been updated."
|
||||
msgstr "Twoje ustawienia użytkownika zostały zaktualizowane."
|
||||
|
||||
|
||||
@@ -157,7 +157,6 @@ msgstr "Alertas"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/containers.tsx
|
||||
msgid "All Containers"
|
||||
msgstr "Todos os Contêineres"
|
||||
@@ -838,7 +837,6 @@ msgstr "Inativo"
|
||||
msgid "Invalid email address."
|
||||
msgstr "Endereço de email inválido."
|
||||
|
||||
#: src/components/lang-toggle.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "Idioma"
|
||||
@@ -1337,7 +1335,6 @@ msgstr "Defina os limiares de porcentagem para as cores do medidor."
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Settings"
|
||||
@@ -1489,7 +1486,6 @@ msgstr "Para email(s)"
|
||||
msgid "Toggle grid"
|
||||
msgstr "Alternar grade"
|
||||
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Toggle theme"
|
||||
msgstr "Alternar tema"
|
||||
@@ -1745,3 +1741,4 @@ msgstr "Sim"
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Your user settings have been updated."
|
||||
msgstr "As configurações do seu usuário foram atualizadas."
|
||||
|
||||
|
||||
1744
internal/site/src/locales/ro/ro.po
Normal file
1744
internal/site/src/locales/ro/ro.po
Normal file
File diff suppressed because it is too large
Load Diff
@@ -157,7 +157,6 @@ msgstr "Оповещения"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/containers.tsx
|
||||
msgid "All Containers"
|
||||
msgstr "Все контейнеры"
|
||||
@@ -838,7 +837,6 @@ msgstr "Неактивно"
|
||||
msgid "Invalid email address."
|
||||
msgstr "Неверный адрес электронной почты."
|
||||
|
||||
#: src/components/lang-toggle.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "Язык"
|
||||
@@ -1337,7 +1335,6 @@ msgstr "Установите процентные пороги для цвето
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Settings"
|
||||
@@ -1489,7 +1486,6 @@ msgstr "На электронную почту"
|
||||
msgid "Toggle grid"
|
||||
msgstr "Переключить сетку"
|
||||
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Toggle theme"
|
||||
msgstr "Переключить тему"
|
||||
@@ -1745,3 +1741,4 @@ msgstr "Да"
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Your user settings have been updated."
|
||||
msgstr "Ваши настройки пользователя были обновлены."
|
||||
|
||||
|
||||
@@ -157,7 +157,6 @@ msgstr "Opozorila"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/containers.tsx
|
||||
msgid "All Containers"
|
||||
msgstr "Vsi kontejnerji"
|
||||
@@ -838,7 +837,6 @@ msgstr "Neaktivno"
|
||||
msgid "Invalid email address."
|
||||
msgstr "Napačen e-poštni naslov."
|
||||
|
||||
#: src/components/lang-toggle.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "Jezik"
|
||||
@@ -1337,7 +1335,6 @@ msgstr "Nastavite odstotne pragove za barve merilnikov."
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Settings"
|
||||
@@ -1489,7 +1486,6 @@ msgstr "E-pošta za"
|
||||
msgid "Toggle grid"
|
||||
msgstr "Preklopi način mreže"
|
||||
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Toggle theme"
|
||||
msgstr "Obrni temo"
|
||||
@@ -1745,3 +1741,4 @@ msgstr "Da"
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Your user settings have been updated."
|
||||
msgstr "Vaše uporabniške nastavitve so posodobljene."
|
||||
|
||||
|
||||
@@ -157,7 +157,6 @@ msgstr "Упозорења"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/containers.tsx
|
||||
msgid "All Containers"
|
||||
msgstr "Сви контејнери"
|
||||
@@ -838,7 +837,6 @@ msgstr "Неактивно"
|
||||
msgid "Invalid email address."
|
||||
msgstr "Неважећа имејл адреса."
|
||||
|
||||
#: src/components/lang-toggle.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "Језик"
|
||||
@@ -1337,7 +1335,6 @@ msgstr "Подесите процентуалне прагове за боје
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Settings"
|
||||
@@ -1489,7 +1486,6 @@ msgstr "На е-пошту(е)"
|
||||
msgid "Toggle grid"
|
||||
msgstr "Укључи/искључи мрежу"
|
||||
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Toggle theme"
|
||||
msgstr "Промени тему"
|
||||
@@ -1745,3 +1741,4 @@ msgstr "Да"
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Your user settings have been updated."
|
||||
msgstr "Ваша корисничка подешавања су ажурирана."
|
||||
|
||||
|
||||
@@ -157,7 +157,6 @@ msgstr "Larm"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/containers.tsx
|
||||
msgid "All Containers"
|
||||
msgstr "Alla behållare"
|
||||
@@ -838,7 +837,6 @@ msgstr "Inaktiv"
|
||||
msgid "Invalid email address."
|
||||
msgstr "Ogiltig e-postadress."
|
||||
|
||||
#: src/components/lang-toggle.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "Språk"
|
||||
@@ -1337,7 +1335,6 @@ msgstr "Ställ in procentuella tröskelvärden för mätarfärger."
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Settings"
|
||||
@@ -1489,7 +1486,6 @@ msgstr "Till e-postadress(er)"
|
||||
msgid "Toggle grid"
|
||||
msgstr "Växla rutnät"
|
||||
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Toggle theme"
|
||||
msgstr "Växla tema"
|
||||
@@ -1745,3 +1741,4 @@ msgstr "Ja"
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Your user settings have been updated."
|
||||
msgstr "Dina användarinställningar har uppdaterats."
|
||||
|
||||
|
||||
1744
internal/site/src/locales/th/th.po
Normal file
1744
internal/site/src/locales/th/th.po
Normal file
File diff suppressed because it is too large
Load Diff
@@ -157,7 +157,6 @@ msgstr "Uyarılar"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/containers.tsx
|
||||
msgid "All Containers"
|
||||
msgstr "Tüm Konteynerler"
|
||||
@@ -838,7 +837,6 @@ msgstr "Pasif"
|
||||
msgid "Invalid email address."
|
||||
msgstr "Geçersiz e-posta adresi."
|
||||
|
||||
#: src/components/lang-toggle.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "Dil"
|
||||
@@ -1337,7 +1335,6 @@ msgstr "Sayaç renkleri için yüzde eşiklerini ayarlayın."
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Settings"
|
||||
@@ -1489,7 +1486,6 @@ msgstr "E-posta(lar)a"
|
||||
msgid "Toggle grid"
|
||||
msgstr "Izgarayı değiştir"
|
||||
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Toggle theme"
|
||||
msgstr "Temayı değiştir"
|
||||
@@ -1745,3 +1741,4 @@ msgstr "Evet"
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Your user settings have been updated."
|
||||
msgstr "Kullanıcı ayarlarınız güncellendi."
|
||||
|
||||
|
||||
@@ -157,7 +157,6 @@ msgstr "Сповіщення"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/containers.tsx
|
||||
msgid "All Containers"
|
||||
msgstr "Всі контейнери"
|
||||
@@ -838,7 +837,6 @@ msgstr "Неактивне"
|
||||
msgid "Invalid email address."
|
||||
msgstr "Неправильна адреса електронної пошти."
|
||||
|
||||
#: src/components/lang-toggle.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "Мова"
|
||||
@@ -1337,7 +1335,6 @@ msgstr "Встановіть відсоткові пороги для кольо
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Settings"
|
||||
@@ -1489,7 +1486,6 @@ msgstr "На електронну пошту"
|
||||
msgid "Toggle grid"
|
||||
msgstr "Перемкнути сітку"
|
||||
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Toggle theme"
|
||||
msgstr "Перемкнути тему"
|
||||
@@ -1745,3 +1741,4 @@ msgstr "Так"
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Your user settings have been updated."
|
||||
msgstr "Ваші налаштування користувача були оновлені."
|
||||
|
||||
|
||||
@@ -157,7 +157,6 @@ msgstr "Cảnh báo"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/containers.tsx
|
||||
msgid "All Containers"
|
||||
msgstr "Tất cả container"
|
||||
@@ -838,7 +837,6 @@ msgstr "Không hoạt động"
|
||||
msgid "Invalid email address."
|
||||
msgstr "Địa chỉ email không hợp lệ."
|
||||
|
||||
#: src/components/lang-toggle.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "Ngôn ngữ"
|
||||
@@ -1337,7 +1335,6 @@ msgstr "Đặt ngưỡng cho màu sắc đồng hồ."
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Settings"
|
||||
@@ -1489,7 +1486,6 @@ msgstr "Đến email(s)"
|
||||
msgid "Toggle grid"
|
||||
msgstr "Chuyển đổi lưới"
|
||||
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Toggle theme"
|
||||
msgstr "Chuyển đổi chủ đề"
|
||||
@@ -1745,3 +1741,4 @@ msgstr "Có"
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Your user settings have been updated."
|
||||
msgstr "Cài đặt người dùng của bạn đã được cập nhật."
|
||||
|
||||
|
||||
@@ -157,7 +157,6 @@ msgstr "警报"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/containers.tsx
|
||||
msgid "All Containers"
|
||||
msgstr "所有容器"
|
||||
@@ -838,7 +837,6 @@ msgstr "非活跃"
|
||||
msgid "Invalid email address."
|
||||
msgstr "无效的电子邮件地址。"
|
||||
|
||||
#: src/components/lang-toggle.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "语言"
|
||||
@@ -1337,7 +1335,6 @@ msgstr "设置仪表颜色的百分比阈值。"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Settings"
|
||||
@@ -1489,7 +1486,6 @@ msgstr "发送到电子邮件"
|
||||
msgid "Toggle grid"
|
||||
msgstr "切换网格"
|
||||
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Toggle theme"
|
||||
msgstr "切换主题"
|
||||
@@ -1745,3 +1741,4 @@ msgstr "是"
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Your user settings have been updated."
|
||||
msgstr "您的用户设置已更新。"
|
||||
|
||||
|
||||
@@ -157,7 +157,6 @@ msgstr "警報"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/containers.tsx
|
||||
msgid "All Containers"
|
||||
msgstr "所有容器"
|
||||
@@ -838,7 +837,6 @@ msgstr "未啟用"
|
||||
msgid "Invalid email address."
|
||||
msgstr "無效的電子郵件地址。"
|
||||
|
||||
#: src/components/lang-toggle.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "語言"
|
||||
@@ -1337,7 +1335,6 @@ msgstr "設定儀表顏色的百分比閾值。"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Settings"
|
||||
@@ -1489,7 +1486,6 @@ msgstr "發送到電子郵件"
|
||||
msgid "Toggle grid"
|
||||
msgstr "切換網格"
|
||||
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Toggle theme"
|
||||
msgstr "切換主題"
|
||||
@@ -1745,3 +1741,4 @@ msgstr "是"
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Your user settings have been updated."
|
||||
msgstr "您的用戶設置已更新。"
|
||||
|
||||
|
||||
@@ -157,7 +157,6 @@ msgstr "警報"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/containers.tsx
|
||||
msgid "All Containers"
|
||||
msgstr "所有容器"
|
||||
@@ -838,7 +837,6 @@ msgstr "未啟用"
|
||||
msgid "Invalid email address."
|
||||
msgstr "無效的電子郵件地址。"
|
||||
|
||||
#: src/components/lang-toggle.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "語言"
|
||||
@@ -1337,7 +1335,6 @@ msgstr "設定儀表顏色的百分比閾值。"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Settings"
|
||||
@@ -1489,7 +1486,6 @@ msgstr "發送到電子郵件"
|
||||
msgid "Toggle grid"
|
||||
msgstr "切換網格"
|
||||
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Toggle theme"
|
||||
msgstr "切換主題"
|
||||
@@ -1745,3 +1741,4 @@ msgstr "是"
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Your user settings have been updated."
|
||||
msgstr "已更新您的使用者設定"
|
||||
|
||||
|
||||
@@ -1,49 +1,3 @@
|
||||
## 0.18.3
|
||||
|
||||
- Add experimental sysfs AMD GPU collector. (#737, #1569)
|
||||
|
||||
- Update LibreHardwareMonitorLib to 0.9.5. (#1697)
|
||||
|
||||
- Improve container network stats accuracy.
|
||||
|
||||
- Fix `SHARE_ALL_SYSTEMS` for system_details, smart_devices, and systemd_services. (#1660)
|
||||
|
||||
- Parse ATA device statistics for temperature and future metrics. (#1689)
|
||||
|
||||
- Add `SMART_DEVICES_SEPARATOR` environment variable and allow drives with the same name to be added with different types (e.g. RAID controllers). (#1655)
|
||||
|
||||
- Add tooltips for navbar buttons. (#1636)
|
||||
|
||||
- Add icon button for mobile use. (#1687)
|
||||
|
||||
- Add tooltip to system name in systems table. (#1640)
|
||||
|
||||
- Improve CJK truncation in UI.
|
||||
|
||||
- Fix container uptime sorting edge case. (#1696)
|
||||
|
||||
- Remove stale systemd services from tracking after deletion. (#1594)
|
||||
|
||||
- Apply SELinux context after binary replacement. (#1678)
|
||||
|
||||
- Update honeypot field name and autofill ignores. (#1011)
|
||||
|
||||
- Write health_file to `/dev/shm` instead of `/tmp` if available. (#1455)
|
||||
|
||||
- Don't force lowercase text for active alerts. (#1682)
|
||||
|
||||
- Ensure battery current charge doesn't exceed full capacity. (#1668)
|
||||
|
||||
- Increase `smartctl --scan` timeout to 10 seconds. (#1465)
|
||||
|
||||
- Use name-only matching for unique S.M.A.R.T. devices. (#1655)
|
||||
|
||||
- Fix smartctlArgs call to use hasExistingData flag. (#1645)
|
||||
|
||||
- Ignore alt key combinations when navigating systems with arrow keys. (#1698)
|
||||
|
||||
- Update Go dependencies
|
||||
|
||||
## 0.18.2
|
||||
|
||||
- Add separate dynamically linked glibc build for Linux. (#1618)
|
||||
|
||||
@@ -7,15 +7,13 @@ param (
|
||||
[int]$Port = 45876,
|
||||
[string]$AgentPath = "",
|
||||
[string]$NSSMPath = "",
|
||||
[switch]$ConfigureFirewall,
|
||||
[ValidateSet("Auto", "Scoop", "WinGet")]
|
||||
[string]$InstallMethod = "Auto"
|
||||
[switch]$ConfigureFirewall
|
||||
)
|
||||
|
||||
# Check if required parameters are provided
|
||||
if ([string]::IsNullOrWhiteSpace($Key)) {
|
||||
Write-Host "ERROR: SSH Key is required." -ForegroundColor Red
|
||||
Write-Host "Usage: .\install-agent.ps1 -Key 'your-ssh-key-here' [-Token 'your-token-here'] [-Url 'your-hub-url-here'] [-Port port-number] [-InstallMethod Auto|Scoop|WinGet] [-ConfigureFirewall]" -ForegroundColor Yellow
|
||||
Write-Host "Usage: .\install-agent.ps1 -Key 'your-ssh-key-here' [-Token 'your-token-here'] [-Url 'your-hub-url-here'] [-Port port-number] [-ConfigureFirewall]" -ForegroundColor Yellow
|
||||
Write-Host "Note: Token and Url are optional for backwards compatibility with older hub versions." -ForegroundColor Yellow
|
||||
exit 1
|
||||
}
|
||||
@@ -488,34 +486,18 @@ try {
|
||||
Write-Host "2. Install WinGet and run this script again" -ForegroundColor Yellow
|
||||
exit 1
|
||||
}
|
||||
|
||||
if ($InstallMethod -eq "Scoop") {
|
||||
if (-not (Test-CommandExists "scoop")) {
|
||||
throw "InstallMethod is set to Scoop, but Scoop is not available in PATH."
|
||||
}
|
||||
|
||||
if (Test-CommandExists "scoop") {
|
||||
Write-Host "Using Scoop for installation..."
|
||||
$AgentPath = Install-WithScoop -Key $Key -Port $Port
|
||||
}
|
||||
elseif ($InstallMethod -eq "WinGet") {
|
||||
if (-not (Test-CommandExists "winget")) {
|
||||
throw "InstallMethod is set to WinGet, but WinGet is not available in PATH."
|
||||
}
|
||||
elseif (Test-CommandExists "winget") {
|
||||
Write-Host "Using WinGet for installation..."
|
||||
$AgentPath = Install-WithWinGet -Key $Key -Port $Port
|
||||
}
|
||||
else {
|
||||
if (Test-CommandExists "scoop") {
|
||||
Write-Host "Using Scoop for installation..."
|
||||
$AgentPath = Install-WithScoop -Key $Key -Port $Port
|
||||
}
|
||||
elseif (Test-CommandExists "winget") {
|
||||
Write-Host "Using WinGet for installation..."
|
||||
$AgentPath = Install-WithWinGet -Key $Key -Port $Port
|
||||
}
|
||||
else {
|
||||
Write-Host "Neither Scoop nor WinGet is installed. Installing Scoop..."
|
||||
$AgentPath = Install-WithScoop -Key $Key -Port $Port
|
||||
}
|
||||
Write-Host "Neither Scoop nor WinGet is installed. Installing Scoop..."
|
||||
$AgentPath = Install-WithScoop -Key $Key -Port $Port
|
||||
}
|
||||
}
|
||||
|
||||
@@ -579,8 +561,7 @@ try {
|
||||
"-Token", "`"$Token`"",
|
||||
"-Url", "`"$Url`"",
|
||||
"-Port", $Port,
|
||||
"-AgentPath", "`"$AgentPath`"",
|
||||
"-InstallMethod", $InstallMethod
|
||||
"-AgentPath", "`"$AgentPath`""
|
||||
)
|
||||
|
||||
# Add NSSMPath if we found it
|
||||
|
||||
Reference in New Issue
Block a user