mirror of
https://github.com/henrygd/beszel.git
synced 2026-03-26 23:46:17 +01:00
Compare commits
60 Commits
intel-gpu
...
f3a74b1f46
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f3a74b1f46 | ||
|
|
4a76b620e7 | ||
|
|
25210d031d | ||
|
|
87b55fd4cf | ||
|
|
b4430ac76f | ||
|
|
5f9aa78b72 | ||
|
|
7537f6bd5c | ||
|
|
83dee5e554 | ||
|
|
036b4495e6 | ||
|
|
31cd36fcc1 | ||
|
|
0a2aaf3260 | ||
|
|
6e2e90120a | ||
|
|
23f95d6ebd | ||
|
|
56dc1096d9 | ||
|
|
9dd203f85a | ||
|
|
adac9cf79d | ||
|
|
efd2ba04c5 | ||
|
|
a9055e216d | ||
|
|
f64130029b | ||
|
|
6d172bac82 | ||
|
|
354cba5690 | ||
|
|
e65cde9675 | ||
|
|
fbd2fbb6a6 | ||
|
|
01bf64083a | ||
|
|
103856121d | ||
|
|
8a798c7e3f | ||
|
|
beeec5c39e | ||
|
|
6d43045d79 | ||
|
|
88ea94f5b0 | ||
|
|
3b8d333f8e | ||
|
|
3a06982502 | ||
|
|
b820b46042 | ||
|
|
fab799f177 | ||
|
|
5eaf9b9157 | ||
|
|
1eed3c53c8 | ||
|
|
90729a7a95 | ||
|
|
d450f6df10 | ||
|
|
7aa2bcf761 | ||
|
|
a7a86f46c3 | ||
|
|
17e30aff60 | ||
|
|
66008e47f3 | ||
|
|
56788b1e5b | ||
|
|
b72371487a | ||
|
|
7656b4189e | ||
|
|
8e6731c102 | ||
|
|
e86fa40fe4 | ||
|
|
f0e728a1ed | ||
|
|
bb076eb439 | ||
|
|
2f0b16367a | ||
|
|
aa33124e18 | ||
|
|
42f404c80a | ||
|
|
5056fddd40 | ||
|
|
2296202ea1 | ||
|
|
c034e9b0fa | ||
|
|
2d45119a98 | ||
|
|
63be4f1ab5 | ||
|
|
6d2259100e | ||
|
|
142af6e7b6 | ||
|
|
5c1e009188 | ||
|
|
27b2cb84d6 |
2
Makefile
2
Makefile
@@ -77,7 +77,7 @@ dev-hub: export ENV=dev
|
|||||||
dev-hub:
|
dev-hub:
|
||||||
mkdir -p ./internal/site/dist && touch ./internal/site/dist/index.html
|
mkdir -p ./internal/site/dist && touch ./internal/site/dist/index.html
|
||||||
@if command -v entr >/dev/null 2>&1; then \
|
@if command -v entr >/dev/null 2>&1; then \
|
||||||
find ./internal -type f -name '*.go' | entr -r -s "cd ./internal/cmd/hub && go run -tags development . serve --http 0.0.0.0:8090"; \
|
find ./internal/cmd/hub/*.go ./internal/{alerts,hub,records,users}/*.go | entr -r -s "cd ./internal/cmd/hub && go run -tags development . serve --http 0.0.0.0:8090"; \
|
||||||
else \
|
else \
|
||||||
cd ./internal/cmd/hub && go run -tags development . serve --http 0.0.0.0:8090; \
|
cd ./internal/cmd/hub && go run -tags development . serve --http 0.0.0.0:8090; \
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -20,8 +20,9 @@ func HasReadableBattery() bool {
|
|||||||
}
|
}
|
||||||
haveCheckedBattery = true
|
haveCheckedBattery = true
|
||||||
bat, err := battery.Get(0)
|
bat, err := battery.Get(0)
|
||||||
systemHasBattery = err == nil && bat != nil && bat.Design != 0 && bat.Full != 0
|
if err == nil && bat != nil {
|
||||||
if !systemHasBattery {
|
systemHasBattery = true
|
||||||
|
} else {
|
||||||
slog.Debug("No battery found", "err", err)
|
slog.Debug("No battery found", "err", err)
|
||||||
}
|
}
|
||||||
return systemHasBattery
|
return systemHasBattery
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ func getToken() (string, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
return strings.TrimSpace(string(tokenBytes)), nil
|
return string(tokenBytes), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getOptions returns the WebSocket client options, creating them if necessary.
|
// getOptions returns the WebSocket client options, creating them if necessary.
|
||||||
|
|||||||
@@ -537,25 +537,4 @@ func TestGetToken(t *testing.T) {
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "", token, "Empty file should return empty string")
|
assert.Equal(t, "", token, "Empty file should return empty string")
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("strips whitespace from TOKEN_FILE", func(t *testing.T) {
|
|
||||||
unsetEnvVars()
|
|
||||||
|
|
||||||
tokenWithWhitespace := " test-token-with-whitespace \n\t"
|
|
||||||
expectedToken := "test-token-with-whitespace"
|
|
||||||
tokenFile, err := os.CreateTemp("", "token-test-*.txt")
|
|
||||||
require.NoError(t, err)
|
|
||||||
defer os.Remove(tokenFile.Name())
|
|
||||||
|
|
||||||
_, err = tokenFile.WriteString(tokenWithWhitespace)
|
|
||||||
require.NoError(t, err)
|
|
||||||
tokenFile.Close()
|
|
||||||
|
|
||||||
os.Setenv("TOKEN_FILE", tokenFile.Name())
|
|
||||||
defer os.Unsetenv("TOKEN_FILE")
|
|
||||||
|
|
||||||
token, err := getToken()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, expectedToken, token, "Whitespace should be stripped from token file content")
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,21 +9,19 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/henrygd/beszel/agent/health"
|
"github.com/henrygd/beszel/agent/health"
|
||||||
"github.com/henrygd/beszel/internal/entities/system"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ConnectionManager manages the connection state and events for the agent.
|
// ConnectionManager manages the connection state and events for the agent.
|
||||||
// It handles both WebSocket and SSH connections, automatically switching between
|
// It handles both WebSocket and SSH connections, automatically switching between
|
||||||
// them based on availability and managing reconnection attempts.
|
// them based on availability and managing reconnection attempts.
|
||||||
type ConnectionManager struct {
|
type ConnectionManager struct {
|
||||||
agent *Agent // Reference to the parent agent
|
agent *Agent // Reference to the parent agent
|
||||||
State ConnectionState // Current connection state
|
State ConnectionState // Current connection state
|
||||||
eventChan chan ConnectionEvent // Channel for connection events
|
eventChan chan ConnectionEvent // Channel for connection events
|
||||||
wsClient *WebSocketClient // WebSocket client for hub communication
|
wsClient *WebSocketClient // WebSocket client for hub communication
|
||||||
serverOptions ServerOptions // Configuration for SSH server
|
serverOptions ServerOptions // Configuration for SSH server
|
||||||
wsTicker *time.Ticker // Ticker for WebSocket connection attempts
|
wsTicker *time.Ticker // Ticker for WebSocket connection attempts
|
||||||
isConnecting bool // Prevents multiple simultaneous reconnection attempts
|
isConnecting bool // Prevents multiple simultaneous reconnection attempts
|
||||||
ConnectionType system.ConnectionType
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConnectionState represents the current connection state of the agent.
|
// ConnectionState represents the current connection state of the agent.
|
||||||
@@ -146,18 +144,15 @@ func (c *ConnectionManager) handleStateChange(newState ConnectionState) {
|
|||||||
switch newState {
|
switch newState {
|
||||||
case WebSocketConnected:
|
case WebSocketConnected:
|
||||||
slog.Info("WebSocket connected", "host", c.wsClient.hubURL.Host)
|
slog.Info("WebSocket connected", "host", c.wsClient.hubURL.Host)
|
||||||
c.ConnectionType = system.ConnectionTypeWebSocket
|
|
||||||
c.stopWsTicker()
|
c.stopWsTicker()
|
||||||
_ = c.agent.StopServer()
|
_ = c.agent.StopServer()
|
||||||
c.isConnecting = false
|
c.isConnecting = false
|
||||||
case SSHConnected:
|
case SSHConnected:
|
||||||
// stop new ws connection attempts
|
// stop new ws connection attempts
|
||||||
slog.Info("SSH connection established")
|
slog.Info("SSH connection established")
|
||||||
c.ConnectionType = system.ConnectionTypeSSH
|
|
||||||
c.stopWsTicker()
|
c.stopWsTicker()
|
||||||
c.isConnecting = false
|
c.isConnecting = false
|
||||||
case Disconnected:
|
case Disconnected:
|
||||||
c.ConnectionType = system.ConnectionTypeNone
|
|
||||||
if c.isConnecting {
|
if c.isConnecting {
|
||||||
// Already handling reconnection, avoid duplicate attempts
|
// Already handling reconnection, avoid duplicate attempts
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -1,81 +0,0 @@
|
|||||||
// Package deltatracker provides a tracker for calculating differences in numeric values over time.
|
|
||||||
package deltatracker
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"golang.org/x/exp/constraints"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Numeric is a constraint that permits any integer or floating-point type.
|
|
||||||
type Numeric interface {
|
|
||||||
constraints.Integer | constraints.Float
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeltaTracker is a generic, thread-safe tracker for calculating differences
|
|
||||||
// in numeric values over time.
|
|
||||||
// K is the key type (e.g., int, string).
|
|
||||||
// V is the value type (e.g., int, int64, float32, float64).
|
|
||||||
type DeltaTracker[K comparable, V Numeric] struct {
|
|
||||||
sync.RWMutex
|
|
||||||
current map[K]V
|
|
||||||
previous map[K]V
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDeltaTracker creates a new generic tracker.
|
|
||||||
func NewDeltaTracker[K comparable, V Numeric]() *DeltaTracker[K, V] {
|
|
||||||
return &DeltaTracker[K, V]{
|
|
||||||
current: make(map[K]V),
|
|
||||||
previous: make(map[K]V),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set records the current value for a given ID.
|
|
||||||
func (t *DeltaTracker[K, V]) Set(id K, value V) {
|
|
||||||
t.Lock()
|
|
||||||
defer t.Unlock()
|
|
||||||
t.current[id] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deltas returns a map of all calculated deltas for the current interval.
|
|
||||||
func (t *DeltaTracker[K, V]) Deltas() map[K]V {
|
|
||||||
t.RLock()
|
|
||||||
defer t.RUnlock()
|
|
||||||
|
|
||||||
deltas := make(map[K]V)
|
|
||||||
for id, currentVal := range t.current {
|
|
||||||
if previousVal, ok := t.previous[id]; ok {
|
|
||||||
deltas[id] = currentVal - previousVal
|
|
||||||
} else {
|
|
||||||
deltas[id] = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return deltas
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delta returns the delta for a single key.
|
|
||||||
// Returns 0 if the key doesn't exist or has no previous value.
|
|
||||||
func (t *DeltaTracker[K, V]) Delta(id K) V {
|
|
||||||
t.RLock()
|
|
||||||
defer t.RUnlock()
|
|
||||||
|
|
||||||
currentVal, currentOk := t.current[id]
|
|
||||||
if !currentOk {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
previousVal, previousOk := t.previous[id]
|
|
||||||
if !previousOk {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
return currentVal - previousVal
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cycle prepares the tracker for the next interval.
|
|
||||||
func (t *DeltaTracker[K, V]) Cycle() {
|
|
||||||
t.Lock()
|
|
||||||
defer t.Unlock()
|
|
||||||
t.previous = t.current
|
|
||||||
t.current = make(map[K]V)
|
|
||||||
}
|
|
||||||
@@ -1,217 +0,0 @@
|
|||||||
package deltatracker
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ExampleDeltaTracker() {
|
|
||||||
tracker := NewDeltaTracker[string, int]()
|
|
||||||
tracker.Set("key1", 10)
|
|
||||||
tracker.Set("key2", 20)
|
|
||||||
tracker.Cycle()
|
|
||||||
tracker.Set("key1", 15)
|
|
||||||
tracker.Set("key2", 30)
|
|
||||||
fmt.Println(tracker.Delta("key1"))
|
|
||||||
fmt.Println(tracker.Delta("key2"))
|
|
||||||
fmt.Println(tracker.Deltas())
|
|
||||||
// Output: 5
|
|
||||||
// 10
|
|
||||||
// map[key1:5 key2:10]
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewDeltaTracker(t *testing.T) {
|
|
||||||
tracker := NewDeltaTracker[string, int]()
|
|
||||||
assert.NotNil(t, tracker)
|
|
||||||
assert.Empty(t, tracker.current)
|
|
||||||
assert.Empty(t, tracker.previous)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSet(t *testing.T) {
|
|
||||||
tracker := NewDeltaTracker[string, int]()
|
|
||||||
tracker.Set("key1", 10)
|
|
||||||
|
|
||||||
tracker.RLock()
|
|
||||||
defer tracker.RUnlock()
|
|
||||||
|
|
||||||
assert.Equal(t, 10, tracker.current["key1"])
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDeltas(t *testing.T) {
|
|
||||||
tracker := NewDeltaTracker[string, int]()
|
|
||||||
|
|
||||||
// Test with no previous values
|
|
||||||
tracker.Set("key1", 10)
|
|
||||||
tracker.Set("key2", 20)
|
|
||||||
|
|
||||||
deltas := tracker.Deltas()
|
|
||||||
assert.Equal(t, 0, deltas["key1"])
|
|
||||||
assert.Equal(t, 0, deltas["key2"])
|
|
||||||
|
|
||||||
// Cycle to move current to previous
|
|
||||||
tracker.Cycle()
|
|
||||||
|
|
||||||
// Set new values and check deltas
|
|
||||||
tracker.Set("key1", 15) // Delta should be 5 (15-10)
|
|
||||||
tracker.Set("key2", 25) // Delta should be 5 (25-20)
|
|
||||||
tracker.Set("key3", 30) // New key, delta should be 0
|
|
||||||
|
|
||||||
deltas = tracker.Deltas()
|
|
||||||
assert.Equal(t, 5, deltas["key1"])
|
|
||||||
assert.Equal(t, 5, deltas["key2"])
|
|
||||||
assert.Equal(t, 0, deltas["key3"])
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCycle(t *testing.T) {
|
|
||||||
tracker := NewDeltaTracker[string, int]()
|
|
||||||
|
|
||||||
tracker.Set("key1", 10)
|
|
||||||
tracker.Set("key2", 20)
|
|
||||||
|
|
||||||
// Verify current has values
|
|
||||||
tracker.RLock()
|
|
||||||
assert.Equal(t, 10, tracker.current["key1"])
|
|
||||||
assert.Equal(t, 20, tracker.current["key2"])
|
|
||||||
assert.Empty(t, tracker.previous)
|
|
||||||
tracker.RUnlock()
|
|
||||||
|
|
||||||
tracker.Cycle()
|
|
||||||
|
|
||||||
// After cycle, previous should have the old current values
|
|
||||||
// and current should be empty
|
|
||||||
tracker.RLock()
|
|
||||||
assert.Empty(t, tracker.current)
|
|
||||||
assert.Equal(t, 10, tracker.previous["key1"])
|
|
||||||
assert.Equal(t, 20, tracker.previous["key2"])
|
|
||||||
tracker.RUnlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCompleteWorkflow(t *testing.T) {
|
|
||||||
tracker := NewDeltaTracker[string, int]()
|
|
||||||
|
|
||||||
// First interval
|
|
||||||
tracker.Set("server1", 100)
|
|
||||||
tracker.Set("server2", 200)
|
|
||||||
|
|
||||||
// Get deltas for first interval (should be zero)
|
|
||||||
firstDeltas := tracker.Deltas()
|
|
||||||
assert.Equal(t, 0, firstDeltas["server1"])
|
|
||||||
assert.Equal(t, 0, firstDeltas["server2"])
|
|
||||||
|
|
||||||
// Cycle to next interval
|
|
||||||
tracker.Cycle()
|
|
||||||
|
|
||||||
// Second interval
|
|
||||||
tracker.Set("server1", 150) // Delta: 50
|
|
||||||
tracker.Set("server2", 180) // Delta: -20
|
|
||||||
tracker.Set("server3", 300) // New server, delta: 300
|
|
||||||
|
|
||||||
secondDeltas := tracker.Deltas()
|
|
||||||
assert.Equal(t, 50, secondDeltas["server1"])
|
|
||||||
assert.Equal(t, -20, secondDeltas["server2"])
|
|
||||||
assert.Equal(t, 0, secondDeltas["server3"])
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDeltaTrackerWithDifferentTypes(t *testing.T) {
|
|
||||||
// Test with int64
|
|
||||||
intTracker := NewDeltaTracker[string, int64]()
|
|
||||||
intTracker.Set("pid1", 1000)
|
|
||||||
intTracker.Cycle()
|
|
||||||
intTracker.Set("pid1", 1200)
|
|
||||||
intDeltas := intTracker.Deltas()
|
|
||||||
assert.Equal(t, int64(200), intDeltas["pid1"])
|
|
||||||
|
|
||||||
// Test with float64
|
|
||||||
floatTracker := NewDeltaTracker[string, float64]()
|
|
||||||
floatTracker.Set("cpu1", 1.5)
|
|
||||||
floatTracker.Cycle()
|
|
||||||
floatTracker.Set("cpu1", 2.7)
|
|
||||||
floatDeltas := floatTracker.Deltas()
|
|
||||||
assert.InDelta(t, 1.2, floatDeltas["cpu1"], 0.0001)
|
|
||||||
|
|
||||||
// Test with int keys
|
|
||||||
pidTracker := NewDeltaTracker[int, int64]()
|
|
||||||
pidTracker.Set(101, 20000)
|
|
||||||
pidTracker.Cycle()
|
|
||||||
pidTracker.Set(101, 22500)
|
|
||||||
pidDeltas := pidTracker.Deltas()
|
|
||||||
assert.Equal(t, int64(2500), pidDeltas[101])
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDelta(t *testing.T) {
|
|
||||||
tracker := NewDeltaTracker[string, int]()
|
|
||||||
|
|
||||||
// Test getting delta for non-existent key
|
|
||||||
result := tracker.Delta("nonexistent")
|
|
||||||
assert.Equal(t, 0, result)
|
|
||||||
|
|
||||||
// Test getting delta for key with no previous value
|
|
||||||
tracker.Set("key1", 10)
|
|
||||||
result = tracker.Delta("key1")
|
|
||||||
assert.Equal(t, 0, result)
|
|
||||||
|
|
||||||
// Cycle to move current to previous
|
|
||||||
tracker.Cycle()
|
|
||||||
|
|
||||||
// Test getting delta for key with previous value
|
|
||||||
tracker.Set("key1", 15)
|
|
||||||
result = tracker.Delta("key1")
|
|
||||||
assert.Equal(t, 5, result)
|
|
||||||
|
|
||||||
// Test getting delta for key that exists in previous but not current
|
|
||||||
result = tracker.Delta("key1")
|
|
||||||
assert.Equal(t, 5, result) // Should still return 5
|
|
||||||
|
|
||||||
// Test getting delta for key that exists in current but not previous
|
|
||||||
tracker.Set("key2", 20)
|
|
||||||
result = tracker.Delta("key2")
|
|
||||||
assert.Equal(t, 0, result)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDeltaWithDifferentTypes(t *testing.T) {
|
|
||||||
// Test with int64
|
|
||||||
intTracker := NewDeltaTracker[string, int64]()
|
|
||||||
intTracker.Set("pid1", 1000)
|
|
||||||
intTracker.Cycle()
|
|
||||||
intTracker.Set("pid1", 1200)
|
|
||||||
result := intTracker.Delta("pid1")
|
|
||||||
assert.Equal(t, int64(200), result)
|
|
||||||
|
|
||||||
// Test with float64
|
|
||||||
floatTracker := NewDeltaTracker[string, float64]()
|
|
||||||
floatTracker.Set("cpu1", 1.5)
|
|
||||||
floatTracker.Cycle()
|
|
||||||
floatTracker.Set("cpu1", 2.7)
|
|
||||||
floatResult := floatTracker.Delta("cpu1")
|
|
||||||
assert.InDelta(t, 1.2, floatResult, 0.0001)
|
|
||||||
|
|
||||||
// Test with int keys
|
|
||||||
pidTracker := NewDeltaTracker[int, int64]()
|
|
||||||
pidTracker.Set(101, 20000)
|
|
||||||
pidTracker.Cycle()
|
|
||||||
pidTracker.Set(101, 22500)
|
|
||||||
pidResult := pidTracker.Delta(101)
|
|
||||||
assert.Equal(t, int64(2500), pidResult)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDeltaConcurrentAccess(t *testing.T) {
|
|
||||||
tracker := NewDeltaTracker[string, int]()
|
|
||||||
|
|
||||||
// Set initial values
|
|
||||||
tracker.Set("key1", 10)
|
|
||||||
tracker.Set("key2", 20)
|
|
||||||
tracker.Cycle()
|
|
||||||
|
|
||||||
// Set new values
|
|
||||||
tracker.Set("key1", 15)
|
|
||||||
tracker.Set("key2", 25)
|
|
||||||
|
|
||||||
// Test concurrent access safety
|
|
||||||
result1 := tracker.Delta("key1")
|
|
||||||
result2 := tracker.Delta("key2")
|
|
||||||
|
|
||||||
assert.Equal(t, 5, result1)
|
|
||||||
assert.Equal(t, 5, result2)
|
|
||||||
}
|
|
||||||
74
agent/gpu.go
74
agent/gpu.go
@@ -27,10 +27,13 @@ const (
|
|||||||
nvidiaSmiInterval string = "4" // in seconds
|
nvidiaSmiInterval string = "4" // in seconds
|
||||||
tegraStatsInterval string = "3700" // in milliseconds
|
tegraStatsInterval string = "3700" // in milliseconds
|
||||||
rocmSmiInterval time.Duration = 4300 * time.Millisecond
|
rocmSmiInterval time.Duration = 4300 * time.Millisecond
|
||||||
|
|
||||||
// Command retry and timeout constants
|
// Command retry and timeout constants
|
||||||
retryWaitTime time.Duration = 5 * time.Second
|
retryWaitTime time.Duration = 5 * time.Second
|
||||||
maxFailureRetries int = 5
|
maxFailureRetries int = 5
|
||||||
|
|
||||||
|
cmdBufferSize uint16 = 10 * 1024
|
||||||
|
|
||||||
// Unit Conversions
|
// Unit Conversions
|
||||||
mebibytesInAMegabyte float64 = 1.024 // nvidia-smi reports memory in MiB
|
mebibytesInAMegabyte float64 = 1.024 // nvidia-smi reports memory in MiB
|
||||||
milliwattsInAWatt float64 = 1000.0 // tegrastats reports power in mW
|
milliwattsInAWatt float64 = 1000.0 // tegrastats reports power in mW
|
||||||
@@ -39,11 +42,10 @@ const (
|
|||||||
// GPUManager manages data collection for GPUs (either Nvidia or AMD)
|
// GPUManager manages data collection for GPUs (either Nvidia or AMD)
|
||||||
type GPUManager struct {
|
type GPUManager struct {
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
nvidiaSmi bool
|
nvidiaSmi bool
|
||||||
rocmSmi bool
|
rocmSmi bool
|
||||||
tegrastats bool
|
tegrastats bool
|
||||||
intelGpuStats bool
|
GpuDataMap map[string]*system.GPUData
|
||||||
GpuDataMap map[string]*system.GPUData
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RocmSmiJson represents the JSON structure of rocm-smi output
|
// RocmSmiJson represents the JSON structure of rocm-smi output
|
||||||
@@ -64,7 +66,6 @@ type gpuCollector struct {
|
|||||||
cmdArgs []string
|
cmdArgs []string
|
||||||
parse func([]byte) bool // returns true if valid data was found
|
parse func([]byte) bool // returns true if valid data was found
|
||||||
buf []byte
|
buf []byte
|
||||||
bufSize uint16
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var errNoValidData = fmt.Errorf("no valid GPU data found") // Error for missing data
|
var errNoValidData = fmt.Errorf("no valid GPU data found") // Error for missing data
|
||||||
@@ -98,7 +99,7 @@ func (c *gpuCollector) collect() error {
|
|||||||
|
|
||||||
scanner := bufio.NewScanner(stdout)
|
scanner := bufio.NewScanner(stdout)
|
||||||
if c.buf == nil {
|
if c.buf == nil {
|
||||||
c.buf = make([]byte, 0, c.bufSize)
|
c.buf = make([]byte, 0, cmdBufferSize)
|
||||||
}
|
}
|
||||||
scanner.Buffer(c.buf, bufio.MaxScanTokenSize)
|
scanner.Buffer(c.buf, bufio.MaxScanTokenSize)
|
||||||
|
|
||||||
@@ -243,31 +244,20 @@ func (gm *GPUManager) GetCurrentData() map[string]system.GPUData {
|
|||||||
// copy / reset the data
|
// copy / reset the data
|
||||||
gpuData := make(map[string]system.GPUData, len(gm.GpuDataMap))
|
gpuData := make(map[string]system.GPUData, len(gm.GpuDataMap))
|
||||||
for id, gpu := range gm.GpuDataMap {
|
for id, gpu := range gm.GpuDataMap {
|
||||||
// avoid division by zero
|
|
||||||
count := max(gpu.Count, 1)
|
|
||||||
|
|
||||||
// average the data
|
|
||||||
gpuAvg := *gpu
|
gpuAvg := *gpu
|
||||||
gpuAvg.Temperature = twoDecimals(gpu.Temperature)
|
|
||||||
gpuAvg.Power = twoDecimals(gpu.Power / count)
|
|
||||||
|
|
||||||
// intel gpu stats doesn't provide usage, memory used, or memory total
|
gpuAvg.Temperature = twoDecimals(gpu.Temperature)
|
||||||
if gm.intelGpuStats {
|
gpuAvg.MemoryUsed = twoDecimals(gpu.MemoryUsed)
|
||||||
maxEngineUsage := 0.0
|
gpuAvg.MemoryTotal = twoDecimals(gpu.MemoryTotal)
|
||||||
for name, engine := range gpu.Engines {
|
|
||||||
gpuAvg.Engines[name] = twoDecimals(engine / count)
|
// avoid division by zero
|
||||||
maxEngineUsage = max(maxEngineUsage, engine/count)
|
if gpu.Count > 0 {
|
||||||
}
|
gpuAvg.Usage = twoDecimals(gpu.Usage / gpu.Count)
|
||||||
gpuAvg.Usage = twoDecimals(maxEngineUsage)
|
gpuAvg.Power = twoDecimals(gpu.Power / gpu.Count)
|
||||||
} else {
|
|
||||||
gpuAvg.Usage = twoDecimals(gpu.Usage / count)
|
|
||||||
gpuAvg.MemoryUsed = twoDecimals(gpu.MemoryUsed)
|
|
||||||
gpuAvg.MemoryTotal = twoDecimals(gpu.MemoryTotal)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset accumulators in the original gpu data for next collection
|
// reset accumulators in the original
|
||||||
gpu.Usage, gpu.Power, gpu.Count = gpuAvg.Usage, gpuAvg.Power, 1
|
gpu.Usage, gpu.Power, gpu.Count = 0, 0, 0
|
||||||
gpu.Engines = gpuAvg.Engines
|
|
||||||
|
|
||||||
// append id to the name if there are multiple GPUs with the same name
|
// append id to the name if there are multiple GPUs with the same name
|
||||||
if nameCounts[gpu.Name] > 1 {
|
if nameCounts[gpu.Name] > 1 {
|
||||||
@@ -294,37 +284,18 @@ func (gm *GPUManager) detectGPUs() error {
|
|||||||
gm.tegrastats = true
|
gm.tegrastats = true
|
||||||
gm.nvidiaSmi = false
|
gm.nvidiaSmi = false
|
||||||
}
|
}
|
||||||
if _, err := exec.LookPath(intelGpuStatsCmd); err == nil {
|
if gm.nvidiaSmi || gm.rocmSmi || gm.tegrastats {
|
||||||
gm.intelGpuStats = true
|
|
||||||
}
|
|
||||||
if gm.nvidiaSmi || gm.rocmSmi || gm.tegrastats || gm.intelGpuStats {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return fmt.Errorf("no GPU found - install nvidia-smi, rocm-smi, tegrastats, or intel_gpu_top")
|
return fmt.Errorf("no GPU found - install nvidia-smi, rocm-smi, or tegrastats")
|
||||||
}
|
}
|
||||||
|
|
||||||
// startCollector starts the appropriate GPU data collector based on the command
|
// startCollector starts the appropriate GPU data collector based on the command
|
||||||
func (gm *GPUManager) startCollector(command string) {
|
func (gm *GPUManager) startCollector(command string) {
|
||||||
collector := gpuCollector{
|
collector := gpuCollector{
|
||||||
name: command,
|
name: command,
|
||||||
bufSize: 10 * 1024,
|
|
||||||
}
|
}
|
||||||
switch command {
|
switch command {
|
||||||
case intelGpuStatsCmd:
|
|
||||||
go func() {
|
|
||||||
failures := 0
|
|
||||||
for {
|
|
||||||
if err := gm.collectIntelStats(); err != nil {
|
|
||||||
failures++
|
|
||||||
if failures > maxFailureRetries {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
slog.Warn("Error collecting Intel GPU data; see https://beszel.dev/guide/gpu", "err", err)
|
|
||||||
time.Sleep(retryWaitTime)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
case nvidiaSmiCmd:
|
case nvidiaSmiCmd:
|
||||||
collector.cmdArgs = []string{
|
collector.cmdArgs = []string{
|
||||||
"-l", nvidiaSmiInterval,
|
"-l", nvidiaSmiInterval,
|
||||||
@@ -373,9 +344,6 @@ func NewGPUManager() (*GPUManager, error) {
|
|||||||
if gm.tegrastats {
|
if gm.tegrastats {
|
||||||
gm.startCollector(tegraStatsCmd)
|
gm.startCollector(tegraStatsCmd)
|
||||||
}
|
}
|
||||||
if gm.intelGpuStats {
|
|
||||||
gm.startCollector(intelGpuStatsCmd)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &gm, nil
|
return &gm, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,102 +0,0 @@
|
|||||||
package agent
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"os/exec"
|
|
||||||
|
|
||||||
"github.com/henrygd/beszel/internal/entities/system"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
intelGpuStatsCmd string = "intel_gpu_top"
|
|
||||||
intelGpuStatsInterval string = "3300" // in milliseconds
|
|
||||||
)
|
|
||||||
|
|
||||||
type intelGpuStats struct {
|
|
||||||
Power struct {
|
|
||||||
GPU float64 `json:"GPU"`
|
|
||||||
} `json:"power"`
|
|
||||||
Engines map[string]struct {
|
|
||||||
Busy float64 `json:"busy"`
|
|
||||||
} `json:"engines"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// updateIntelFromStats updates aggregated GPU data from a single intelGpuStats sample
|
|
||||||
func (gm *GPUManager) updateIntelFromStats(sample *intelGpuStats) bool {
|
|
||||||
gm.Lock()
|
|
||||||
defer gm.Unlock()
|
|
||||||
|
|
||||||
// only one gpu for now - cmd doesn't provide all by default
|
|
||||||
gpuData, ok := gm.GpuDataMap["0"]
|
|
||||||
if !ok {
|
|
||||||
gpuData = &system.GPUData{Name: "GPU", Engines: make(map[string]float64)}
|
|
||||||
gm.GpuDataMap["0"] = gpuData
|
|
||||||
}
|
|
||||||
|
|
||||||
if sample.Power.GPU > 0 {
|
|
||||||
gpuData.Power += sample.Power.GPU
|
|
||||||
}
|
|
||||||
|
|
||||||
if gpuData.Engines == nil {
|
|
||||||
gpuData.Engines = make(map[string]float64, len(sample.Engines))
|
|
||||||
}
|
|
||||||
for name, engine := range sample.Engines {
|
|
||||||
gpuData.Engines[name] += engine.Busy
|
|
||||||
}
|
|
||||||
|
|
||||||
gpuData.Count++
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// collectIntelStats executes intel_gpu_top in JSON mode and stream-decodes the array of samples
|
|
||||||
func (gm *GPUManager) collectIntelStats() error {
|
|
||||||
cmd := exec.Command(intelGpuStatsCmd, "-s", intelGpuStatsInterval, "-J")
|
|
||||||
stdout, err := cmd.StdoutPipe()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := cmd.Start(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
dec := json.NewDecoder(stdout)
|
|
||||||
|
|
||||||
// Expect a JSON array stream: [ { ... }, { ... }, ... ]
|
|
||||||
tok, err := dec.Token()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if delim, ok := tok.(json.Delim); !ok || delim != '[' {
|
|
||||||
return fmt.Errorf("unexpected JSON start token: %v", tok)
|
|
||||||
}
|
|
||||||
|
|
||||||
var sample intelGpuStats
|
|
||||||
for {
|
|
||||||
if dec.More() {
|
|
||||||
// Clear the engines map before decoding
|
|
||||||
if sample.Engines != nil {
|
|
||||||
for k := range sample.Engines {
|
|
||||||
delete(sample.Engines, k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := dec.Decode(&sample); err != nil {
|
|
||||||
return fmt.Errorf("decode intel gpu: %w", err)
|
|
||||||
}
|
|
||||||
gm.updateIntelFromStats(&sample)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// Attempt to read closing bracket (will only be present when process exits)
|
|
||||||
tok, err = dec.Token()
|
|
||||||
if err != nil {
|
|
||||||
// When the process is still running, decoder will block in More/Decode; any error here is terminal
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if delim, ok := tok.(json.Delim); ok && delim == ']' {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return cmd.Wait()
|
|
||||||
}
|
|
||||||
@@ -792,96 +792,3 @@ func TestAccumulation(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIntelUpdateFromStats(t *testing.T) {
|
|
||||||
gm := &GPUManager{
|
|
||||||
GpuDataMap: make(map[string]*system.GPUData),
|
|
||||||
}
|
|
||||||
|
|
||||||
// First sample with power and two engines
|
|
||||||
sample1 := intelGpuStats{
|
|
||||||
Engines: map[string]struct {
|
|
||||||
Busy float64 `json:"busy"`
|
|
||||||
}{
|
|
||||||
"Render/3D": {Busy: 20.0},
|
|
||||||
"Video": {Busy: 5.0},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
sample1.Power.GPU = 10.5
|
|
||||||
|
|
||||||
ok := gm.updateIntelFromStats(&sample1)
|
|
||||||
assert.True(t, ok)
|
|
||||||
|
|
||||||
gpu := gm.GpuDataMap["0"]
|
|
||||||
require.NotNil(t, gpu)
|
|
||||||
assert.Equal(t, "GPU", gpu.Name)
|
|
||||||
assert.InDelta(t, 10.5, gpu.Power, 0.001)
|
|
||||||
assert.InDelta(t, 20.0, gpu.Engines["Render/3D"], 0.001)
|
|
||||||
assert.InDelta(t, 5.0, gpu.Engines["Video"], 0.001)
|
|
||||||
assert.Equal(t, float64(1), gpu.Count)
|
|
||||||
|
|
||||||
// Second sample with zero power (should not add) and additional engine busy
|
|
||||||
sample2 := intelGpuStats{
|
|
||||||
Engines: map[string]struct {
|
|
||||||
Busy float64 `json:"busy"`
|
|
||||||
}{
|
|
||||||
"Render/3D": {Busy: 10.0},
|
|
||||||
"Video": {Busy: 2.5},
|
|
||||||
"Blitter": {Busy: 1.0},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
// zero power should not increment power accumulator
|
|
||||||
sample2.Power.GPU = 0.0
|
|
||||||
|
|
||||||
ok = gm.updateIntelFromStats(&sample2)
|
|
||||||
assert.True(t, ok)
|
|
||||||
|
|
||||||
gpu = gm.GpuDataMap["0"]
|
|
||||||
require.NotNil(t, gpu)
|
|
||||||
assert.InDelta(t, 10.5, gpu.Power, 0.001)
|
|
||||||
assert.InDelta(t, 30.0, gpu.Engines["Render/3D"], 0.001) // 20 + 10
|
|
||||||
assert.InDelta(t, 7.5, gpu.Engines["Video"], 0.001) // 5 + 2.5
|
|
||||||
assert.InDelta(t, 1.0, gpu.Engines["Blitter"], 0.001)
|
|
||||||
assert.Equal(t, float64(2), gpu.Count)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIntelCollectorStreaming(t *testing.T) {
|
|
||||||
// Save and override PATH
|
|
||||||
origPath := os.Getenv("PATH")
|
|
||||||
defer os.Setenv("PATH", origPath)
|
|
||||||
|
|
||||||
dir := t.TempDir()
|
|
||||||
os.Setenv("PATH", dir)
|
|
||||||
|
|
||||||
// Create a fake intel_gpu_top that prints a JSON array with two samples and exits
|
|
||||||
scriptPath := filepath.Join(dir, "intel_gpu_top")
|
|
||||||
script := `#!/bin/sh
|
|
||||||
# Ignore args -s and -J
|
|
||||||
# Emit a JSON array with two objects, separated by a comma, then exit
|
|
||||||
(echo '['; \
|
|
||||||
echo '{"power":{"GPU":1.5},"engines":{"Render/3D":{"busy":12.34}}},'; \
|
|
||||||
echo '{"power":{"GPU":2.0},"engines":{"Video":{"busy":5}}}'; \
|
|
||||||
echo ']')`
|
|
||||||
if err := os.WriteFile(scriptPath, []byte(script), 0755); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
gm := &GPUManager{
|
|
||||||
GpuDataMap: make(map[string]*system.GPUData),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run the collector once; it should read two samples and return
|
|
||||||
if err := gm.collectIntelStats(); err != nil {
|
|
||||||
t.Fatalf("collectIntelStats error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
gpu := gm.GpuDataMap["0"]
|
|
||||||
require.NotNil(t, gpu)
|
|
||||||
// Power should be sum of non-zero samples: 1.5 + 2.0 = 3.5
|
|
||||||
assert.InDelta(t, 3.5, gpu.Power, 0.001)
|
|
||||||
// Engines aggregated
|
|
||||||
assert.InDelta(t, 12.34, gpu.Engines["Render/3D"], 0.001)
|
|
||||||
assert.InDelta(t, 5.0, gpu.Engines["Video"], 0.001)
|
|
||||||
// Count should be 2 samples
|
|
||||||
assert.Equal(t, float64(2), gpu.Count)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,90 +1,13 @@
|
|||||||
package agent
|
package agent
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/henrygd/beszel/agent/deltatracker"
|
|
||||||
"github.com/henrygd/beszel/internal/entities/system"
|
|
||||||
psutilNet "github.com/shirou/gopsutil/v4/net"
|
psutilNet "github.com/shirou/gopsutil/v4/net"
|
||||||
)
|
)
|
||||||
|
|
||||||
var netInterfaceDeltaTracker = deltatracker.NewDeltaTracker[string, uint64]()
|
|
||||||
|
|
||||||
func (a *Agent) updateNetworkStats(systemStats *system.Stats) {
|
|
||||||
// network stats
|
|
||||||
if len(a.netInterfaces) == 0 {
|
|
||||||
// if no network interfaces, initialize again
|
|
||||||
// this is a fix if agent started before network is online (#466)
|
|
||||||
// maybe refactor this in the future to not cache interface names at all so we
|
|
||||||
// don't miss an interface that's been added after agent started in any circumstance
|
|
||||||
a.initializeNetIoStats()
|
|
||||||
}
|
|
||||||
|
|
||||||
if systemStats.NetworkInterfaces == nil {
|
|
||||||
systemStats.NetworkInterfaces = make(map[string][4]uint64, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
if netIO, err := psutilNet.IOCounters(true); err == nil {
|
|
||||||
msElapsed := uint64(time.Since(a.netIoStats.Time).Milliseconds())
|
|
||||||
a.netIoStats.Time = time.Now()
|
|
||||||
totalBytesSent := uint64(0)
|
|
||||||
totalBytesRecv := uint64(0)
|
|
||||||
netInterfaceDeltaTracker.Cycle()
|
|
||||||
// sum all bytes sent and received
|
|
||||||
for _, v := range netIO {
|
|
||||||
// skip if not in valid network interfaces list
|
|
||||||
if _, exists := a.netInterfaces[v.Name]; !exists {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
totalBytesSent += v.BytesSent
|
|
||||||
totalBytesRecv += v.BytesRecv
|
|
||||||
|
|
||||||
// track deltas for each network interface
|
|
||||||
var upDelta, downDelta uint64
|
|
||||||
upKey, downKey := fmt.Sprintf("%sup", v.Name), fmt.Sprintf("%sdown", v.Name)
|
|
||||||
netInterfaceDeltaTracker.Set(upKey, v.BytesSent)
|
|
||||||
netInterfaceDeltaTracker.Set(downKey, v.BytesRecv)
|
|
||||||
if msElapsed > 0 {
|
|
||||||
upDelta = netInterfaceDeltaTracker.Delta(upKey) * 1000 / msElapsed
|
|
||||||
downDelta = netInterfaceDeltaTracker.Delta(downKey) * 1000 / msElapsed
|
|
||||||
}
|
|
||||||
// add interface to systemStats
|
|
||||||
systemStats.NetworkInterfaces[v.Name] = [4]uint64{upDelta, downDelta, v.BytesSent, v.BytesRecv}
|
|
||||||
}
|
|
||||||
|
|
||||||
// add to systemStats
|
|
||||||
var bytesSentPerSecond, bytesRecvPerSecond uint64
|
|
||||||
if msElapsed > 0 {
|
|
||||||
bytesSentPerSecond = (totalBytesSent - a.netIoStats.BytesSent) * 1000 / msElapsed
|
|
||||||
bytesRecvPerSecond = (totalBytesRecv - a.netIoStats.BytesRecv) * 1000 / msElapsed
|
|
||||||
}
|
|
||||||
networkSentPs := bytesToMegabytes(float64(bytesSentPerSecond))
|
|
||||||
networkRecvPs := bytesToMegabytes(float64(bytesRecvPerSecond))
|
|
||||||
// add check for issue (#150) where sent is a massive number
|
|
||||||
if networkSentPs > 10_000 || networkRecvPs > 10_000 {
|
|
||||||
slog.Warn("Invalid net stats. Resetting.", "sent", networkSentPs, "recv", networkRecvPs)
|
|
||||||
for _, v := range netIO {
|
|
||||||
if _, exists := a.netInterfaces[v.Name]; !exists {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
slog.Info(v.Name, "recv", v.BytesRecv, "sent", v.BytesSent)
|
|
||||||
}
|
|
||||||
// reset network I/O stats
|
|
||||||
a.initializeNetIoStats()
|
|
||||||
} else {
|
|
||||||
systemStats.NetworkSent = networkSentPs
|
|
||||||
systemStats.NetworkRecv = networkRecvPs
|
|
||||||
systemStats.Bandwidth[0], systemStats.Bandwidth[1] = bytesSentPerSecond, bytesRecvPerSecond
|
|
||||||
// update netIoStats
|
|
||||||
a.netIoStats.BytesSent = totalBytesSent
|
|
||||||
a.netIoStats.BytesRecv = totalBytesRecv
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Agent) initializeNetIoStats() {
|
func (a *Agent) initializeNetIoStats() {
|
||||||
// reset valid network interfaces
|
// reset valid network interfaces
|
||||||
a.netInterfaces = make(map[string]struct{}, 0)
|
a.netInterfaces = make(map[string]struct{}, 0)
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import (
|
|||||||
"github.com/shirou/gopsutil/v4/host"
|
"github.com/shirou/gopsutil/v4/host"
|
||||||
"github.com/shirou/gopsutil/v4/load"
|
"github.com/shirou/gopsutil/v4/load"
|
||||||
"github.com/shirou/gopsutil/v4/mem"
|
"github.com/shirou/gopsutil/v4/mem"
|
||||||
|
psutilNet "github.com/shirou/gopsutil/v4/net"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Sets initial / non-changing values about the host system
|
// Sets initial / non-changing values about the host system
|
||||||
@@ -31,7 +32,7 @@ func (a *Agent) initializeSystemInfo() {
|
|||||||
a.systemInfo.KernelVersion = version
|
a.systemInfo.KernelVersion = version
|
||||||
a.systemInfo.Os = system.Darwin
|
a.systemInfo.Os = system.Darwin
|
||||||
} else if strings.Contains(platform, "indows") {
|
} else if strings.Contains(platform, "indows") {
|
||||||
a.systemInfo.KernelVersion = fmt.Sprintf("%s %s", strings.Replace(platform, "Microsoft ", "", 1), version)
|
a.systemInfo.KernelVersion = strings.Replace(platform, "Microsoft ", "", 1) + " " + version
|
||||||
a.systemInfo.Os = system.Windows
|
a.systemInfo.Os = system.Windows
|
||||||
} else if platform == "freebsd" {
|
} else if platform == "freebsd" {
|
||||||
a.systemInfo.Os = system.Freebsd
|
a.systemInfo.Os = system.Freebsd
|
||||||
@@ -69,7 +70,7 @@ func (a *Agent) initializeSystemInfo() {
|
|||||||
|
|
||||||
// Returns current info, stats about the host system
|
// Returns current info, stats about the host system
|
||||||
func (a *Agent) getSystemStats() system.Stats {
|
func (a *Agent) getSystemStats() system.Stats {
|
||||||
var systemStats system.Stats
|
systemStats := system.Stats{}
|
||||||
|
|
||||||
// battery
|
// battery
|
||||||
if battery.HasReadableBattery() {
|
if battery.HasReadableBattery() {
|
||||||
@@ -172,7 +173,55 @@ func (a *Agent) getSystemStats() system.Stats {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// network stats
|
// network stats
|
||||||
a.updateNetworkStats(&systemStats)
|
if len(a.netInterfaces) == 0 {
|
||||||
|
// if no network interfaces, initialize again
|
||||||
|
// this is a fix if agent started before network is online (#466)
|
||||||
|
// maybe refactor this in the future to not cache interface names at all so we
|
||||||
|
// don't miss an interface that's been added after agent started in any circumstance
|
||||||
|
a.initializeNetIoStats()
|
||||||
|
}
|
||||||
|
if netIO, err := psutilNet.IOCounters(true); err == nil {
|
||||||
|
msElapsed := uint64(time.Since(a.netIoStats.Time).Milliseconds())
|
||||||
|
a.netIoStats.Time = time.Now()
|
||||||
|
totalBytesSent := uint64(0)
|
||||||
|
totalBytesRecv := uint64(0)
|
||||||
|
// sum all bytes sent and received
|
||||||
|
for _, v := range netIO {
|
||||||
|
// skip if not in valid network interfaces list
|
||||||
|
if _, exists := a.netInterfaces[v.Name]; !exists {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
totalBytesSent += v.BytesSent
|
||||||
|
totalBytesRecv += v.BytesRecv
|
||||||
|
}
|
||||||
|
// add to systemStats
|
||||||
|
var bytesSentPerSecond, bytesRecvPerSecond uint64
|
||||||
|
if msElapsed > 0 {
|
||||||
|
bytesSentPerSecond = (totalBytesSent - a.netIoStats.BytesSent) * 1000 / msElapsed
|
||||||
|
bytesRecvPerSecond = (totalBytesRecv - a.netIoStats.BytesRecv) * 1000 / msElapsed
|
||||||
|
}
|
||||||
|
networkSentPs := bytesToMegabytes(float64(bytesSentPerSecond))
|
||||||
|
networkRecvPs := bytesToMegabytes(float64(bytesRecvPerSecond))
|
||||||
|
// add check for issue (#150) where sent is a massive number
|
||||||
|
if networkSentPs > 10_000 || networkRecvPs > 10_000 {
|
||||||
|
slog.Warn("Invalid net stats. Resetting.", "sent", networkSentPs, "recv", networkRecvPs)
|
||||||
|
for _, v := range netIO {
|
||||||
|
if _, exists := a.netInterfaces[v.Name]; !exists {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
slog.Info(v.Name, "recv", v.BytesRecv, "sent", v.BytesSent)
|
||||||
|
}
|
||||||
|
// reset network I/O stats
|
||||||
|
a.initializeNetIoStats()
|
||||||
|
} else {
|
||||||
|
systemStats.NetworkSent = networkSentPs
|
||||||
|
systemStats.NetworkRecv = networkRecvPs
|
||||||
|
systemStats.Bandwidth[0], systemStats.Bandwidth[1] = bytesSentPerSecond, bytesRecvPerSecond
|
||||||
|
// update netIoStats
|
||||||
|
a.netIoStats.BytesSent = totalBytesSent
|
||||||
|
a.netIoStats.BytesRecv = totalBytesRecv
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// temperatures
|
// temperatures
|
||||||
// TODO: maybe refactor to methods on systemStats
|
// TODO: maybe refactor to methods on systemStats
|
||||||
@@ -212,7 +261,6 @@ func (a *Agent) getSystemStats() system.Stats {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// update base system info
|
// update base system info
|
||||||
a.systemInfo.ConnectionType = a.connectionManager.ConnectionType
|
|
||||||
a.systemInfo.Cpu = systemStats.Cpu
|
a.systemInfo.Cpu = systemStats.Cpu
|
||||||
a.systemInfo.LoadAvg = systemStats.LoadAvg
|
a.systemInfo.LoadAvg = systemStats.LoadAvg
|
||||||
// TODO: remove these in future release in favor of load avg array
|
// TODO: remove these in future release in favor of load avg array
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import "github.com/blang/semver"
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
// Version is the current version of the application.
|
// Version is the current version of the application.
|
||||||
Version = "0.12.9"
|
Version = "0.12.7"
|
||||||
// AppName is the name of the application.
|
// AppName is the name of the application.
|
||||||
AppName = "beszel"
|
AppName = "beszel"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -25,12 +25,7 @@ type alertInfo struct {
|
|||||||
// startWorker is a long-running goroutine that processes alert tasks
|
// startWorker is a long-running goroutine that processes alert tasks
|
||||||
// every x seconds. It must be running to process status alerts.
|
// every x seconds. It must be running to process status alerts.
|
||||||
func (am *AlertManager) startWorker() {
|
func (am *AlertManager) startWorker() {
|
||||||
processPendingAlerts := time.Tick(15 * time.Second)
|
tick := time.Tick(15 * time.Second)
|
||||||
|
|
||||||
// check for status alerts that are not resolved when system comes up
|
|
||||||
// (can be removed if we figure out core bug in #1052)
|
|
||||||
checkStatusAlerts := time.Tick(561 * time.Second)
|
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-am.stopChan:
|
case <-am.stopChan:
|
||||||
@@ -46,9 +41,7 @@ func (am *AlertManager) startWorker() {
|
|||||||
case "cancel":
|
case "cancel":
|
||||||
am.pendingAlerts.Delete(task.alertRecord.Id)
|
am.pendingAlerts.Delete(task.alertRecord.Id)
|
||||||
}
|
}
|
||||||
case <-checkStatusAlerts:
|
case <-tick:
|
||||||
resolveStatusAlerts(am.hub)
|
|
||||||
case <-processPendingAlerts:
|
|
||||||
// Check for expired alerts every tick
|
// Check for expired alerts every tick
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
for key, value := range am.pendingAlerts.Range {
|
for key, value := range am.pendingAlerts.Range {
|
||||||
@@ -177,35 +170,3 @@ func (am *AlertManager) sendStatusAlert(alertStatus string, systemName string, a
|
|||||||
LinkText: "View " + systemName,
|
LinkText: "View " + systemName,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// resolveStatusAlerts resolves any status alerts that weren't resolved
|
|
||||||
// when system came up (https://github.com/henrygd/beszel/issues/1052)
|
|
||||||
func resolveStatusAlerts(app core.App) error {
|
|
||||||
db := app.DB()
|
|
||||||
// Find all active status alerts where the system is actually up
|
|
||||||
var alertIds []string
|
|
||||||
err := db.NewQuery(`
|
|
||||||
SELECT a.id
|
|
||||||
FROM alerts a
|
|
||||||
JOIN systems s ON a.system = s.id
|
|
||||||
WHERE a.name = 'Status'
|
|
||||||
AND a.triggered = true
|
|
||||||
AND s.status = 'up'
|
|
||||||
`).Column(&alertIds)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// resolve all matching alert records
|
|
||||||
for _, alertId := range alertIds {
|
|
||||||
alert, err := app.FindRecordById("alerts", alertId)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
alert.Set("triggered", false)
|
|
||||||
err = app.Save(alert)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import (
|
|||||||
"testing/synctest"
|
"testing/synctest"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/henrygd/beszel/internal/alerts"
|
|
||||||
beszelTests "github.com/henrygd/beszel/internal/tests"
|
beszelTests "github.com/henrygd/beszel/internal/tests"
|
||||||
|
|
||||||
"github.com/pocketbase/dbx"
|
"github.com/pocketbase/dbx"
|
||||||
@@ -370,9 +369,33 @@ func TestUserAlertsApi(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getHubWithUser(t *testing.T) (*beszelTests.TestHub, *core.Record) {
|
||||||
|
hub, err := beszelTests.NewTestHub(t.TempDir())
|
||||||
|
assert.NoError(t, err)
|
||||||
|
hub.StartHub()
|
||||||
|
|
||||||
|
// Manually initialize the system manager to bind event hooks
|
||||||
|
err = hub.GetSystemManager().Initialize()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Create a test user
|
||||||
|
user, err := beszelTests.CreateUser(hub, "test@example.com", "password")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Create user settings for the test user (required for alert notifications)
|
||||||
|
userSettingsData := map[string]any{
|
||||||
|
"user": user.Id,
|
||||||
|
"settings": `{"emails":[test@example.com],"webhooks":[]}`,
|
||||||
|
}
|
||||||
|
_, err = beszelTests.CreateRecord(hub, "user_settings", userSettingsData)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
return hub, user
|
||||||
|
}
|
||||||
|
|
||||||
func TestStatusAlerts(t *testing.T) {
|
func TestStatusAlerts(t *testing.T) {
|
||||||
synctest.Test(t, func(t *testing.T) {
|
synctest.Test(t, func(t *testing.T) {
|
||||||
hub, user := beszelTests.GetHubWithUser(t)
|
hub, user := getHubWithUser(t)
|
||||||
defer hub.Cleanup()
|
defer hub.Cleanup()
|
||||||
|
|
||||||
systems, err := beszelTests.CreateSystems(hub, 4, user.Id, "paused")
|
systems, err := beszelTests.CreateSystems(hub, 4, user.Id, "paused")
|
||||||
@@ -453,7 +476,7 @@ func TestStatusAlerts(t *testing.T) {
|
|||||||
|
|
||||||
func TestAlertsHistory(t *testing.T) {
|
func TestAlertsHistory(t *testing.T) {
|
||||||
synctest.Test(t, func(t *testing.T) {
|
synctest.Test(t, func(t *testing.T) {
|
||||||
hub, user := beszelTests.GetHubWithUser(t)
|
hub, user := getHubWithUser(t)
|
||||||
defer hub.Cleanup()
|
defer hub.Cleanup()
|
||||||
|
|
||||||
// Create systems and alerts
|
// Create systems and alerts
|
||||||
@@ -579,102 +602,3 @@ func TestAlertsHistory(t *testing.T) {
|
|||||||
assert.EqualValues(t, 2, totalHistoryCount, "Should have 2 total alert history records")
|
assert.EqualValues(t, 2, totalHistoryCount, "Should have 2 total alert history records")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
func TestResolveStatusAlerts(t *testing.T) {
|
|
||||||
hub, user := beszelTests.GetHubWithUser(t)
|
|
||||||
defer hub.Cleanup()
|
|
||||||
|
|
||||||
// Create a systemUp
|
|
||||||
systemUp, err := beszelTests.CreateRecord(hub, "systems", map[string]any{
|
|
||||||
"name": "test-system",
|
|
||||||
"users": []string{user.Id},
|
|
||||||
"host": "127.0.0.1",
|
|
||||||
"status": "up",
|
|
||||||
})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
systemDown, err := beszelTests.CreateRecord(hub, "systems", map[string]any{
|
|
||||||
"name": "test-system-2",
|
|
||||||
"users": []string{user.Id},
|
|
||||||
"host": "127.0.0.2",
|
|
||||||
"status": "up",
|
|
||||||
})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
// Create a status alertUp for the system
|
|
||||||
alertUp, err := beszelTests.CreateRecord(hub, "alerts", map[string]any{
|
|
||||||
"name": "Status",
|
|
||||||
"system": systemUp.Id,
|
|
||||||
"user": user.Id,
|
|
||||||
"min": 1,
|
|
||||||
})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
alertDown, err := beszelTests.CreateRecord(hub, "alerts", map[string]any{
|
|
||||||
"name": "Status",
|
|
||||||
"system": systemDown.Id,
|
|
||||||
"user": user.Id,
|
|
||||||
"min": 1,
|
|
||||||
})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
// Verify alert is not triggered initially
|
|
||||||
assert.False(t, alertUp.GetBool("triggered"), "Alert should not be triggered initially")
|
|
||||||
|
|
||||||
// Set the system to 'up' (this should not trigger the alert)
|
|
||||||
systemUp.Set("status", "up")
|
|
||||||
err = hub.SaveNoValidate(systemUp)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
systemDown.Set("status", "down")
|
|
||||||
err = hub.SaveNoValidate(systemDown)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
// Wait a moment for any processing
|
|
||||||
time.Sleep(10 * time.Millisecond)
|
|
||||||
|
|
||||||
// Verify alertUp is still not triggered after setting system to up
|
|
||||||
alertUp, err = hub.FindFirstRecordByFilter("alerts", "id={:id}", dbx.Params{"id": alertUp.Id})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.False(t, alertUp.GetBool("triggered"), "Alert should not be triggered when system is up")
|
|
||||||
|
|
||||||
// Manually set both alerts triggered to true
|
|
||||||
alertUp.Set("triggered", true)
|
|
||||||
err = hub.SaveNoValidate(alertUp)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
alertDown.Set("triggered", true)
|
|
||||||
err = hub.SaveNoValidate(alertDown)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
// Verify we have exactly one alert with triggered true
|
|
||||||
triggeredCount, err := hub.CountRecords("alerts", dbx.HashExp{"triggered": true})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.EqualValues(t, 2, triggeredCount, "Should have exactly two alerts with triggered true")
|
|
||||||
|
|
||||||
// Verify the specific alertUp is triggered
|
|
||||||
alertUp, err = hub.FindFirstRecordByFilter("alerts", "id={:id}", dbx.Params{"id": alertUp.Id})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.True(t, alertUp.GetBool("triggered"), "Alert should be triggered")
|
|
||||||
|
|
||||||
// Verify we have two unresolved alert history records
|
|
||||||
alertHistoryCount, err := hub.CountRecords("alerts_history", dbx.HashExp{"resolved": ""})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.EqualValues(t, 2, alertHistoryCount, "Should have exactly two unresolved alert history records")
|
|
||||||
|
|
||||||
err = alerts.ResolveStatusAlerts(hub)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
// Verify alertUp is not triggered after resolving
|
|
||||||
alertUp, err = hub.FindFirstRecordByFilter("alerts", "id={:id}", dbx.Params{"id": alertUp.Id})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.False(t, alertUp.GetBool("triggered"), "Alert should not be triggered after resolving")
|
|
||||||
// Verify alertDown is still triggered
|
|
||||||
alertDown, err = hub.FindFirstRecordByFilter("alerts", "id={:id}", dbx.Params{"id": alertDown.Id})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.True(t, alertDown.GetBool("triggered"), "Alert should still be triggered after resolving")
|
|
||||||
|
|
||||||
// Verify we have one unresolved alert history record
|
|
||||||
alertHistoryCount, err = hub.CountRecords("alerts_history", dbx.HashExp{"resolved": ""})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.EqualValues(t, 1, alertHistoryCount, "Should have exactly one unresolved alert history record")
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
//go:build testing
|
|
||||||
// +build testing
|
|
||||||
|
|
||||||
package alerts
|
package alerts
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -56,7 +53,3 @@ func (am *AlertManager) ForceExpirePendingAlerts() {
|
|||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func ResolveStatusAlerts(app core.App) error {
|
|
||||||
return resolveStatusAlerts(app)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -2,18 +2,15 @@ FROM --platform=$BUILDPLATFORM golang:alpine AS builder
|
|||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY go.mod go.sum ./
|
||||||
COPY ../go.mod ../go.sum ./
|
# RUN go mod download
|
||||||
RUN go mod download
|
COPY *.go ./
|
||||||
|
COPY cmd ./cmd
|
||||||
# Copy source files
|
COPY internal ./internal
|
||||||
COPY . ./
|
|
||||||
|
|
||||||
# Build
|
# Build
|
||||||
ARG TARGETOS TARGETARCH
|
ARG TARGETOS TARGETARCH
|
||||||
RUN CGO_ENABLED=0 GOGC=75 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -ldflags "-w -s" -o /agent ./internal/cmd/agent
|
RUN CGO_ENABLED=0 GOGC=75 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -ldflags "-w -s" -o /agent ./cmd/agent
|
||||||
|
|
||||||
RUN rm -rf /tmp/*
|
|
||||||
|
|
||||||
# --------------------------
|
# --------------------------
|
||||||
# Final image: GPU-enabled agent with nvidia-smi
|
# Final image: GPU-enabled agent with nvidia-smi
|
||||||
@@ -21,7 +18,4 @@ RUN rm -rf /tmp/*
|
|||||||
FROM nvidia/cuda:12.2.2-base-ubuntu22.04
|
FROM nvidia/cuda:12.2.2-base-ubuntu22.04
|
||||||
COPY --from=builder /agent /agent
|
COPY --from=builder /agent /agent
|
||||||
|
|
||||||
# this is so we don't need to create the /tmp directory in the scratch container
|
|
||||||
COPY --from=builder /tmp /tmp
|
|
||||||
|
|
||||||
ENTRYPOINT ["/agent"]
|
ENTRYPOINT ["/agent"]
|
||||||
|
|||||||
@@ -38,21 +38,19 @@ type Stats struct {
|
|||||||
Bandwidth [2]uint64 `json:"b,omitzero" cbor:"26,keyasint,omitzero"` // [sent bytes, recv bytes]
|
Bandwidth [2]uint64 `json:"b,omitzero" cbor:"26,keyasint,omitzero"` // [sent bytes, recv bytes]
|
||||||
MaxBandwidth [2]uint64 `json:"bm,omitzero" cbor:"27,keyasint,omitzero"` // [sent bytes, recv bytes]
|
MaxBandwidth [2]uint64 `json:"bm,omitzero" cbor:"27,keyasint,omitzero"` // [sent bytes, recv bytes]
|
||||||
// TODO: remove other load fields in future release in favor of load avg array
|
// TODO: remove other load fields in future release in favor of load avg array
|
||||||
LoadAvg [3]float64 `json:"la,omitempty" cbor:"28,keyasint"`
|
LoadAvg [3]float64 `json:"la,omitempty" cbor:"28,keyasint"`
|
||||||
Battery [2]uint8 `json:"bat,omitzero" cbor:"29,keyasint,omitzero"` // [percent, charge state, current]
|
Battery [2]uint8 `json:"bat,omitzero" cbor:"29,keyasint,omitzero"` // [percent, charge state, current]
|
||||||
MaxMem float64 `json:"mm,omitempty" cbor:"30,keyasint,omitempty"`
|
MaxMem float64 `json:"mm,omitempty" cbor:"30,keyasint,omitempty"`
|
||||||
NetworkInterfaces map[string][4]uint64 `json:"ni,omitempty" cbor:"31,keyasint,omitempty"` // [upload bytes, download bytes, total upload, total download]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type GPUData struct {
|
type GPUData struct {
|
||||||
Name string `json:"n" cbor:"0,keyasint"`
|
Name string `json:"n" cbor:"0,keyasint"`
|
||||||
Temperature float64 `json:"-"`
|
Temperature float64 `json:"-"`
|
||||||
MemoryUsed float64 `json:"mu,omitempty,omitzero" cbor:"1,keyasint,omitempty,omitzero"`
|
MemoryUsed float64 `json:"mu,omitempty" cbor:"1,keyasint,omitempty"`
|
||||||
MemoryTotal float64 `json:"mt,omitempty,omitzero" cbor:"2,keyasint,omitempty,omitzero"`
|
MemoryTotal float64 `json:"mt,omitempty" cbor:"2,keyasint,omitempty"`
|
||||||
Usage float64 `json:"u" cbor:"3,keyasint,omitempty"`
|
Usage float64 `json:"u" cbor:"3,keyasint"`
|
||||||
Power float64 `json:"p,omitempty" cbor:"4,keyasint,omitempty"`
|
Power float64 `json:"p,omitempty" cbor:"4,keyasint,omitempty"`
|
||||||
Count float64 `json:"-"`
|
Count float64 `json:"-"`
|
||||||
Engines map[string]float64 `json:"e,omitempty" cbor:"5,keyasint,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type FsStats struct {
|
type FsStats struct {
|
||||||
@@ -85,14 +83,6 @@ const (
|
|||||||
Freebsd
|
Freebsd
|
||||||
)
|
)
|
||||||
|
|
||||||
type ConnectionType = uint8
|
|
||||||
|
|
||||||
const (
|
|
||||||
ConnectionTypeNone ConnectionType = iota
|
|
||||||
ConnectionTypeSSH
|
|
||||||
ConnectionTypeWebSocket
|
|
||||||
)
|
|
||||||
|
|
||||||
type Info struct {
|
type Info struct {
|
||||||
Hostname string `json:"h" cbor:"0,keyasint"`
|
Hostname string `json:"h" cbor:"0,keyasint"`
|
||||||
KernelVersion string `json:"k,omitempty" cbor:"1,keyasint,omitempty"`
|
KernelVersion string `json:"k,omitempty" cbor:"1,keyasint,omitempty"`
|
||||||
@@ -114,8 +104,7 @@ type Info struct {
|
|||||||
LoadAvg15 float64 `json:"l15,omitempty" cbor:"17,keyasint,omitempty"`
|
LoadAvg15 float64 `json:"l15,omitempty" cbor:"17,keyasint,omitempty"`
|
||||||
BandwidthBytes uint64 `json:"bb" cbor:"18,keyasint"`
|
BandwidthBytes uint64 `json:"bb" cbor:"18,keyasint"`
|
||||||
// TODO: remove load fields in future release in favor of load avg array
|
// TODO: remove load fields in future release in favor of load avg array
|
||||||
LoadAvg [3]float64 `json:"la,omitempty" cbor:"19,keyasint"`
|
LoadAvg [3]float64 `json:"la,omitempty" cbor:"19,keyasint"`
|
||||||
ConnectionType ConnectionType `json:"ct,omitempty" cbor:"20,keyasint,omitempty,omitzero"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Final data structure to return to the hub
|
// Final data structure to return to the hub
|
||||||
|
|||||||
@@ -175,31 +175,35 @@ func (h *Hub) registerCronJobs(_ *core.ServeEvent) error {
|
|||||||
|
|
||||||
// custom middlewares
|
// custom middlewares
|
||||||
func (h *Hub) registerMiddlewares(se *core.ServeEvent) {
|
func (h *Hub) registerMiddlewares(se *core.ServeEvent) {
|
||||||
// authorizes request with user matching the provided email
|
|
||||||
authorizeRequestWithEmail := func(e *core.RequestEvent, email string) (err error) {
|
|
||||||
if e.Auth != nil || email == "" {
|
|
||||||
return e.Next()
|
|
||||||
}
|
|
||||||
isAuthRefresh := e.Request.URL.Path == "/api/collections/users/auth-refresh" && e.Request.Method == http.MethodPost
|
|
||||||
e.Auth, err = e.App.FindFirstRecordByData("users", "email", email)
|
|
||||||
if err != nil || !isAuthRefresh {
|
|
||||||
return e.Next()
|
|
||||||
}
|
|
||||||
// auth refresh endpoint, make sure token is set in header
|
|
||||||
token, _ := e.Auth.NewAuthToken()
|
|
||||||
e.Request.Header.Set("Authorization", token)
|
|
||||||
return e.Next()
|
|
||||||
}
|
|
||||||
// authenticate with trusted header
|
|
||||||
if autoLogin, _ := GetEnv("AUTO_LOGIN"); autoLogin != "" {
|
|
||||||
se.Router.BindFunc(func(e *core.RequestEvent) error {
|
|
||||||
return authorizeRequestWithEmail(e, autoLogin)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
// authenticate with trusted header
|
// authenticate with trusted header
|
||||||
if trustedHeader, _ := GetEnv("TRUSTED_AUTH_HEADER"); trustedHeader != "" {
|
if trustedHeader, _ := GetEnv("TRUSTED_AUTH_HEADER"); trustedHeader != "" {
|
||||||
se.Router.BindFunc(func(e *core.RequestEvent) error {
|
se.Router.BindFunc(func(e *core.RequestEvent) error {
|
||||||
return authorizeRequestWithEmail(e, e.Request.Header.Get(trustedHeader))
|
if e.Auth != nil {
|
||||||
|
return e.Next()
|
||||||
|
}
|
||||||
|
trustedEmail := e.Request.Header.Get(trustedHeader)
|
||||||
|
if trustedEmail == "" {
|
||||||
|
return e.Next()
|
||||||
|
}
|
||||||
|
isAuthRefresh := e.Request.URL.Path == "/api/collections/users/auth-refresh" && e.Request.Method == http.MethodPost
|
||||||
|
if !isAuthRefresh {
|
||||||
|
authRecord, err := e.App.FindAuthRecordByEmail("users", trustedEmail)
|
||||||
|
if err == nil {
|
||||||
|
e.Auth = authRecord
|
||||||
|
}
|
||||||
|
return e.Next()
|
||||||
|
}
|
||||||
|
// if auth refresh endpoint, find user record directly and generate token
|
||||||
|
user, err := e.App.FindFirstRecordByData("users", "email", trustedEmail)
|
||||||
|
if err != nil {
|
||||||
|
return e.Next()
|
||||||
|
}
|
||||||
|
e.Auth = user
|
||||||
|
// need to set the authorization header for the client sdk to pick up the token
|
||||||
|
if token, err := user.NewAuthToken(); err == nil {
|
||||||
|
e.Request.Header.Set("Authorization", token)
|
||||||
|
}
|
||||||
|
return e.Next()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -712,60 +712,6 @@ func TestCreateUserEndpointAvailability(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAutoLoginMiddleware(t *testing.T) {
|
|
||||||
var hubs []*beszelTests.TestHub
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
defer os.Unsetenv("AUTO_LOGIN")
|
|
||||||
for _, hub := range hubs {
|
|
||||||
hub.Cleanup()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
os.Setenv("AUTO_LOGIN", "user@test.com")
|
|
||||||
|
|
||||||
testAppFactory := func(t testing.TB) *pbTests.TestApp {
|
|
||||||
hub, _ := beszelTests.NewTestHub(t.TempDir())
|
|
||||||
hubs = append(hubs, hub)
|
|
||||||
hub.StartHub()
|
|
||||||
return hub.TestApp
|
|
||||||
}
|
|
||||||
|
|
||||||
scenarios := []beszelTests.ApiScenario{
|
|
||||||
{
|
|
||||||
Name: "GET /getkey - without auto login should fail",
|
|
||||||
Method: http.MethodGet,
|
|
||||||
URL: "/api/beszel/getkey",
|
|
||||||
ExpectedStatus: 401,
|
|
||||||
ExpectedContent: []string{"requires valid"},
|
|
||||||
TestAppFactory: testAppFactory,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "GET /getkey - with auto login should fail if no matching user",
|
|
||||||
Method: http.MethodGet,
|
|
||||||
URL: "/api/beszel/getkey",
|
|
||||||
ExpectedStatus: 401,
|
|
||||||
ExpectedContent: []string{"requires valid"},
|
|
||||||
TestAppFactory: testAppFactory,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "GET /getkey - with auto login should succeed",
|
|
||||||
Method: http.MethodGet,
|
|
||||||
URL: "/api/beszel/getkey",
|
|
||||||
ExpectedStatus: 200,
|
|
||||||
ExpectedContent: []string{"\"key\":", "\"v\":"},
|
|
||||||
TestAppFactory: testAppFactory,
|
|
||||||
BeforeTestFunc: func(t testing.TB, app *pbTests.TestApp, e *core.ServeEvent) {
|
|
||||||
beszelTests.CreateUser(app, "user@test.com", "password123")
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, scenario := range scenarios {
|
|
||||||
scenario.Test(t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTrustedHeaderMiddleware(t *testing.T) {
|
func TestTrustedHeaderMiddleware(t *testing.T) {
|
||||||
var hubs []*beszelTests.TestHub
|
var hubs []*beszelTests.TestHub
|
||||||
|
|
||||||
|
|||||||
@@ -225,19 +225,6 @@ func (rm *RecordManager) AverageSystemStats(db dbx.Builder, records RecordIds) *
|
|||||||
sum.MaxBandwidth[0] = max(sum.MaxBandwidth[0], stats.MaxBandwidth[0], stats.Bandwidth[0])
|
sum.MaxBandwidth[0] = max(sum.MaxBandwidth[0], stats.MaxBandwidth[0], stats.Bandwidth[0])
|
||||||
sum.MaxBandwidth[1] = max(sum.MaxBandwidth[1], stats.MaxBandwidth[1], stats.Bandwidth[1])
|
sum.MaxBandwidth[1] = max(sum.MaxBandwidth[1], stats.MaxBandwidth[1], stats.Bandwidth[1])
|
||||||
|
|
||||||
// Accumulate network interfaces
|
|
||||||
if sum.NetworkInterfaces == nil {
|
|
||||||
sum.NetworkInterfaces = make(map[string][4]uint64, len(stats.NetworkInterfaces))
|
|
||||||
}
|
|
||||||
for key, value := range stats.NetworkInterfaces {
|
|
||||||
sum.NetworkInterfaces[key] = [4]uint64{
|
|
||||||
sum.NetworkInterfaces[key][0] + value[0],
|
|
||||||
sum.NetworkInterfaces[key][1] + value[1],
|
|
||||||
max(sum.NetworkInterfaces[key][2], value[2]),
|
|
||||||
max(sum.NetworkInterfaces[key][3], value[3]),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Accumulate temperatures
|
// Accumulate temperatures
|
||||||
if stats.Temperatures != nil {
|
if stats.Temperatures != nil {
|
||||||
if sum.Temperatures == nil {
|
if sum.Temperatures == nil {
|
||||||
@@ -284,16 +271,6 @@ func (rm *RecordManager) AverageSystemStats(db dbx.Builder, records RecordIds) *
|
|||||||
gpu.Usage += value.Usage
|
gpu.Usage += value.Usage
|
||||||
gpu.Power += value.Power
|
gpu.Power += value.Power
|
||||||
gpu.Count += value.Count
|
gpu.Count += value.Count
|
||||||
|
|
||||||
if value.Engines != nil {
|
|
||||||
if gpu.Engines == nil {
|
|
||||||
gpu.Engines = make(map[string]float64, len(value.Engines))
|
|
||||||
}
|
|
||||||
for engineKey, engineValue := range value.Engines {
|
|
||||||
gpu.Engines[engineKey] += engineValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sum.GPUData[id] = gpu
|
sum.GPUData[id] = gpu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -322,19 +299,6 @@ func (rm *RecordManager) AverageSystemStats(db dbx.Builder, records RecordIds) *
|
|||||||
sum.Bandwidth[0] = sum.Bandwidth[0] / uint64(count)
|
sum.Bandwidth[0] = sum.Bandwidth[0] / uint64(count)
|
||||||
sum.Bandwidth[1] = sum.Bandwidth[1] / uint64(count)
|
sum.Bandwidth[1] = sum.Bandwidth[1] / uint64(count)
|
||||||
sum.Battery[0] = uint8(batterySum / int(count))
|
sum.Battery[0] = uint8(batterySum / int(count))
|
||||||
|
|
||||||
// Average network interfaces
|
|
||||||
if sum.NetworkInterfaces != nil {
|
|
||||||
for key := range sum.NetworkInterfaces {
|
|
||||||
sum.NetworkInterfaces[key] = [4]uint64{
|
|
||||||
sum.NetworkInterfaces[key][0] / uint64(count),
|
|
||||||
sum.NetworkInterfaces[key][1] / uint64(count),
|
|
||||||
sum.NetworkInterfaces[key][2],
|
|
||||||
sum.NetworkInterfaces[key][3],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Average temperatures
|
// Average temperatures
|
||||||
if sum.Temperatures != nil && tempCount > 0 {
|
if sum.Temperatures != nil && tempCount > 0 {
|
||||||
for key := range sum.Temperatures {
|
for key := range sum.Temperatures {
|
||||||
@@ -363,13 +327,6 @@ func (rm *RecordManager) AverageSystemStats(db dbx.Builder, records RecordIds) *
|
|||||||
gpu.Usage = twoDecimals(gpu.Usage / count)
|
gpu.Usage = twoDecimals(gpu.Usage / count)
|
||||||
gpu.Power = twoDecimals(gpu.Power / count)
|
gpu.Power = twoDecimals(gpu.Power / count)
|
||||||
gpu.Count = twoDecimals(gpu.Count / count)
|
gpu.Count = twoDecimals(gpu.Count / count)
|
||||||
|
|
||||||
if gpu.Engines != nil {
|
|
||||||
for engineKey := range gpu.Engines {
|
|
||||||
gpu.Engines[engineKey] = twoDecimals(gpu.Engines[engineKey] / count)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sum.GPUData[id] = gpu
|
sum.GPUData[id] = gpu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -175,7 +175,7 @@ func TestDeleteOldSystemStats(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Run deletion
|
// Run deletion
|
||||||
err = records.DeleteOldSystemStats(hub)
|
err = records.TestDeleteOldSystemStats(hub)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Verify results
|
// Verify results
|
||||||
@@ -268,7 +268,7 @@ func TestDeleteOldAlertsHistory(t *testing.T) {
|
|||||||
assert.Equal(t, int64(tc.alertCount), countBefore, "Initial count should match")
|
assert.Equal(t, int64(tc.alertCount), countBefore, "Initial count should match")
|
||||||
|
|
||||||
// Run deletion
|
// Run deletion
|
||||||
err = records.DeleteOldAlertsHistory(hub, tc.countToKeep, tc.countBeforeDeletion)
|
err = records.TestDeleteOldAlertsHistory(hub, tc.countToKeep, tc.countBeforeDeletion)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Count after deletion
|
// Count after deletion
|
||||||
@@ -332,7 +332,7 @@ func TestDeleteOldAlertsHistoryEdgeCases(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Should not error and should not delete anything
|
// Should not error and should not delete anything
|
||||||
err = records.DeleteOldAlertsHistory(hub, 10, 20)
|
err = records.TestDeleteOldAlertsHistory(hub, 10, 20)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
count, err := hub.CountRecords("alerts_history")
|
count, err := hub.CountRecords("alerts_history")
|
||||||
@@ -346,7 +346,7 @@ func TestDeleteOldAlertsHistoryEdgeCases(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Should not error with empty table
|
// Should not error with empty table
|
||||||
err = records.DeleteOldAlertsHistory(hub, 10, 20)
|
err = records.TestDeleteOldAlertsHistory(hub, 10, 20)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -376,7 +376,7 @@ func TestTwoDecimals(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
result := records.TwoDecimals(tc.input)
|
result := records.TestTwoDecimals(tc.input)
|
||||||
assert.InDelta(t, tc.expected, result, 0.02, "twoDecimals(%f) should equal %f", tc.input, tc.expected)
|
assert.InDelta(t, tc.expected, result, 0.02, "twoDecimals(%f) should equal %f", tc.input, tc.expected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,17 +7,17 @@ import (
|
|||||||
"github.com/pocketbase/pocketbase/core"
|
"github.com/pocketbase/pocketbase/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DeleteOldSystemStats exposes deleteOldSystemStats for testing
|
// TestDeleteOldSystemStats exposes deleteOldSystemStats for testing
|
||||||
func DeleteOldSystemStats(app core.App) error {
|
func TestDeleteOldSystemStats(app core.App) error {
|
||||||
return deleteOldSystemStats(app)
|
return deleteOldSystemStats(app)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteOldAlertsHistory exposes deleteOldAlertsHistory for testing
|
// TestDeleteOldAlertsHistory exposes deleteOldAlertsHistory for testing
|
||||||
func DeleteOldAlertsHistory(app core.App, countToKeep, countBeforeDeletion int) error {
|
func TestDeleteOldAlertsHistory(app core.App, countToKeep, countBeforeDeletion int) error {
|
||||||
return deleteOldAlertsHistory(app, countToKeep, countBeforeDeletion)
|
return deleteOldAlertsHistory(app, countToKeep, countBeforeDeletion)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TwoDecimals exposes twoDecimals for testing
|
// TestTwoDecimals exposes twoDecimals for testing
|
||||||
func TwoDecimals(value float64) float64 {
|
func TestTwoDecimals(value float64) float64 {
|
||||||
return twoDecimals(value)
|
return twoDecimals(value)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,10 +17,7 @@
|
|||||||
"linter": {
|
"linter": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"rules": {
|
"rules": {
|
||||||
"recommended": true,
|
"recommended": true
|
||||||
"correctness": {
|
|
||||||
"useUniqueElementIds": "off"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"javascript": {
|
"javascript": {
|
||||||
@@ -38,4 +35,4 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
186
internal/site/package-lock.json
generated
186
internal/site/package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "beszel",
|
"name": "beszel",
|
||||||
"version": "0.12.9",
|
"version": "0.12.7",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "beszel",
|
"name": "beszel",
|
||||||
"version": "0.12.9",
|
"version": "0.12.7",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@henrygd/queue": "^1.0.7",
|
"@henrygd/queue": "^1.0.7",
|
||||||
"@henrygd/semaphore": "^0.0.2",
|
"@henrygd/semaphore": "^0.0.2",
|
||||||
@@ -46,7 +46,6 @@
|
|||||||
"valibot": "^0.42.1"
|
"valibot": "^0.42.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "2.2.3",
|
|
||||||
"@lingui/cli": "^5.4.1",
|
"@lingui/cli": "^5.4.1",
|
||||||
"@lingui/swc-plugin": "^5.6.1",
|
"@lingui/swc-plugin": "^5.6.1",
|
||||||
"@lingui/vite-plugin": "^5.4.1",
|
"@lingui/vite-plugin": "^5.4.1",
|
||||||
@@ -331,169 +330,6 @@
|
|||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@biomejs/biome": {
|
|
||||||
"version": "2.2.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.2.3.tgz",
|
|
||||||
"integrity": "sha512-9w0uMTvPrIdvUrxazZ42Ib7t8Y2yoGLKLdNne93RLICmaHw7mcLv4PPb5LvZLJF3141gQHiCColOh/v6VWlWmg==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT OR Apache-2.0",
|
|
||||||
"bin": {
|
|
||||||
"biome": "bin/biome"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14.21.3"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "opencollective",
|
|
||||||
"url": "https://opencollective.com/biome"
|
|
||||||
},
|
|
||||||
"optionalDependencies": {
|
|
||||||
"@biomejs/cli-darwin-arm64": "2.2.3",
|
|
||||||
"@biomejs/cli-darwin-x64": "2.2.3",
|
|
||||||
"@biomejs/cli-linux-arm64": "2.2.3",
|
|
||||||
"@biomejs/cli-linux-arm64-musl": "2.2.3",
|
|
||||||
"@biomejs/cli-linux-x64": "2.2.3",
|
|
||||||
"@biomejs/cli-linux-x64-musl": "2.2.3",
|
|
||||||
"@biomejs/cli-win32-arm64": "2.2.3",
|
|
||||||
"@biomejs/cli-win32-x64": "2.2.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@biomejs/cli-darwin-arm64": {
|
|
||||||
"version": "2.2.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.2.3.tgz",
|
|
||||||
"integrity": "sha512-OrqQVBpadB5eqzinXN4+Q6honBz+tTlKVCsbEuEpljK8ASSItzIRZUA02mTikl3H/1nO2BMPFiJ0nkEZNy3B1w==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT OR Apache-2.0",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"darwin"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14.21.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@biomejs/cli-darwin-x64": {
|
|
||||||
"version": "2.2.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.2.3.tgz",
|
|
||||||
"integrity": "sha512-OCdBpb1TmyfsTgBAM1kPMXyYKTohQ48WpiN9tkt9xvU6gKVKHY4oVwteBebiOqyfyzCNaSiuKIPjmHjUZ2ZNMg==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT OR Apache-2.0",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"darwin"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14.21.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@biomejs/cli-linux-arm64": {
|
|
||||||
"version": "2.2.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.2.3.tgz",
|
|
||||||
"integrity": "sha512-g/Uta2DqYpECxG+vUmTAmUKlVhnGEcY7DXWgKP8ruLRa8Si1QHsWknPY3B/wCo0KgYiFIOAZ9hjsHfNb9L85+g==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT OR Apache-2.0",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14.21.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@biomejs/cli-linux-arm64-musl": {
|
|
||||||
"version": "2.2.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.2.3.tgz",
|
|
||||||
"integrity": "sha512-q3w9jJ6JFPZPeqyvwwPeaiS/6NEszZ+pXKF+IczNo8Xj6fsii45a4gEEicKyKIytalV+s829ACZujQlXAiVLBQ==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT OR Apache-2.0",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14.21.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@biomejs/cli-linux-x64": {
|
|
||||||
"version": "2.2.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.2.3.tgz",
|
|
||||||
"integrity": "sha512-LEtyYL1fJsvw35CxrbQ0gZoxOG3oZsAjzfRdvRBRHxOpQ91Q5doRVjvWW/wepgSdgk5hlaNzfeqpyGmfSD0Eyw==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT OR Apache-2.0",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14.21.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@biomejs/cli-linux-x64-musl": {
|
|
||||||
"version": "2.2.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.2.3.tgz",
|
|
||||||
"integrity": "sha512-y76Dn4vkP1sMRGPFlNc+OTETBhGPJ90jY3il6jAfur8XWrYBQV3swZ1Jo0R2g+JpOeeoA0cOwM7mJG6svDz79w==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT OR Apache-2.0",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14.21.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@biomejs/cli-win32-arm64": {
|
|
||||||
"version": "2.2.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.2.3.tgz",
|
|
||||||
"integrity": "sha512-Ms9zFYzjcJK7LV+AOMYnjN3pV3xL8Prxf9aWdDVL74onLn5kcvZ1ZMQswE5XHtnd/r/0bnUd928Rpbs14BzVmA==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT OR Apache-2.0",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"win32"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14.21.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@biomejs/cli-win32-x64": {
|
|
||||||
"version": "2.2.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.2.3.tgz",
|
|
||||||
"integrity": "sha512-gvCpewE7mBwBIpqk1YrUqNR4mCiyJm6UI3YWQQXkedSSEwzRdodRpaKhbdbHw1/hmTWOVXQ+Eih5Qctf4TCVOQ==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT OR Apache-2.0",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"win32"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14.21.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/aix-ppc64": {
|
"node_modules/@esbuild/aix-ppc64": {
|
||||||
"version": "0.25.6",
|
"version": "0.25.6",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.6.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.6.tgz",
|
||||||
@@ -5927,14 +5763,14 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/tinyglobby": {
|
"node_modules/tinyglobby": {
|
||||||
"version": "0.2.15",
|
"version": "0.2.14",
|
||||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
|
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz",
|
||||||
"integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
|
"integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fdir": "^6.5.0",
|
"fdir": "^6.4.4",
|
||||||
"picomatch": "^4.0.3"
|
"picomatch": "^4.0.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12.0.0"
|
"node": ">=12.0.0"
|
||||||
@@ -6121,9 +5957,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "7.1.5",
|
"version": "7.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-7.1.3.tgz",
|
||||||
"integrity": "sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ==",
|
"integrity": "sha512-OOUi5zjkDxYrKhTV3V7iKsoS37VUM7v40+HuwEmcrsf11Cdx9y3DIr2Px6liIcZFwt3XSRpQvFpL3WVy7ApkGw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -6132,7 +5968,7 @@
|
|||||||
"picomatch": "^4.0.3",
|
"picomatch": "^4.0.3",
|
||||||
"postcss": "^8.5.6",
|
"postcss": "^8.5.6",
|
||||||
"rollup": "^4.43.0",
|
"rollup": "^4.43.0",
|
||||||
"tinyglobby": "^0.2.15"
|
"tinyglobby": "^0.2.14"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"vite": "bin/vite.js"
|
"vite": "bin/vite.js"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "beszel",
|
"name": "beszel",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.12.9",
|
"version": "0.12.7",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite --host",
|
"dev": "vite --host",
|
||||||
@@ -76,4 +76,4 @@
|
|||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@esbuild/linux-arm64": "^0.21.5"
|
"@esbuild/linux-arm64": "^0.21.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,5 @@
|
|||||||
import { t } from "@lingui/core/macro"
|
|
||||||
import { Trans } from "@lingui/react/macro"
|
import { Trans } from "@lingui/react/macro"
|
||||||
import { useStore } from "@nanostores/react"
|
import { t } from "@lingui/core/macro"
|
||||||
import { getPagePath } from "@nanostores/router"
|
|
||||||
import { ChevronDownIcon, ExternalLinkIcon, PlusIcon } from "lucide-react"
|
|
||||||
import { memo, useEffect, useRef, useState } from "react"
|
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
@@ -14,30 +10,34 @@ import {
|
|||||||
DialogTitle,
|
DialogTitle,
|
||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
} from "@/components/ui/dialog"
|
} from "@/components/ui/dialog"
|
||||||
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
||||||
import { Input } from "@/components/ui/input"
|
import { Input } from "@/components/ui/input"
|
||||||
import { Label } from "@/components/ui/label"
|
import { Label } from "@/components/ui/label"
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
|
||||||
import { isReadOnlyUser, pb } from "@/lib/api"
|
|
||||||
import { SystemStatus } from "@/lib/enums"
|
|
||||||
import { $publicKey } from "@/lib/stores"
|
import { $publicKey } from "@/lib/stores"
|
||||||
import { cn, generateToken, tokenMap, useBrowserStorage } from "@/lib/utils"
|
import { cn, generateToken, tokenMap, useBrowserStorage } from "@/lib/utils"
|
||||||
import type { SystemRecord } from "@/types"
|
import { pb, isReadOnlyUser } from "@/lib/api"
|
||||||
|
import { useStore } from "@nanostores/react"
|
||||||
|
import { ChevronDownIcon, ExternalLinkIcon, PlusIcon } from "lucide-react"
|
||||||
|
import { memo, useEffect, useRef, useState } from "react"
|
||||||
|
import { $router, basePath, Link, navigate } from "./router"
|
||||||
|
import { SystemRecord } from "@/types"
|
||||||
|
import { SystemStatus } from "@/lib/enums"
|
||||||
|
import { AppleIcon, DockerIcon, TuxIcon, WindowsIcon } from "./ui/icons"
|
||||||
|
import { InputCopy } from "./ui/input-copy"
|
||||||
|
import { getPagePath } from "@nanostores/router"
|
||||||
import {
|
import {
|
||||||
copyDockerCompose,
|
copyDockerCompose,
|
||||||
copyDockerRun,
|
copyDockerRun,
|
||||||
copyLinuxCommand,
|
copyLinuxCommand,
|
||||||
copyWindowsCommand,
|
copyWindowsCommand,
|
||||||
type DropdownItem,
|
DropdownItem,
|
||||||
InstallDropdown,
|
InstallDropdown,
|
||||||
} from "./install-dropdowns"
|
} from "./install-dropdowns"
|
||||||
import { $router, basePath, Link, navigate } from "./router"
|
|
||||||
import { DropdownMenu, DropdownMenuTrigger } from "./ui/dropdown-menu"
|
import { DropdownMenu, DropdownMenuTrigger } from "./ui/dropdown-menu"
|
||||||
import { AppleIcon, DockerIcon, FreeBsdIcon, TuxIcon, WindowsIcon } from "./ui/icons"
|
|
||||||
import { InputCopy } from "./ui/input-copy"
|
|
||||||
|
|
||||||
export function AddSystemButton({ className }: { className?: string }) {
|
export function AddSystemButton({ className }: { className?: string }) {
|
||||||
const [open, setOpen] = useState(false)
|
const [open, setOpen] = useState(false)
|
||||||
const opened = useRef(false)
|
let opened = useRef(false)
|
||||||
if (open) {
|
if (open) {
|
||||||
opened.current = true
|
opened.current = true
|
||||||
}
|
}
|
||||||
@@ -253,12 +253,6 @@ export const SystemDialog = ({ setOpen, system }: { setOpen: (open: boolean) =>
|
|||||||
copyWindowsCommand(isUnixSocket ? hostValue : port.current?.value, publicKey, token),
|
copyWindowsCommand(isUnixSocket ? hostValue : port.current?.value, publicKey, token),
|
||||||
icons: [WindowsIcon],
|
icons: [WindowsIcon],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
text: t({ message: "FreeBSD command", context: "Button to copy install command" }),
|
|
||||||
onClick: async () =>
|
|
||||||
copyLinuxCommand(isUnixSocket ? hostValue : port.current?.value, publicKey, token),
|
|
||||||
icons: [FreeBsdIcon],
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
text: t`Manual setup instructions`,
|
text: t`Manual setup instructions`,
|
||||||
url: "https://beszel.dev/guide/agent-installation#binary",
|
url: "https://beszel.dev/guide/agent-installation#binary",
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { t } from "@lingui/core/macro"
|
import { ColumnDef } from "@tanstack/react-table"
|
||||||
import { Trans } from "@lingui/react/macro"
|
import { AlertsHistoryRecord } from "@/types"
|
||||||
import type { ColumnDef } from "@tanstack/react-table"
|
|
||||||
import { Badge } from "@/components/ui/badge"
|
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
|
import { Badge } from "@/components/ui/badge"
|
||||||
|
import { formatShortDate, toFixedFloat, formatDuration, cn } from "@/lib/utils"
|
||||||
import { alertInfo } from "@/lib/alerts"
|
import { alertInfo } from "@/lib/alerts"
|
||||||
import { cn, formatDuration, formatShortDate, toFixedFloat } from "@/lib/utils"
|
import { Trans } from "@lingui/react/macro"
|
||||||
import type { AlertsHistoryRecord } from "@/types"
|
import { t } from "@lingui/core/macro"
|
||||||
|
|
||||||
export const alertsHistoryColumns: ColumnDef<AlertsHistoryRecord>[] = [
|
export const alertsHistoryColumns: ColumnDef<AlertsHistoryRecord>[] = [
|
||||||
{
|
{
|
||||||
@@ -38,7 +38,7 @@ export const alertsHistoryColumns: ColumnDef<AlertsHistoryRecord>[] = [
|
|||||||
</Button>
|
</Button>
|
||||||
),
|
),
|
||||||
cell: ({ getValue, row }) => {
|
cell: ({ getValue, row }) => {
|
||||||
const name = getValue() as string
|
let name = getValue() as string
|
||||||
const info = alertInfo[row.original.name]
|
const info = alertInfo[row.original.name]
|
||||||
const Icon = info?.icon
|
const Icon = info?.icon
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { t } from "@lingui/core/macro"
|
import { t } from "@lingui/core/macro"
|
||||||
import { useStore } from "@nanostores/react"
|
|
||||||
import { BellIcon } from "lucide-react"
|
|
||||||
import { memo, useMemo, useState } from "react"
|
import { memo, useMemo, useState } from "react"
|
||||||
import { Button } from "@/components/ui/button"
|
import { useStore } from "@nanostores/react"
|
||||||
import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet"
|
|
||||||
import { $alerts } from "@/lib/stores"
|
import { $alerts } from "@/lib/stores"
|
||||||
|
import { BellIcon } from "lucide-react"
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
import type { SystemRecord } from "@/types"
|
import { Button } from "@/components/ui/button"
|
||||||
|
import { SystemRecord } from "@/types"
|
||||||
import { AlertDialogContent } from "./alerts-sheet"
|
import { AlertDialogContent } from "./alerts-sheet"
|
||||||
|
import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet"
|
||||||
|
|
||||||
export default memo(function AlertsButton({ system }: { system: SystemRecord }) {
|
export default memo(function AlertsButton({ system }: { system: SystemRecord }) {
|
||||||
const [opened, setOpened] = useState(false)
|
const [opened, setOpened] = useState(false)
|
||||||
|
|||||||
@@ -1,20 +1,21 @@
|
|||||||
import { t } from "@lingui/core/macro"
|
import { t } from "@lingui/core/macro"
|
||||||
import { Plural, Trans } from "@lingui/react/macro"
|
import { Trans, Plural } from "@lingui/react/macro"
|
||||||
import { useStore } from "@nanostores/react"
|
|
||||||
import { getPagePath } from "@nanostores/router"
|
|
||||||
import { GlobeIcon, ServerIcon } from "lucide-react"
|
|
||||||
import { lazy, memo, Suspense, useMemo, useState } from "react"
|
|
||||||
import { $router, Link } from "@/components/router"
|
|
||||||
import { Checkbox } from "@/components/ui/checkbox"
|
|
||||||
import { DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog"
|
|
||||||
import { Switch } from "@/components/ui/switch"
|
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
|
||||||
import { toast } from "@/components/ui/use-toast"
|
|
||||||
import { alertInfo } from "@/lib/alerts"
|
|
||||||
import { pb } from "@/lib/api"
|
|
||||||
import { $alerts, $systems } from "@/lib/stores"
|
import { $alerts, $systems } from "@/lib/stores"
|
||||||
import { cn, debounce } from "@/lib/utils"
|
import { cn, debounce } from "@/lib/utils"
|
||||||
import type { AlertInfo, AlertRecord, SystemRecord } from "@/types"
|
import { alertInfo } from "@/lib/alerts"
|
||||||
|
import { Switch } from "@/components/ui/switch"
|
||||||
|
import { AlertInfo, AlertRecord, SystemRecord } from "@/types"
|
||||||
|
import { lazy, memo, Suspense, useMemo, useState } from "react"
|
||||||
|
import { toast } from "@/components/ui/use-toast"
|
||||||
|
import { useStore } from "@nanostores/react"
|
||||||
|
import { getPagePath } from "@nanostores/router"
|
||||||
|
import { Checkbox } from "@/components/ui/checkbox"
|
||||||
|
import { DialogTitle, DialogDescription } from "@/components/ui/dialog"
|
||||||
|
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs"
|
||||||
|
import { ServerIcon, GlobeIcon } from "lucide-react"
|
||||||
|
import { $router, Link } from "@/components/router"
|
||||||
|
import { DialogHeader } from "@/components/ui/dialog"
|
||||||
|
import { pb } from "@/lib/api"
|
||||||
|
|
||||||
const Slider = lazy(() => import("@/components/ui/slider"))
|
const Slider = lazy(() => import("@/components/ui/slider"))
|
||||||
|
|
||||||
@@ -171,7 +172,7 @@ export function AlertContent({
|
|||||||
|
|
||||||
const [checked, setChecked] = useState(global ? false : !!alert)
|
const [checked, setChecked] = useState(global ? false : !!alert)
|
||||||
const [min, setMin] = useState(alert?.min || 10)
|
const [min, setMin] = useState(alert?.min || 10)
|
||||||
const [value, setValue] = useState(alert?.value || (singleDescription ? 0 : (alertData.start ?? 80)))
|
const [value, setValue] = useState(alert?.value || (singleDescription ? 0 : alertData.start ?? 80))
|
||||||
|
|
||||||
const Icon = alertData.icon
|
const Icon = alertData.icon
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,9 @@
|
|||||||
import { useMemo } from "react"
|
|
||||||
import { Area, AreaChart, CartesianGrid, YAxis } from "recharts"
|
import { Area, AreaChart, CartesianGrid, YAxis } from "recharts"
|
||||||
import {
|
import { ChartContainer, ChartTooltip, ChartTooltipContent, xAxis } from "@/components/ui/chart"
|
||||||
ChartContainer,
|
import { cn, formatShortDate, chartMargin } from "@/lib/utils"
|
||||||
ChartLegend,
|
|
||||||
ChartLegendContent,
|
|
||||||
ChartTooltip,
|
|
||||||
ChartTooltipContent,
|
|
||||||
xAxis,
|
|
||||||
} from "@/components/ui/chart"
|
|
||||||
import { chartMargin, cn, formatShortDate } from "@/lib/utils"
|
|
||||||
import type { ChartData, SystemStatsRecord } from "@/types"
|
|
||||||
import { useYAxisWidth } from "./hooks"
|
import { useYAxisWidth } from "./hooks"
|
||||||
|
import { ChartData, SystemStatsRecord } from "@/types"
|
||||||
|
import { useMemo } from "react"
|
||||||
|
|
||||||
export type DataPoint = {
|
export type DataPoint = {
|
||||||
label: string
|
label: string
|
||||||
@@ -27,8 +20,6 @@ export default function AreaChartDefault({
|
|||||||
contentFormatter,
|
contentFormatter,
|
||||||
dataPoints,
|
dataPoints,
|
||||||
domain,
|
domain,
|
||||||
legend,
|
|
||||||
itemSorter,
|
|
||||||
}: // logRender = false,
|
}: // logRender = false,
|
||||||
{
|
{
|
||||||
chartData: ChartData
|
chartData: ChartData
|
||||||
@@ -38,13 +29,10 @@ export default function AreaChartDefault({
|
|||||||
contentFormatter: ({ value, payload }: { value: number; payload: SystemStatsRecord }) => string
|
contentFormatter: ({ value, payload }: { value: number; payload: SystemStatsRecord }) => string
|
||||||
dataPoints?: DataPoint[]
|
dataPoints?: DataPoint[]
|
||||||
domain?: [number, number]
|
domain?: [number, number]
|
||||||
legend?: boolean
|
|
||||||
itemSorter?: (a: any, b: any) => number
|
|
||||||
// logRender?: boolean
|
// logRender?: boolean
|
||||||
}) {
|
}) {
|
||||||
const { yAxisWidth, updateYAxisWidth } = useYAxisWidth()
|
const { yAxisWidth, updateYAxisWidth } = useYAxisWidth()
|
||||||
|
|
||||||
// biome-ignore lint/correctness/useExhaustiveDependencies: ignore
|
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
if (chartData.systemStats.length === 0) {
|
if (chartData.systemStats.length === 0) {
|
||||||
return null
|
return null
|
||||||
@@ -75,8 +63,6 @@ export default function AreaChartDefault({
|
|||||||
<ChartTooltip
|
<ChartTooltip
|
||||||
animationEasing="ease-out"
|
animationEasing="ease-out"
|
||||||
animationDuration={150}
|
animationDuration={150}
|
||||||
// @ts-expect-error
|
|
||||||
itemSorter={itemSorter}
|
|
||||||
content={
|
content={
|
||||||
<ChartTooltipContent
|
<ChartTooltipContent
|
||||||
labelFormatter={(_, data) => formatShortDate(data[0].payload.created)}
|
labelFormatter={(_, data) => formatShortDate(data[0].payload.created)}
|
||||||
@@ -84,14 +70,11 @@ export default function AreaChartDefault({
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
{dataPoints?.map((dataPoint) => {
|
{dataPoints?.map((dataPoint, i) => {
|
||||||
let { color } = dataPoint
|
const color = `var(--chart-${dataPoint.color})`
|
||||||
if (typeof color === "number") {
|
|
||||||
color = `var(--chart-${color})`
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
<Area
|
<Area
|
||||||
key={dataPoint.label}
|
key={i}
|
||||||
dataKey={dataPoint.dataKey}
|
dataKey={dataPoint.dataKey}
|
||||||
name={dataPoint.label}
|
name={dataPoint.label}
|
||||||
type="monotoneX"
|
type="monotoneX"
|
||||||
@@ -102,7 +85,7 @@ export default function AreaChartDefault({
|
|||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
{legend && <ChartLegend content={<ChartLegendContent />} />}
|
{/* <ChartLegend content={<ChartLegendContent />} /> */}
|
||||||
</AreaChart>
|
</AreaChart>
|
||||||
</ChartContainer>
|
</ChartContainer>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { useStore } from "@nanostores/react"
|
|
||||||
import { HistoryIcon } from "lucide-react"
|
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
|
||||||
import { $chartTime } from "@/lib/stores"
|
import { $chartTime } from "@/lib/stores"
|
||||||
import { chartTimeData, cn } from "@/lib/utils"
|
import { chartTimeData, cn } from "@/lib/utils"
|
||||||
import type { ChartTimes } from "@/types"
|
import { ChartTimes } from "@/types"
|
||||||
|
import { useStore } from "@nanostores/react"
|
||||||
|
import { HistoryIcon } from "lucide-react"
|
||||||
|
|
||||||
export default function ChartTimeSelect({ className }: { className?: string }) {
|
export default function ChartTimeSelect({ className }: { className?: string }) {
|
||||||
const chartTime = useStore($chartTime)
|
const chartTime = useStore($chartTime)
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
// import Spinner from '../spinner'
|
|
||||||
import { useStore } from "@nanostores/react"
|
|
||||||
import { memo, useMemo } from "react"
|
|
||||||
import { Area, AreaChart, CartesianGrid, YAxis } from "recharts"
|
import { Area, AreaChart, CartesianGrid, YAxis } from "recharts"
|
||||||
import { type ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent, xAxis } from "@/components/ui/chart"
|
import { type ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent, xAxis } from "@/components/ui/chart"
|
||||||
import { ChartType, Unit } from "@/lib/enums"
|
import { memo, useMemo } from "react"
|
||||||
|
import { cn, formatShortDate, chartMargin, toFixedFloat, formatBytes, decimalString } from "@/lib/utils"
|
||||||
|
// import Spinner from '../spinner'
|
||||||
|
import { useStore } from "@nanostores/react"
|
||||||
import { $containerFilter, $userSettings } from "@/lib/stores"
|
import { $containerFilter, $userSettings } from "@/lib/stores"
|
||||||
import { chartMargin, cn, decimalString, formatBytes, formatShortDate, toFixedFloat } from "@/lib/utils"
|
|
||||||
import type { ChartData } from "@/types"
|
import type { ChartData } from "@/types"
|
||||||
import { Separator } from "../ui/separator"
|
import { Separator } from "../ui/separator"
|
||||||
|
import { ChartType, Unit } from "@/lib/enums"
|
||||||
import { useYAxisWidth } from "./hooks"
|
import { useYAxisWidth } from "./hooks"
|
||||||
|
|
||||||
export default memo(function ContainerChart({
|
export default memo(function ContainerChart({
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { useLingui } from "@lingui/react/macro"
|
|
||||||
import { memo } from "react"
|
|
||||||
import { Area, AreaChart, CartesianGrid, YAxis } from "recharts"
|
import { Area, AreaChart, CartesianGrid, YAxis } from "recharts"
|
||||||
import { ChartContainer, ChartTooltip, ChartTooltipContent, xAxis } from "@/components/ui/chart"
|
import { ChartContainer, ChartTooltip, ChartTooltipContent, xAxis } from "@/components/ui/chart"
|
||||||
|
import { cn, formatShortDate, decimalString, chartMargin, formatBytes, toFixedFloat } from "@/lib/utils"
|
||||||
|
import { ChartData } from "@/types"
|
||||||
|
import { memo } from "react"
|
||||||
|
import { useLingui } from "@lingui/react/macro"
|
||||||
import { Unit } from "@/lib/enums"
|
import { Unit } from "@/lib/enums"
|
||||||
import { chartMargin, cn, decimalString, formatBytes, formatShortDate, toFixedFloat } from "@/lib/utils"
|
|
||||||
import type { ChartData } from "@/types"
|
|
||||||
import { useYAxisWidth } from "./hooks"
|
import { useYAxisWidth } from "./hooks"
|
||||||
|
|
||||||
export default memo(function DiskChart({
|
export default memo(function DiskChart({
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { memo, useMemo } from "react"
|
|
||||||
import { CartesianGrid, Line, LineChart, YAxis } from "recharts"
|
import { CartesianGrid, Line, LineChart, YAxis } from "recharts"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ChartContainer,
|
ChartContainer,
|
||||||
ChartLegend,
|
ChartLegend,
|
||||||
@@ -8,8 +8,9 @@ import {
|
|||||||
ChartTooltipContent,
|
ChartTooltipContent,
|
||||||
xAxis,
|
xAxis,
|
||||||
} from "@/components/ui/chart"
|
} from "@/components/ui/chart"
|
||||||
import { chartMargin, cn, decimalString, formatShortDate, toFixedFloat } from "@/lib/utils"
|
import { cn, formatShortDate, toFixedFloat, decimalString, chartMargin } from "@/lib/utils"
|
||||||
import type { ChartData } from "@/types"
|
import { ChartData } from "@/types"
|
||||||
|
import { memo, useMemo } from "react"
|
||||||
import { useYAxisWidth } from "./hooks"
|
import { useYAxisWidth } from "./hooks"
|
||||||
|
|
||||||
export default memo(function GpuPowerChart({ chartData }: { chartData: ChartData }) {
|
export default memo(function GpuPowerChart({ chartData }: { chartData: ChartData }) {
|
||||||
@@ -26,10 +27,10 @@ export default memo(function GpuPowerChart({ chartData }: { chartData: ChartData
|
|||||||
colors: Record<string, string>
|
colors: Record<string, string>
|
||||||
}
|
}
|
||||||
const powerSums = {} as Record<string, number>
|
const powerSums = {} as Record<string, number>
|
||||||
for (const data of chartData.systemStats) {
|
for (let data of chartData.systemStats) {
|
||||||
const newData = { created: data.created } as Record<string, number | string>
|
let newData = { created: data.created } as Record<string, number | string>
|
||||||
|
|
||||||
for (const gpu of Object.values(data.stats?.g ?? {})) {
|
for (let gpu of Object.values(data.stats?.g ?? {})) {
|
||||||
if (gpu.p) {
|
if (gpu.p) {
|
||||||
const name = gpu.n
|
const name = gpu.n
|
||||||
newData[name] = gpu.p
|
newData[name] = gpu.p
|
||||||
@@ -39,7 +40,7 @@ export default memo(function GpuPowerChart({ chartData }: { chartData: ChartData
|
|||||||
newChartData.data.push(newData)
|
newChartData.data.push(newData)
|
||||||
}
|
}
|
||||||
const keys = Object.keys(powerSums).sort((a, b) => powerSums[b] - powerSums[a])
|
const keys = Object.keys(powerSums).sort((a, b) => powerSums[b] - powerSums[a])
|
||||||
for (const key of keys) {
|
for (let key of keys) {
|
||||||
newChartData.colors[key] = `hsl(${((keys.indexOf(key) * 360) / keys.length) % 360}, 60%, 55%)`
|
newChartData.colors[key] = `hsl(${((keys.indexOf(key) * 360) / keys.length) % 360}, 60%, 55%)`
|
||||||
}
|
}
|
||||||
return newChartData
|
return newChartData
|
||||||
@@ -66,7 +67,7 @@ export default memo(function GpuPowerChart({ chartData }: { chartData: ChartData
|
|||||||
width={yAxisWidth}
|
width={yAxisWidth}
|
||||||
tickFormatter={(value) => {
|
tickFormatter={(value) => {
|
||||||
const val = toFixedFloat(value, 2)
|
const val = toFixedFloat(value, 2)
|
||||||
return updateYAxisWidth(`${val}W`)
|
return updateYAxisWidth(val + "W")
|
||||||
}}
|
}}
|
||||||
tickLine={false}
|
tickLine={false}
|
||||||
axisLine={false}
|
axisLine={false}
|
||||||
@@ -75,12 +76,12 @@ export default memo(function GpuPowerChart({ chartData }: { chartData: ChartData
|
|||||||
<ChartTooltip
|
<ChartTooltip
|
||||||
animationEasing="ease-out"
|
animationEasing="ease-out"
|
||||||
animationDuration={150}
|
animationDuration={150}
|
||||||
// @ts-expect-error
|
// @ts-ignore
|
||||||
itemSorter={(a, b) => b.value - a.value}
|
itemSorter={(a, b) => b.value - a.value}
|
||||||
content={
|
content={
|
||||||
<ChartTooltipContent
|
<ChartTooltipContent
|
||||||
labelFormatter={(_, data) => formatShortDate(data[0].payload.created)}
|
labelFormatter={(_, data) => formatShortDate(data[0].payload.created)}
|
||||||
contentFormatter={(item) => `${decimalString(item.value)}W`}
|
contentFormatter={(item) => decimalString(item.value) + "W"}
|
||||||
// indicator="line"
|
// indicator="line"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useMemo, useState } from "react"
|
import { useMemo, useState } from "react"
|
||||||
import type { ChartConfig } from "@/components/ui/chart"
|
import { ChartConfig } from "@/components/ui/chart"
|
||||||
import type { ChartData, SystemStats, SystemStatsRecord } from "@/types"
|
import { ChartData } from "@/types"
|
||||||
|
|
||||||
/** Chart configurations for CPU, memory, and network usage charts */
|
/** Chart configurations for CPU, memory, and network usage charts */
|
||||||
export interface ContainerChartConfigs {
|
export interface ContainerChartConfigs {
|
||||||
@@ -105,21 +105,3 @@ export function useYAxisWidth() {
|
|||||||
}
|
}
|
||||||
return { yAxisWidth, updateYAxisWidth }
|
return { yAxisWidth, updateYAxisWidth }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assures consistent colors for network interfaces
|
|
||||||
export function useNetworkInterfaces(interfaces: SystemStats["ni"]) {
|
|
||||||
const keys = Object.keys(interfaces ?? {})
|
|
||||||
const sortedKeys = keys.sort((a, b) => (interfaces?.[b]?.[3] ?? 0) - (interfaces?.[a]?.[3] ?? 0))
|
|
||||||
return {
|
|
||||||
length: sortedKeys.length,
|
|
||||||
data: (index = 3) => {
|
|
||||||
return sortedKeys.map((key) => ({
|
|
||||||
label: key,
|
|
||||||
dataKey: ({ stats }: SystemStatsRecord) => stats?.ni?.[key]?.[index],
|
|
||||||
color: `hsl(${220 + (((sortedKeys.indexOf(key) * 360) / sortedKeys.length) % 360)}, 70%, 50%)`,
|
|
||||||
|
|
||||||
opacity: 0.3,
|
|
||||||
}))
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
import { useMemo } from "react"
|
|
||||||
import { CartesianGrid, Line, LineChart, YAxis } from "recharts"
|
|
||||||
import {
|
|
||||||
ChartContainer,
|
|
||||||
ChartLegend,
|
|
||||||
ChartLegendContent,
|
|
||||||
ChartTooltip,
|
|
||||||
ChartTooltipContent,
|
|
||||||
xAxis,
|
|
||||||
} from "@/components/ui/chart"
|
|
||||||
import { chartMargin, cn, formatShortDate } from "@/lib/utils"
|
|
||||||
import type { ChartData, SystemStatsRecord } from "@/types"
|
|
||||||
import { useYAxisWidth } from "./hooks"
|
|
||||||
|
|
||||||
export type DataPoint = {
|
|
||||||
label: string
|
|
||||||
dataKey: (data: SystemStatsRecord) => number | undefined
|
|
||||||
color: number | string
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function LineChartDefault({
|
|
||||||
chartData,
|
|
||||||
max,
|
|
||||||
maxToggled,
|
|
||||||
tickFormatter,
|
|
||||||
contentFormatter,
|
|
||||||
dataPoints,
|
|
||||||
domain,
|
|
||||||
legend,
|
|
||||||
itemSorter,
|
|
||||||
}: // logRender = false,
|
|
||||||
{
|
|
||||||
chartData: ChartData
|
|
||||||
max?: number
|
|
||||||
maxToggled?: boolean
|
|
||||||
tickFormatter: (value: number, index: number) => string
|
|
||||||
contentFormatter: ({ value, payload }: { value: number; payload: SystemStatsRecord }) => string
|
|
||||||
dataPoints?: DataPoint[]
|
|
||||||
domain?: [number, number]
|
|
||||||
legend?: boolean
|
|
||||||
itemSorter?: (a: any, b: any) => number
|
|
||||||
// logRender?: boolean
|
|
||||||
}) {
|
|
||||||
const { yAxisWidth, updateYAxisWidth } = useYAxisWidth()
|
|
||||||
|
|
||||||
// biome-ignore lint/correctness/useExhaustiveDependencies: ignore
|
|
||||||
return useMemo(() => {
|
|
||||||
if (chartData.systemStats.length === 0) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
// if (logRender) {
|
|
||||||
// console.log("Rendered at", new Date())
|
|
||||||
// }
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<ChartContainer
|
|
||||||
className={cn("h-full w-full absolute aspect-auto bg-card opacity-0 transition-opacity", {
|
|
||||||
"opacity-100": yAxisWidth,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<LineChart accessibilityLayer data={chartData.systemStats} margin={chartMargin}>
|
|
||||||
<CartesianGrid vertical={false} />
|
|
||||||
<YAxis
|
|
||||||
direction="ltr"
|
|
||||||
orientation={chartData.orientation}
|
|
||||||
className="tracking-tighter"
|
|
||||||
width={yAxisWidth}
|
|
||||||
domain={domain ?? [0, max ?? "auto"]}
|
|
||||||
tickFormatter={(value, index) => updateYAxisWidth(tickFormatter(value, index))}
|
|
||||||
tickLine={false}
|
|
||||||
axisLine={false}
|
|
||||||
/>
|
|
||||||
{xAxis(chartData)}
|
|
||||||
<ChartTooltip
|
|
||||||
animationEasing="ease-out"
|
|
||||||
animationDuration={150}
|
|
||||||
// @ts-expect-error
|
|
||||||
itemSorter={itemSorter}
|
|
||||||
content={
|
|
||||||
<ChartTooltipContent
|
|
||||||
labelFormatter={(_, data) => formatShortDate(data[0].payload.created)}
|
|
||||||
contentFormatter={contentFormatter}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
{dataPoints?.map((dataPoint) => {
|
|
||||||
let { color } = dataPoint
|
|
||||||
if (typeof color === "number") {
|
|
||||||
color = `var(--chart-${color})`
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<Line
|
|
||||||
key={dataPoint.label}
|
|
||||||
dataKey={dataPoint.dataKey}
|
|
||||||
name={dataPoint.label}
|
|
||||||
type="monotoneX"
|
|
||||||
dot={false}
|
|
||||||
strokeWidth={1.5}
|
|
||||||
stroke={color}
|
|
||||||
isAnimationActive={false}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
{legend && <ChartLegend content={<ChartLegendContent />} />}
|
|
||||||
</LineChart>
|
|
||||||
</ChartContainer>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}, [chartData.systemStats.at(-1), yAxisWidth, maxToggled])
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import { t } from "@lingui/core/macro"
|
|
||||||
import { memo } from "react"
|
|
||||||
import { CartesianGrid, Line, LineChart, YAxis } from "recharts"
|
import { CartesianGrid, Line, LineChart, YAxis } from "recharts"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ChartContainer,
|
ChartContainer,
|
||||||
ChartLegend,
|
ChartLegend,
|
||||||
@@ -9,8 +8,10 @@ import {
|
|||||||
ChartTooltipContent,
|
ChartTooltipContent,
|
||||||
xAxis,
|
xAxis,
|
||||||
} from "@/components/ui/chart"
|
} from "@/components/ui/chart"
|
||||||
import { chartMargin, cn, decimalString, formatShortDate, toFixedFloat } from "@/lib/utils"
|
import { cn, formatShortDate, toFixedFloat, decimalString, chartMargin } from "@/lib/utils"
|
||||||
import type { ChartData, SystemStats } from "@/types"
|
import { ChartData, SystemStats } from "@/types"
|
||||||
|
import { memo } from "react"
|
||||||
|
import { t } from "@lingui/core/macro"
|
||||||
import { useYAxisWidth } from "./hooks"
|
import { useYAxisWidth } from "./hooks"
|
||||||
|
|
||||||
export default memo(function LoadAverageChart({ chartData }: { chartData: ChartData }) {
|
export default memo(function LoadAverageChart({ chartData }: { chartData: ChartData }) {
|
||||||
@@ -59,7 +60,7 @@ export default memo(function LoadAverageChart({ chartData }: { chartData: ChartD
|
|||||||
<ChartTooltip
|
<ChartTooltip
|
||||||
animationEasing="ease-out"
|
animationEasing="ease-out"
|
||||||
animationDuration={150}
|
animationDuration={150}
|
||||||
// @ts-expect-error
|
// @ts-ignore
|
||||||
// itemSorter={(a, b) => b.value - a.value}
|
// itemSorter={(a, b) => b.value - a.value}
|
||||||
content={
|
content={
|
||||||
<ChartTooltipContent
|
<ChartTooltipContent
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { useLingui } from "@lingui/react/macro"
|
|
||||||
import { memo } from "react"
|
|
||||||
import { Area, AreaChart, CartesianGrid, YAxis } from "recharts"
|
import { Area, AreaChart, CartesianGrid, YAxis } from "recharts"
|
||||||
import { ChartContainer, ChartTooltip, ChartTooltipContent, xAxis } from "@/components/ui/chart"
|
import { ChartContainer, ChartTooltip, ChartTooltipContent, xAxis } from "@/components/ui/chart"
|
||||||
|
import { cn, decimalString, formatShortDate, chartMargin, formatBytes, toFixedFloat } from "@/lib/utils"
|
||||||
|
import { memo } from "react"
|
||||||
|
import { ChartData } from "@/types"
|
||||||
|
import { useLingui } from "@lingui/react/macro"
|
||||||
import { Unit } from "@/lib/enums"
|
import { Unit } from "@/lib/enums"
|
||||||
import { chartMargin, cn, decimalString, formatBytes, formatShortDate, toFixedFloat } from "@/lib/utils"
|
|
||||||
import type { ChartData } from "@/types"
|
|
||||||
import { useYAxisWidth } from "./hooks"
|
import { useYAxisWidth } from "./hooks"
|
||||||
|
|
||||||
export default memo(function MemChart({ chartData, showMax }: { chartData: ChartData; showMax: boolean }) {
|
export default memo(function MemChart({ chartData, showMax }: { chartData: ChartData; showMax: boolean }) {
|
||||||
@@ -53,7 +53,7 @@ export default memo(function MemChart({ chartData, showMax }: { chartData: Chart
|
|||||||
animationDuration={150}
|
animationDuration={150}
|
||||||
content={
|
content={
|
||||||
<ChartTooltipContent
|
<ChartTooltipContent
|
||||||
// @ts-expect-error
|
// @ts-ignore
|
||||||
itemSorter={(a, b) => a.order - b.order}
|
itemSorter={(a, b) => a.order - b.order}
|
||||||
labelFormatter={(_, data) => formatShortDate(data[0].payload.created)}
|
labelFormatter={(_, data) => formatShortDate(data[0].payload.created)}
|
||||||
contentFormatter={({ value }) => {
|
contentFormatter={({ value }) => {
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import { t } from "@lingui/core/macro"
|
import { t } from "@lingui/core/macro"
|
||||||
import { useStore } from "@nanostores/react"
|
|
||||||
import { memo } from "react"
|
|
||||||
import { Area, AreaChart, CartesianGrid, YAxis } from "recharts"
|
import { Area, AreaChart, CartesianGrid, YAxis } from "recharts"
|
||||||
import { ChartContainer, ChartTooltip, ChartTooltipContent, xAxis } from "@/components/ui/chart"
|
import { ChartContainer, ChartTooltip, ChartTooltipContent, xAxis } from "@/components/ui/chart"
|
||||||
|
import { cn, formatShortDate, decimalString, chartMargin, formatBytes, toFixedFloat } from "@/lib/utils"
|
||||||
|
import { ChartData } from "@/types"
|
||||||
|
import { memo } from "react"
|
||||||
import { $userSettings } from "@/lib/stores"
|
import { $userSettings } from "@/lib/stores"
|
||||||
import { chartMargin, cn, decimalString, formatBytes, formatShortDate, toFixedFloat } from "@/lib/utils"
|
import { useStore } from "@nanostores/react"
|
||||||
import type { ChartData } from "@/types"
|
|
||||||
import { useYAxisWidth } from "./hooks"
|
import { useYAxisWidth } from "./hooks"
|
||||||
|
|
||||||
export default memo(function SwapChart({ chartData }: { chartData: ChartData }) {
|
export default memo(function SwapChart({ chartData }: { chartData: ChartData }) {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { useStore } from "@nanostores/react"
|
|
||||||
import { memo, useMemo } from "react"
|
|
||||||
import { CartesianGrid, Line, LineChart, YAxis } from "recharts"
|
import { CartesianGrid, Line, LineChart, YAxis } from "recharts"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ChartContainer,
|
ChartContainer,
|
||||||
ChartLegend,
|
ChartLegend,
|
||||||
@@ -9,9 +8,11 @@ import {
|
|||||||
ChartTooltipContent,
|
ChartTooltipContent,
|
||||||
xAxis,
|
xAxis,
|
||||||
} from "@/components/ui/chart"
|
} from "@/components/ui/chart"
|
||||||
|
import { cn, formatShortDate, toFixedFloat, chartMargin, formatTemperature, decimalString } from "@/lib/utils"
|
||||||
|
import { ChartData } from "@/types"
|
||||||
|
import { memo, useMemo } from "react"
|
||||||
import { $temperatureFilter, $userSettings } from "@/lib/stores"
|
import { $temperatureFilter, $userSettings } from "@/lib/stores"
|
||||||
import { chartMargin, cn, decimalString, formatShortDate, formatTemperature, toFixedFloat } from "@/lib/utils"
|
import { useStore } from "@nanostores/react"
|
||||||
import type { ChartData } from "@/types"
|
|
||||||
import { useYAxisWidth } from "./hooks"
|
import { useYAxisWidth } from "./hooks"
|
||||||
|
|
||||||
export default memo(function TemperatureChart({ chartData }: { chartData: ChartData }) {
|
export default memo(function TemperatureChart({ chartData }: { chartData: ChartData }) {
|
||||||
@@ -30,18 +31,18 @@ export default memo(function TemperatureChart({ chartData }: { chartData: ChartD
|
|||||||
colors: Record<string, string>
|
colors: Record<string, string>
|
||||||
}
|
}
|
||||||
const tempSums = {} as Record<string, number>
|
const tempSums = {} as Record<string, number>
|
||||||
for (const data of chartData.systemStats) {
|
for (let data of chartData.systemStats) {
|
||||||
const newData = { created: data.created } as Record<string, number | string>
|
let newData = { created: data.created } as Record<string, number | string>
|
||||||
const keys = Object.keys(data.stats?.t ?? {})
|
let keys = Object.keys(data.stats?.t ?? {})
|
||||||
for (let i = 0; i < keys.length; i++) {
|
for (let i = 0; i < keys.length; i++) {
|
||||||
const key = keys[i]
|
let key = keys[i]
|
||||||
newData[key] = data.stats.t![key]
|
newData[key] = data.stats.t![key]
|
||||||
tempSums[key] = (tempSums[key] ?? 0) + newData[key]
|
tempSums[key] = (tempSums[key] ?? 0) + newData[key]
|
||||||
}
|
}
|
||||||
newChartData.data.push(newData)
|
newChartData.data.push(newData)
|
||||||
}
|
}
|
||||||
const keys = Object.keys(tempSums).sort((a, b) => tempSums[b] - tempSums[a])
|
const keys = Object.keys(tempSums).sort((a, b) => tempSums[b] - tempSums[a])
|
||||||
for (const key of keys) {
|
for (let key of keys) {
|
||||||
newChartData.colors[key] = `hsl(${((keys.indexOf(key) * 360) / keys.length) % 360}, 60%, 55%)`
|
newChartData.colors[key] = `hsl(${((keys.indexOf(key) * 360) / keys.length) % 360}, 60%, 55%)`
|
||||||
}
|
}
|
||||||
return newChartData
|
return newChartData
|
||||||
@@ -77,7 +78,7 @@ export default memo(function TemperatureChart({ chartData }: { chartData: ChartD
|
|||||||
<ChartTooltip
|
<ChartTooltip
|
||||||
animationEasing="ease-out"
|
animationEasing="ease-out"
|
||||||
animationDuration={150}
|
animationDuration={150}
|
||||||
// @ts-expect-error
|
// @ts-ignore
|
||||||
itemSorter={(a, b) => b.value - a.value}
|
itemSorter={(a, b) => b.value - a.value}
|
||||||
content={
|
content={
|
||||||
<ChartTooltipContent
|
<ChartTooltipContent
|
||||||
@@ -92,7 +93,7 @@ export default memo(function TemperatureChart({ chartData }: { chartData: ChartD
|
|||||||
/>
|
/>
|
||||||
{colors.map((key) => {
|
{colors.map((key) => {
|
||||||
const filtered = filter && !key.toLowerCase().includes(filter.toLowerCase())
|
const filtered = filter && !key.toLowerCase().includes(filter.toLowerCase())
|
||||||
const strokeOpacity = filtered ? 0.1 : 1
|
let strokeOpacity = filtered ? 0.1 : 1
|
||||||
return (
|
return (
|
||||||
<Line
|
<Line
|
||||||
key={key}
|
key={key}
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
import { t } from "@lingui/core/macro"
|
|
||||||
import { Trans } from "@lingui/react/macro"
|
|
||||||
import { getPagePath } from "@nanostores/router"
|
|
||||||
import { DialogDescription } from "@radix-ui/react-dialog"
|
|
||||||
import {
|
import {
|
||||||
AlertOctagonIcon,
|
AlertOctagonIcon,
|
||||||
BookIcon,
|
BookIcon,
|
||||||
@@ -14,7 +10,7 @@ import {
|
|||||||
SettingsIcon,
|
SettingsIcon,
|
||||||
UsersIcon,
|
UsersIcon,
|
||||||
} from "lucide-react"
|
} from "lucide-react"
|
||||||
import { memo, useEffect, useMemo } from "react"
|
|
||||||
import {
|
import {
|
||||||
CommandDialog,
|
CommandDialog,
|
||||||
CommandEmpty,
|
CommandEmpty,
|
||||||
@@ -25,10 +21,15 @@ import {
|
|||||||
CommandSeparator,
|
CommandSeparator,
|
||||||
CommandShortcut,
|
CommandShortcut,
|
||||||
} from "@/components/ui/command"
|
} from "@/components/ui/command"
|
||||||
import { isAdmin } from "@/lib/api"
|
import { memo, useEffect, useMemo } from "react"
|
||||||
import { $systems } from "@/lib/stores"
|
import { $systems } from "@/lib/stores"
|
||||||
import { getHostDisplayValue, listen } from "@/lib/utils"
|
import { getHostDisplayValue, listen } from "@/lib/utils"
|
||||||
import { $router, basePath, navigate, prependBasePath } from "./router"
|
import { $router, basePath, navigate, prependBasePath } from "./router"
|
||||||
|
import { Trans } from "@lingui/react/macro"
|
||||||
|
import { t } from "@lingui/core/macro"
|
||||||
|
import { getPagePath } from "@nanostores/router"
|
||||||
|
import { DialogDescription } from "@radix-ui/react-dialog"
|
||||||
|
import { isAdmin } from "@/lib/api"
|
||||||
|
|
||||||
export default memo(function CommandPalette({ open, setOpen }: { open: boolean; setOpen: (open: boolean) => void }) {
|
export default memo(function CommandPalette({ open, setOpen }: { open: boolean; setOpen: (open: boolean) => void }) {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { Trans } from "@lingui/react/macro"
|
import { Trans } from "@lingui/react/macro";
|
||||||
import { useEffect, useMemo, useRef } from "react"
|
import { useEffect, useMemo, useRef } from "react"
|
||||||
import { $copyContent } from "@/lib/stores"
|
|
||||||
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "./ui/dialog"
|
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "./ui/dialog"
|
||||||
import { Textarea } from "./ui/textarea"
|
import { Textarea } from "./ui/textarea"
|
||||||
|
import { $copyContent } from "@/lib/stores"
|
||||||
|
|
||||||
export default function CopyToClipboard({ content }: { content: string }) {
|
export default function CopyToClipboard({ content }: { content: string }) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { i18n } from "@lingui/core"
|
|
||||||
import { memo } from "react"
|
import { memo } from "react"
|
||||||
import { copyToClipboard, getHubURL } from "@/lib/utils"
|
|
||||||
import { DropdownMenuContent, DropdownMenuItem } from "./ui/dropdown-menu"
|
import { DropdownMenuContent, DropdownMenuItem } from "./ui/dropdown-menu"
|
||||||
|
import { copyToClipboard, getHubURL } from "@/lib/utils"
|
||||||
|
import { i18n } from "@lingui/core"
|
||||||
|
|
||||||
// const isbeta = beszel.hub_version.includes("beta")
|
// const isbeta = beszel.hub_version.includes("beta")
|
||||||
// const imagetag = isbeta ? ":edge" : ""
|
// const imagetag = isbeta ? ":edge" : ""
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import { useLingui } from "@lingui/react/macro"
|
|
||||||
import { LanguagesIcon } from "lucide-react"
|
import { LanguagesIcon } from "lucide-react"
|
||||||
|
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"
|
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"
|
||||||
import { dynamicActivate } from "@/lib/i18n"
|
|
||||||
import languages from "@/lib/languages"
|
import languages from "@/lib/languages"
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
import { useLingui } from "@lingui/react/macro"
|
||||||
|
import { dynamicActivate } from "@/lib/i18n"
|
||||||
|
|
||||||
export function LangToggle() {
|
export function LangToggle() {
|
||||||
const { i18n } = useLingui()
|
const { i18n } = useLingui()
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
import { t } from "@lingui/core/macro"
|
import { t } from "@lingui/core/macro"
|
||||||
import { Trans } from "@lingui/react/macro"
|
import { Trans } from "@lingui/react/macro"
|
||||||
import { getPagePath } from "@nanostores/router"
|
import { cn } from "@/lib/utils"
|
||||||
import { KeyIcon, LoaderCircle, LockIcon, LogInIcon, MailIcon } from "lucide-react"
|
|
||||||
import type { AuthMethodsList, AuthProviderInfo, OAuth2AuthConfig } from "pocketbase"
|
|
||||||
import { useCallback, useEffect, useState } from "react"
|
|
||||||
import * as v from "valibot"
|
|
||||||
import { buttonVariants } from "@/components/ui/button"
|
import { buttonVariants } from "@/components/ui/button"
|
||||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"
|
|
||||||
import { Input } from "@/components/ui/input"
|
import { Input } from "@/components/ui/input"
|
||||||
import { Label } from "@/components/ui/label"
|
import { Label } from "@/components/ui/label"
|
||||||
import { pb } from "@/lib/api"
|
import { KeyIcon, LoaderCircle, LockIcon, LogInIcon, MailIcon } from "lucide-react"
|
||||||
import { $authenticated } from "@/lib/stores"
|
import { $authenticated } from "@/lib/stores"
|
||||||
import { cn } from "@/lib/utils"
|
import * as v from "valibot"
|
||||||
import { $router, Link, prependBasePath } from "../router"
|
|
||||||
import { toast } from "../ui/use-toast"
|
import { toast } from "../ui/use-toast"
|
||||||
|
import { Dialog, DialogContent, DialogTrigger, DialogHeader, DialogTitle } from "@/components/ui/dialog"
|
||||||
|
import { useCallback, useEffect, useState } from "react"
|
||||||
|
import { AuthMethodsList, AuthProviderInfo, OAuth2AuthConfig } from "pocketbase"
|
||||||
|
import { $router, Link, prependBasePath } from "../router"
|
||||||
|
import { getPagePath } from "@nanostores/router"
|
||||||
|
import { pb } from "@/lib/api"
|
||||||
import { OtpInputForm } from "./otp-forms"
|
import { OtpInputForm } from "./otp-forms"
|
||||||
|
|
||||||
const honeypot = v.literal("")
|
const honeypot = v.literal("")
|
||||||
@@ -83,9 +83,9 @@ export function UserAuthForm({
|
|||||||
const result = v.safeParse(Schema, data)
|
const result = v.safeParse(Schema, data)
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
console.log(result)
|
console.log(result)
|
||||||
const errors = {}
|
let errors = {}
|
||||||
for (const issue of result.issues) {
|
for (const issue of result.issues) {
|
||||||
// @ts-expect-error
|
// @ts-ignore
|
||||||
errors[issue.path[0].key] = issue.message
|
errors[issue.path[0].key] = issue.message
|
||||||
}
|
}
|
||||||
setErrors(errors)
|
setErrors(errors)
|
||||||
@@ -96,7 +96,7 @@ export function UserAuthForm({
|
|||||||
if (isFirstRun) {
|
if (isFirstRun) {
|
||||||
// check that passwords match
|
// check that passwords match
|
||||||
if (password !== passwordConfirm) {
|
if (password !== passwordConfirm) {
|
||||||
const msg = "Passwords do not match"
|
let msg = "Passwords do not match"
|
||||||
setErrors({ passwordConfirm: msg })
|
setErrors({ passwordConfirm: msg })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
import { t } from "@lingui/core/macro"
|
|
||||||
import { Trans } from "@lingui/react/macro"
|
import { Trans } from "@lingui/react/macro"
|
||||||
|
import { t } from "@lingui/core/macro"
|
||||||
import { LoaderCircle, MailIcon, SendHorizonalIcon } from "lucide-react"
|
import { LoaderCircle, MailIcon, SendHorizonalIcon } from "lucide-react"
|
||||||
import { useCallback, useState } from "react"
|
|
||||||
import { pb } from "@/lib/api"
|
|
||||||
import { cn } from "@/lib/utils"
|
|
||||||
import { buttonVariants } from "../ui/button"
|
|
||||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "../ui/dialog"
|
|
||||||
import { Input } from "../ui/input"
|
import { Input } from "../ui/input"
|
||||||
import { Label } from "../ui/label"
|
import { Label } from "../ui/label"
|
||||||
|
import { useCallback, useState } from "react"
|
||||||
import { toast } from "../ui/use-toast"
|
import { toast } from "../ui/use-toast"
|
||||||
|
import { buttonVariants } from "../ui/button"
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
import { Dialog, DialogHeader } from "../ui/dialog"
|
||||||
|
import { DialogContent, DialogTrigger, DialogTitle } from "../ui/dialog"
|
||||||
|
import { pb } from "@/lib/api"
|
||||||
|
|
||||||
const showLoginFaliedToast = () => {
|
const showLoginFaliedToast = () => {
|
||||||
toast({
|
toast({
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import { t } from "@lingui/core/macro"
|
import { t } from "@lingui/core/macro"
|
||||||
import { useStore } from "@nanostores/react"
|
|
||||||
import type { AuthMethodsList } from "pocketbase"
|
|
||||||
import { useEffect, useMemo, useState } from "react"
|
|
||||||
import { UserAuthForm } from "@/components/login/auth-form"
|
import { UserAuthForm } from "@/components/login/auth-form"
|
||||||
import { pb } from "@/lib/api"
|
|
||||||
import { Logo } from "../logo"
|
import { Logo } from "../logo"
|
||||||
import { ModeToggle } from "../mode-toggle"
|
import { useEffect, useMemo, useState } from "react"
|
||||||
import { $router } from "../router"
|
import { useStore } from "@nanostores/react"
|
||||||
import { useTheme } from "../theme-provider"
|
|
||||||
import ForgotPassword from "./forgot-pass-form"
|
import ForgotPassword from "./forgot-pass-form"
|
||||||
|
import { $router } from "../router"
|
||||||
|
import { AuthMethodsList } from "pocketbase"
|
||||||
|
import { useTheme } from "../theme-provider"
|
||||||
|
import { pb } from "@/lib/api"
|
||||||
|
import { ModeToggle } from "../mode-toggle"
|
||||||
import { OtpRequestForm } from "./otp-forms"
|
import { OtpRequestForm } from "./otp-forms"
|
||||||
|
|
||||||
export default function () {
|
export default function () {
|
||||||
@@ -53,7 +53,7 @@ export default function () {
|
|||||||
<div className="min-h-svh grid items-center py-12">
|
<div className="min-h-svh grid items-center py-12">
|
||||||
<div
|
<div
|
||||||
className="grid gap-5 w-full px-4 mx-auto"
|
className="grid gap-5 w-full px-4 mx-auto"
|
||||||
// @ts-expect-error
|
// @ts-ignore
|
||||||
style={{ maxWidth: "21.5em", "--border": theme == "light" ? "hsl(30, 8%, 70%)" : "hsl(220, 3%, 25%)" }}
|
style={{ maxWidth: "21.5em", "--border": theme == "light" ? "hsl(30, 8%, 70%)" : "hsl(220, 3%, 25%)" }}
|
||||||
>
|
>
|
||||||
<div className="absolute top-3 right-3">
|
<div className="absolute top-3 right-3">
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import { Trans } from "@lingui/react/macro"
|
|
||||||
import { LoaderCircle, MailIcon, SendHorizonalIcon } from "lucide-react"
|
|
||||||
import { useCallback, useState } from "react"
|
import { useCallback, useState } from "react"
|
||||||
import { InputOTP, InputOTPGroup, InputOTPSlot } from "@/components/ui/otp"
|
|
||||||
import { pb } from "@/lib/api"
|
import { pb } from "@/lib/api"
|
||||||
import { $authenticated } from "@/lib/stores"
|
import { $authenticated } from "@/lib/stores"
|
||||||
|
import { InputOTP, InputOTPGroup, InputOTPSlot } from "@/components/ui/otp"
|
||||||
|
import { Trans } from "@lingui/react/macro"
|
||||||
|
import { showLoginFaliedToast } from "./auth-form"
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
import { $router } from "../router"
|
import { MailIcon, LoaderCircle, SendHorizonalIcon } from "lucide-react"
|
||||||
|
import { Label } from "../ui/label"
|
||||||
import { buttonVariants } from "../ui/button"
|
import { buttonVariants } from "../ui/button"
|
||||||
import { Input } from "../ui/input"
|
import { Input } from "../ui/input"
|
||||||
import { Label } from "../ui/label"
|
import { $router } from "../router"
|
||||||
import { showLoginFaliedToast } from "./auth-form"
|
|
||||||
|
|
||||||
export function OtpInputForm({ otpId, mfaId }: { otpId: string; mfaId: string }) {
|
export function OtpInputForm({ otpId, mfaId }: { otpId: string; mfaId: string }) {
|
||||||
const [value, setValue] = useState("")
|
const [value, setValue] = useState("")
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { t } from "@lingui/core/macro"
|
import { t } from "@lingui/core/macro"
|
||||||
import { MoonStarIcon, SunIcon } from "lucide-react"
|
import { MoonStarIcon, SunIcon } from "lucide-react"
|
||||||
import { useTheme } from "@/components/theme-provider"
|
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
|
import { useTheme } from "@/components/theme-provider"
|
||||||
|
|
||||||
export function ModeToggle() {
|
export function ModeToggle() {
|
||||||
const { theme, setTheme } = useTheme()
|
const { theme, setTheme } = useTheme()
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Trans } from "@lingui/react/macro"
|
import { Trans } from "@lingui/react/macro"
|
||||||
import { getPagePath } from "@nanostores/router"
|
import { useState, lazy, Suspense } from "react"
|
||||||
|
import { Button, buttonVariants } from "@/components/ui/button"
|
||||||
import {
|
import {
|
||||||
DatabaseBackupIcon,
|
DatabaseBackupIcon,
|
||||||
LogOutIcon,
|
LogOutIcon,
|
||||||
@@ -10,24 +11,23 @@ import {
|
|||||||
UserIcon,
|
UserIcon,
|
||||||
UsersIcon,
|
UsersIcon,
|
||||||
} from "lucide-react"
|
} from "lucide-react"
|
||||||
import { lazy, Suspense, useState } from "react"
|
import { $router, basePath, Link, prependBasePath } from "./router"
|
||||||
import { Button, buttonVariants } from "@/components/ui/button"
|
import { LangToggle } from "./lang-toggle"
|
||||||
|
import { ModeToggle } from "./mode-toggle"
|
||||||
|
import { Logo } from "./logo"
|
||||||
|
import { cn, runOnce } from "@/lib/utils"
|
||||||
|
import { isReadOnlyUser, isAdmin, logOut, pb } from "@/lib/api"
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
|
DropdownMenuTrigger,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
DropdownMenuGroup,
|
|
||||||
DropdownMenuItem,
|
|
||||||
DropdownMenuLabel,
|
DropdownMenuLabel,
|
||||||
DropdownMenuSeparator,
|
DropdownMenuSeparator,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuGroup,
|
||||||
|
DropdownMenuItem,
|
||||||
} from "@/components/ui/dropdown-menu"
|
} from "@/components/ui/dropdown-menu"
|
||||||
import { isAdmin, isReadOnlyUser, logOut, pb } from "@/lib/api"
|
|
||||||
import { cn, runOnce } from "@/lib/utils"
|
|
||||||
import { AddSystemButton } from "./add-system"
|
import { AddSystemButton } from "./add-system"
|
||||||
import { LangToggle } from "./lang-toggle"
|
import { getPagePath } from "@nanostores/router"
|
||||||
import { Logo } from "./logo"
|
|
||||||
import { ModeToggle } from "./mode-toggle"
|
|
||||||
import { $router, basePath, Link, prependBasePath } from "./router"
|
|
||||||
|
|
||||||
const CommandPalette = lazy(() => import("./command-palette"))
|
const CommandPalette = lazy(() => import("./command-palette"))
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export const prependBasePath = (path: string) => (basePath + path).replaceAll("/
|
|||||||
|
|
||||||
// prepend base path to routes
|
// prepend base path to routes
|
||||||
for (const route in routes) {
|
for (const route in routes) {
|
||||||
// @ts-expect-error need as const above to get nanostores to parse types properly
|
// @ts-ignore need as const above to get nanostores to parse types properly
|
||||||
routes[route] = prependBasePath(routes[route])
|
routes[route] = prependBasePath(routes[route])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { Trans, useLingui } from "@lingui/react/macro"
|
|||||||
import { redirectPage } from "@nanostores/router"
|
import { redirectPage } from "@nanostores/router"
|
||||||
import {
|
import {
|
||||||
CopyIcon,
|
CopyIcon,
|
||||||
ExternalLinkIcon,
|
|
||||||
FingerprintIcon,
|
FingerprintIcon,
|
||||||
KeyIcon,
|
KeyIcon,
|
||||||
MoreHorizontalIcon,
|
MoreHorizontalIcon,
|
||||||
@@ -29,7 +28,7 @@ import {
|
|||||||
DropdownMenuSeparator,
|
DropdownMenuSeparator,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu"
|
} from "@/components/ui/dropdown-menu"
|
||||||
import { AppleIcon, DockerIcon, FreeBsdIcon, TuxIcon, WindowsIcon } from "@/components/ui/icons"
|
import { AppleIcon, DockerIcon, TuxIcon, WindowsIcon } from "@/components/ui/icons"
|
||||||
import { Separator } from "@/components/ui/separator"
|
import { Separator } from "@/components/ui/separator"
|
||||||
import { Switch } from "@/components/ui/switch"
|
import { Switch } from "@/components/ui/switch"
|
||||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
|
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
|
||||||
@@ -151,7 +150,6 @@ const SectionUniversalToken = memo(() => {
|
|||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// biome-ignore lint/correctness/useExhaustiveDependencies: only on mount
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
updateToken()
|
updateToken()
|
||||||
}, [])
|
}, [])
|
||||||
@@ -223,16 +221,6 @@ const ActionsButtonUniversalToken = memo(({ token, checked }: { token: string; c
|
|||||||
onClick: () => copyWindowsCommand(port, publicKey, token),
|
onClick: () => copyWindowsCommand(port, publicKey, token),
|
||||||
icons: [WindowsIcon],
|
icons: [WindowsIcon],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
text: t({ message: "FreeBSD command", context: "Button to copy install command" }),
|
|
||||||
onClick: () => copyLinuxCommand(port, publicKey, token),
|
|
||||||
icons: [FreeBsdIcon],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: t`Manual setup instructions`,
|
|
||||||
url: "https://beszel.dev/guide/agent-installation#binary",
|
|
||||||
icons: [ExternalLinkIcon],
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
@@ -303,8 +291,8 @@ const SectionTable = memo(({ fingerprints = [] }: { fingerprints: FingerprintRec
|
|||||||
</tr>
|
</tr>
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
<TableBody className="whitespace-pre">
|
<TableBody className="whitespace-pre">
|
||||||
{fingerprints.map((fingerprint) => (
|
{fingerprints.map((fingerprint, i) => (
|
||||||
<TableRow key={fingerprint.id}>
|
<TableRow key={i}>
|
||||||
<TableCell className="font-medium ps-5 py-2 max-w-60 truncate">
|
<TableCell className="font-medium ps-5 py-2 max-w-60 truncate">
|
||||||
{fingerprint.expand.system.name}
|
{fingerprint.expand.system.name}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
@@ -329,10 +317,10 @@ async function updateFingerprint(fingerprint: FingerprintRecord, rotateToken = f
|
|||||||
fingerprint: "",
|
fingerprint: "",
|
||||||
token: rotateToken ? generateToken() : fingerprint.token,
|
token: rotateToken ? generateToken() : fingerprint.token,
|
||||||
})
|
})
|
||||||
} catch (error: unknown) {
|
} catch (error: any) {
|
||||||
toast({
|
toast({
|
||||||
title: t`Error`,
|
title: t`Error`,
|
||||||
description: (error as Error).message,
|
description: error.message,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,15 +3,7 @@ import { Plural, Trans, useLingui } from "@lingui/react/macro"
|
|||||||
import { useStore } from "@nanostores/react"
|
import { useStore } from "@nanostores/react"
|
||||||
import { getPagePath } from "@nanostores/router"
|
import { getPagePath } from "@nanostores/router"
|
||||||
import { timeTicks } from "d3-time"
|
import { timeTicks } from "d3-time"
|
||||||
import {
|
import { ClockArrowUp, CpuIcon, GlobeIcon, LayoutGridIcon, MonitorIcon, XIcon } from "lucide-react"
|
||||||
ChevronRightSquareIcon,
|
|
||||||
ClockArrowUp,
|
|
||||||
CpuIcon,
|
|
||||||
GlobeIcon,
|
|
||||||
LayoutGridIcon,
|
|
||||||
MonitorIcon,
|
|
||||||
XIcon,
|
|
||||||
} from "lucide-react"
|
|
||||||
import { subscribeKeys } from "nanostores"
|
import { subscribeKeys } from "nanostores"
|
||||||
import React, { type JSX, memo, useCallback, useEffect, useMemo, useRef, useState } from "react"
|
import React, { type JSX, memo, useCallback, useEffect, useMemo, useRef, useState } from "react"
|
||||||
import AreaChartDefault from "@/components/charts/area-chart"
|
import AreaChartDefault from "@/components/charts/area-chart"
|
||||||
@@ -24,7 +16,7 @@ import MemChart from "@/components/charts/mem-chart"
|
|||||||
import SwapChart from "@/components/charts/swap-chart"
|
import SwapChart from "@/components/charts/swap-chart"
|
||||||
import TemperatureChart from "@/components/charts/temperature-chart"
|
import TemperatureChart from "@/components/charts/temperature-chart"
|
||||||
import { getPbTimestamp, pb } from "@/lib/api"
|
import { getPbTimestamp, pb } from "@/lib/api"
|
||||||
import { ChartType, ConnectionType, Os, SystemStatus, Unit } from "@/lib/enums"
|
import { ChartType, Os, SystemStatus, Unit } from "@/lib/enums"
|
||||||
import { batteryStateTranslations } from "@/lib/i18n"
|
import { batteryStateTranslations } from "@/lib/i18n"
|
||||||
import {
|
import {
|
||||||
$allSystemsByName,
|
$allSystemsByName,
|
||||||
@@ -55,13 +47,11 @@ import { $router, navigate } from "../router"
|
|||||||
import Spinner from "../spinner"
|
import Spinner from "../spinner"
|
||||||
import { Button } from "../ui/button"
|
import { Button } from "../ui/button"
|
||||||
import { Card, CardDescription, CardHeader, CardTitle } from "../ui/card"
|
import { Card, CardDescription, CardHeader, CardTitle } from "../ui/card"
|
||||||
import { AppleIcon, ChartAverage, ChartMax, FreeBsdIcon, Rows, TuxIcon, WebSocketIcon, WindowsIcon } from "../ui/icons"
|
import { AppleIcon, ChartAverage, ChartMax, FreeBsdIcon, Rows, TuxIcon, WindowsIcon } from "../ui/icons"
|
||||||
import { Input } from "../ui/input"
|
import { Input } from "../ui/input"
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../ui/select"
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../ui/select"
|
||||||
import { Separator } from "../ui/separator"
|
import { Separator } from "../ui/separator"
|
||||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "../ui/tooltip"
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "../ui/tooltip"
|
||||||
import NetworkSheet from "./system/network-sheet"
|
|
||||||
import LineChartDefault from "../charts/line-chart"
|
|
||||||
|
|
||||||
type ChartTimeData = {
|
type ChartTimeData = {
|
||||||
time: number
|
time: number
|
||||||
@@ -139,7 +129,7 @@ async function getStats<T extends SystemStatsRecord | ContainerStatsRecord>(
|
|||||||
|
|
||||||
function dockerOrPodman(str: string, system: SystemRecord) {
|
function dockerOrPodman(str: string, system: SystemRecord) {
|
||||||
if (system.info.p) {
|
if (system.info.p) {
|
||||||
return str.replace("docker", "podman").replace("Docker", "Podman")
|
str = str.replace("docker", "podman").replace("Docker", "Podman")
|
||||||
}
|
}
|
||||||
return str
|
return str
|
||||||
}
|
}
|
||||||
@@ -399,7 +389,6 @@ export default memo(function SystemDetail({ name }: { name: string }) {
|
|||||||
const lastGpuVals = Object.values(systemStats.at(-1)?.stats.g ?? {})
|
const lastGpuVals = Object.values(systemStats.at(-1)?.stats.g ?? {})
|
||||||
const hasGpuData = lastGpuVals.length > 0
|
const hasGpuData = lastGpuVals.length > 0
|
||||||
const hasGpuPowerData = lastGpuVals.some((gpu) => gpu.p !== undefined)
|
const hasGpuPowerData = lastGpuVals.some((gpu) => gpu.p !== undefined)
|
||||||
const hasGpuEnginesData = lastGpuVals.some((gpu) => gpu.e !== undefined)
|
|
||||||
|
|
||||||
let translatedStatus: string = system.status
|
let translatedStatus: string = system.status
|
||||||
if (system.status === SystemStatus.Up) {
|
if (system.status === SystemStatus.Up) {
|
||||||
@@ -417,45 +406,25 @@ export default memo(function SystemDetail({ name }: { name: string }) {
|
|||||||
<div>
|
<div>
|
||||||
<h1 className="text-[1.6rem] font-semibold mb-1.5">{system.name}</h1>
|
<h1 className="text-[1.6rem] font-semibold mb-1.5">{system.name}</h1>
|
||||||
<div className="flex flex-wrap items-center gap-3 gap-y-2 text-sm opacity-90">
|
<div className="flex flex-wrap items-center gap-3 gap-y-2 text-sm opacity-90">
|
||||||
<TooltipProvider>
|
<div className="capitalize flex gap-2 items-center">
|
||||||
<Tooltip>
|
<span className={cn("relative flex h-3 w-3")}>
|
||||||
<TooltipTrigger asChild>
|
{system.status === SystemStatus.Up && (
|
||||||
<div className="capitalize flex gap-2 items-center">
|
<span
|
||||||
<span className={cn("relative flex h-3 w-3")}>
|
className="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75"
|
||||||
{system.status === SystemStatus.Up && (
|
style={{ animationDuration: "1.5s" }}
|
||||||
<span
|
></span>
|
||||||
className="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75"
|
|
||||||
style={{ animationDuration: "1.5s" }}
|
|
||||||
></span>
|
|
||||||
)}
|
|
||||||
<span
|
|
||||||
className={cn("relative inline-flex rounded-full h-3 w-3", {
|
|
||||||
"bg-green-500": system.status === SystemStatus.Up,
|
|
||||||
"bg-red-500": system.status === SystemStatus.Down,
|
|
||||||
"bg-primary/40": system.status === SystemStatus.Paused,
|
|
||||||
"bg-yellow-500": system.status === SystemStatus.Pending,
|
|
||||||
})}
|
|
||||||
></span>
|
|
||||||
</span>
|
|
||||||
{translatedStatus}
|
|
||||||
</div>
|
|
||||||
</TooltipTrigger>
|
|
||||||
{system.info.ct && (
|
|
||||||
<TooltipContent>
|
|
||||||
{system.info.ct === ConnectionType.WebSocket ? (
|
|
||||||
<div className="flex gap-1 items-center">
|
|
||||||
<WebSocketIcon className="size-4" /> WebSocket
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="flex gap-1 items-center">
|
|
||||||
<ChevronRightSquareIcon className="size-4" strokeWidth={2} /> SSH
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</TooltipContent>
|
|
||||||
)}
|
)}
|
||||||
</Tooltip>
|
<span
|
||||||
</TooltipProvider>
|
className={cn("relative inline-flex rounded-full h-3 w-3", {
|
||||||
|
"bg-green-500": system.status === SystemStatus.Up,
|
||||||
|
"bg-red-500": system.status === SystemStatus.Down,
|
||||||
|
"bg-primary/40": system.status === SystemStatus.Paused,
|
||||||
|
"bg-yellow-500": system.status === SystemStatus.Pending,
|
||||||
|
})}
|
||||||
|
></span>
|
||||||
|
</span>
|
||||||
|
{translatedStatus}
|
||||||
|
</div>
|
||||||
{systemInfo.map(({ value, label, Icon, hide }) => {
|
{systemInfo.map(({ value, label, Icon, hide }) => {
|
||||||
if (hide || !value) {
|
if (hide || !value) {
|
||||||
return null
|
return null
|
||||||
@@ -595,13 +564,13 @@ export default memo(function SystemDetail({ name }: { name: string }) {
|
|||||||
dataPoints={[
|
dataPoints={[
|
||||||
{
|
{
|
||||||
label: t({ message: "Write", comment: "Disk write" }),
|
label: t({ message: "Write", comment: "Disk write" }),
|
||||||
dataKey: ({ stats }: SystemStatsRecord) => (showMax ? stats?.dwm : stats?.dw),
|
dataKey: ({ stats }) => (showMax ? stats?.dwm : stats?.dw),
|
||||||
color: 3,
|
color: 3,
|
||||||
opacity: 0.3,
|
opacity: 0.3,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t({ message: "Read", comment: "Disk read" }),
|
label: t({ message: "Read", comment: "Disk read" }),
|
||||||
dataKey: ({ stats }: SystemStatsRecord) => (showMax ? stats?.drm : stats?.dr),
|
dataKey: ({ stats }) => (showMax ? stats?.drm : stats?.dr),
|
||||||
color: 1,
|
color: 1,
|
||||||
opacity: 0.3,
|
opacity: 0.3,
|
||||||
},
|
},
|
||||||
@@ -621,12 +590,7 @@ export default memo(function SystemDetail({ name }: { name: string }) {
|
|||||||
empty={dataEmpty}
|
empty={dataEmpty}
|
||||||
grid={grid}
|
grid={grid}
|
||||||
title={t`Bandwidth`}
|
title={t`Bandwidth`}
|
||||||
cornerEl={
|
cornerEl={maxValSelect}
|
||||||
<div className="flex gap-2">
|
|
||||||
{maxValSelect}
|
|
||||||
<NetworkSheet chartData={chartData} dataEmpty={dataEmpty} grid={grid} maxValues={maxValues} />
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
description={t`Network traffic of public interfaces`}
|
description={t`Network traffic of public interfaces`}
|
||||||
>
|
>
|
||||||
<AreaChartDefault
|
<AreaChartDefault
|
||||||
@@ -636,7 +600,7 @@ export default memo(function SystemDetail({ name }: { name: string }) {
|
|||||||
{
|
{
|
||||||
label: t`Sent`,
|
label: t`Sent`,
|
||||||
// use bytes if available, otherwise multiply old MB (can remove in future)
|
// use bytes if available, otherwise multiply old MB (can remove in future)
|
||||||
dataKey(data: SystemStatsRecord) {
|
dataKey(data) {
|
||||||
if (showMax) {
|
if (showMax) {
|
||||||
return data?.stats?.bm?.[0] ?? (data?.stats?.nsm ?? 0) * 1024 * 1024
|
return data?.stats?.bm?.[0] ?? (data?.stats?.nsm ?? 0) * 1024 * 1024
|
||||||
}
|
}
|
||||||
@@ -647,7 +611,7 @@ export default memo(function SystemDetail({ name }: { name: string }) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t`Received`,
|
label: t`Received`,
|
||||||
dataKey(data: SystemStatsRecord) {
|
dataKey(data) {
|
||||||
if (showMax) {
|
if (showMax) {
|
||||||
return data?.stats?.bm?.[1] ?? (data?.stats?.nrm ?? 0) * 1024 * 1024
|
return data?.stats?.bm?.[1] ?? (data?.stats?.nrm ?? 0) * 1024 * 1024
|
||||||
}
|
}
|
||||||
@@ -656,9 +620,7 @@ export default memo(function SystemDetail({ name }: { name: string }) {
|
|||||||
color: 2,
|
color: 2,
|
||||||
opacity: 0.2,
|
opacity: 0.2,
|
||||||
},
|
},
|
||||||
]
|
]}
|
||||||
// try to place the lesser number in front for better visibility
|
|
||||||
.sort(() => (systemStats.at(-1)?.stats.b?.[1] ?? 0) - (systemStats.at(-1)?.stats.b?.[0] ?? 0))}
|
|
||||||
tickFormatter={(val) => {
|
tickFormatter={(val) => {
|
||||||
const { value, unit } = formatBytes(val, true, userSettings.unitNet, false)
|
const { value, unit } = formatBytes(val, true, userSettings.unitNet, false)
|
||||||
return `${toFixedFloat(value, value >= 10 ? 0 : 1)} ${unit}`
|
return `${toFixedFloat(value, value >= 10 ? 0 : 1)} ${unit}`
|
||||||
@@ -712,7 +674,6 @@ export default memo(function SystemDetail({ name }: { name: string }) {
|
|||||||
grid={grid}
|
grid={grid}
|
||||||
title={t`Load Average`}
|
title={t`Load Average`}
|
||||||
description={t`System load averages over time`}
|
description={t`System load averages over time`}
|
||||||
legend={true}
|
|
||||||
>
|
>
|
||||||
<LoadAverageChart chartData={chartData} />
|
<LoadAverageChart chartData={chartData} />
|
||||||
</ChartCard>
|
</ChartCard>
|
||||||
@@ -726,7 +687,6 @@ export default memo(function SystemDetail({ name }: { name: string }) {
|
|||||||
title={t`Temperature`}
|
title={t`Temperature`}
|
||||||
description={t`Temperatures of system sensors`}
|
description={t`Temperatures of system sensors`}
|
||||||
cornerEl={<FilterBar store={$temperatureFilter} />}
|
cornerEl={<FilterBar store={$temperatureFilter} />}
|
||||||
legend={Object.keys(systemStats.at(-1)?.stats.t ?? {}).length < 12}
|
|
||||||
>
|
>
|
||||||
<TemperatureChart chartData={chartData} />
|
<TemperatureChart chartData={chartData} />
|
||||||
</ChartCard>
|
</ChartCard>
|
||||||
@@ -761,12 +721,6 @@ export default memo(function SystemDetail({ name }: { name: string }) {
|
|||||||
</ChartCard>
|
</ChartCard>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Non-power GPU charts */}
|
|
||||||
{hasGpuData && (
|
|
||||||
<div className="grid xl:grid-cols-2 gap-4">
|
|
||||||
{/* GPU power draw chart */}
|
{/* GPU power draw chart */}
|
||||||
{hasGpuPowerData && (
|
{hasGpuPowerData && (
|
||||||
<ChartCard
|
<ChartCard
|
||||||
@@ -778,16 +732,11 @@ export default memo(function SystemDetail({ name }: { name: string }) {
|
|||||||
<GpuPowerChart chartData={chartData} />
|
<GpuPowerChart chartData={chartData} />
|
||||||
</ChartCard>
|
</ChartCard>
|
||||||
)}
|
)}
|
||||||
{hasGpuEnginesData && (
|
</div>
|
||||||
<ChartCard
|
|
||||||
empty={dataEmpty}
|
{/* GPU charts */}
|
||||||
grid={grid}
|
{hasGpuData && (
|
||||||
title={t`GPU Engines`}
|
<div className="grid xl:grid-cols-2 gap-4">
|
||||||
description={t`Average utilization of GPU engines`}
|
|
||||||
>
|
|
||||||
<GpuEnginesChart chartData={chartData} />
|
|
||||||
</ChartCard>
|
|
||||||
)}
|
|
||||||
{Object.keys(systemStats.at(-1)?.stats.g ?? {}).map((id) => {
|
{Object.keys(systemStats.at(-1)?.stats.g ?? {}).map((id) => {
|
||||||
const gpu = systemStats.at(-1)?.stats.g?.[id] as GPUData
|
const gpu = systemStats.at(-1)?.stats.g?.[id] as GPUData
|
||||||
return (
|
return (
|
||||||
@@ -812,8 +761,6 @@ export default memo(function SystemDetail({ name }: { name: string }) {
|
|||||||
contentFormatter={({ value }) => `${decimalString(value)}%`}
|
contentFormatter={({ value }) => `${decimalString(value)}%`}
|
||||||
/>
|
/>
|
||||||
</ChartCard>
|
</ChartCard>
|
||||||
|
|
||||||
{(gpu.mt ?? 0) > 0 && (
|
|
||||||
<ChartCard
|
<ChartCard
|
||||||
empty={dataEmpty}
|
empty={dataEmpty}
|
||||||
grid={grid}
|
grid={grid}
|
||||||
@@ -841,9 +788,7 @@ export default memo(function SystemDetail({ name }: { name: string }) {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</ChartCard>
|
</ChartCard>
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
@@ -914,22 +859,6 @@ export default memo(function SystemDetail({ name }: { name: string }) {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
function GpuEnginesChart({ chartData }: { chartData: ChartData }) {
|
|
||||||
const dataPoints = []
|
|
||||||
const engines = Object.keys(chartData.systemStats?.at(-1)?.stats.g?.[0]?.e ?? {}).sort()
|
|
||||||
for (const engine of engines) {
|
|
||||||
dataPoints.push({
|
|
||||||
label: engine,
|
|
||||||
dataKey: ({ stats }: SystemStatsRecord) => stats?.g?.[0]?.e?.[engine] ?? 0,
|
|
||||||
color: `hsl(${140 + ((engines.indexOf(engine) * 360) / engines.length) % 360}, 65%, 52%)`,
|
|
||||||
opacity: 0.35,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<LineChartDefault legend={true} chartData={chartData} dataPoints={dataPoints} tickFormatter={(val) => `${toFixedFloat(val, 2)}%`} contentFormatter={({ value }) => `${decimalString(value)}%`} />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function FilterBar({ store = $containerFilter }: { store?: typeof $containerFilter }) {
|
function FilterBar({ store = $containerFilter }: { store?: typeof $containerFilter }) {
|
||||||
const containerFilter = useStore(store)
|
const containerFilter = useStore(store)
|
||||||
const { t } = useLingui()
|
const { t } = useLingui()
|
||||||
@@ -950,7 +879,7 @@ function FilterBar({ store = $containerFilter }: { store?: typeof $containerFilt
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Input placeholder={t`Filter...`} className="ps-4 pe-8 w-full sm:w-44" onChange={handleChange} ref={inputRef} />
|
<Input placeholder={t`Filter...`} className="ps-4 pe-8" onChange={handleChange} ref={inputRef} />
|
||||||
{containerFilter && (
|
{containerFilter && (
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -976,7 +905,7 @@ const SelectAvgMax = memo(({ max }: { max: boolean }) => {
|
|||||||
const Icon = max ? ChartMax : ChartAverage
|
const Icon = max ? ChartMax : ChartAverage
|
||||||
return (
|
return (
|
||||||
<Select value={max ? "max" : "avg"} onValueChange={(e) => $maxValues.set(e === "max")}>
|
<Select value={max ? "max" : "avg"} onValueChange={(e) => $maxValues.set(e === "max")}>
|
||||||
<SelectTrigger className="relative ps-10 pe-5 w-full sm:w-44">
|
<SelectTrigger className="relative ps-10 pe-5">
|
||||||
<Icon className="h-4 w-4 absolute start-4 top-1/2 -translate-y-1/2 opacity-85" />
|
<Icon className="h-4 w-4 absolute start-4 top-1/2 -translate-y-1/2 opacity-85" />
|
||||||
<SelectValue />
|
<SelectValue />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
@@ -992,15 +921,13 @@ const SelectAvgMax = memo(({ max }: { max: boolean }) => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
export function ChartCard({
|
function ChartCard({
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
children,
|
children,
|
||||||
grid,
|
grid,
|
||||||
empty,
|
empty,
|
||||||
cornerEl,
|
cornerEl,
|
||||||
legend,
|
|
||||||
className,
|
|
||||||
}: {
|
}: {
|
||||||
title: string
|
title: string
|
||||||
description: string
|
description: string
|
||||||
@@ -1008,22 +935,17 @@ export function ChartCard({
|
|||||||
grid?: boolean
|
grid?: boolean
|
||||||
empty?: boolean
|
empty?: boolean
|
||||||
cornerEl?: JSX.Element | null
|
cornerEl?: JSX.Element | null
|
||||||
legend?: boolean
|
|
||||||
className?: string
|
|
||||||
}) {
|
}) {
|
||||||
const { isIntersecting, ref } = useIntersectionObserver()
|
const { isIntersecting, ref } = useIntersectionObserver()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card className={cn("pb-2 sm:pb-4 odd:last-of-type:col-span-full", { "col-span-full": !grid })} ref={ref}>
|
||||||
className={cn("pb-2 sm:pb-4 odd:last-of-type:col-span-full min-h-full", { "col-span-full": !grid }, className)}
|
|
||||||
ref={ref}
|
|
||||||
>
|
|
||||||
<CardHeader className="pb-5 pt-4 gap-1 relative max-sm:py-3 max-sm:px-4">
|
<CardHeader className="pb-5 pt-4 gap-1 relative max-sm:py-3 max-sm:px-4">
|
||||||
<CardTitle className="text-xl sm:text-2xl">{title}</CardTitle>
|
<CardTitle className="text-xl sm:text-2xl">{title}</CardTitle>
|
||||||
<CardDescription>{description}</CardDescription>
|
<CardDescription>{description}</CardDescription>
|
||||||
{cornerEl && <div className="py-1 grid sm:justify-end sm:absolute sm:top-3.5 sm:end-3.5">{cornerEl}</div>}
|
{cornerEl && <div className="relative py-1 block sm:w-44 sm:absolute sm:top-3.5 sm:end-3.5">{cornerEl}</div>}
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<div className={cn("ps-0 w-[calc(100%-1.5em)] relative group", legend ? "h-54 md:h-56" : "h-48 md:h-52")}>
|
<div className="ps-0 w-[calc(100%-1.5em)] h-48 md:h-52 relative group">
|
||||||
{
|
{
|
||||||
<Spinner
|
<Spinner
|
||||||
msg={empty ? t`Waiting for enough records to display` : undefined}
|
msg={empty ? t`Waiting for enough records to display` : undefined}
|
||||||
|
|||||||
@@ -1,154 +0,0 @@
|
|||||||
import { t } from "@lingui/core/macro"
|
|
||||||
import { useStore } from "@nanostores/react"
|
|
||||||
import { MoreHorizontalIcon } from "lucide-react"
|
|
||||||
import { memo, useRef, useState } from "react"
|
|
||||||
import AreaChartDefault from "@/components/charts/area-chart"
|
|
||||||
import ChartTimeSelect from "@/components/charts/chart-time-select"
|
|
||||||
import { useNetworkInterfaces } from "@/components/charts/hooks"
|
|
||||||
import { Button } from "@/components/ui/button"
|
|
||||||
import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet"
|
|
||||||
import { $userSettings } from "@/lib/stores"
|
|
||||||
import { decimalString, formatBytes, toFixedFloat } from "@/lib/utils"
|
|
||||||
import type { ChartData } from "@/types"
|
|
||||||
import { ChartCard } from "../system"
|
|
||||||
|
|
||||||
export default memo(function NetworkSheet({
|
|
||||||
chartData,
|
|
||||||
dataEmpty,
|
|
||||||
grid,
|
|
||||||
maxValues,
|
|
||||||
}: {
|
|
||||||
chartData: ChartData
|
|
||||||
dataEmpty: boolean
|
|
||||||
grid: boolean
|
|
||||||
maxValues: boolean
|
|
||||||
}) {
|
|
||||||
const [netInterfacesOpen, setNetInterfacesOpen] = useState(false)
|
|
||||||
const userSettings = useStore($userSettings)
|
|
||||||
const netInterfaces = useNetworkInterfaces(chartData.systemStats.at(-1)?.stats?.ni ?? {})
|
|
||||||
const showNetLegend = netInterfaces.length > 0
|
|
||||||
const hasOpened = useRef(false)
|
|
||||||
|
|
||||||
if (netInterfacesOpen && !hasOpened.current) {
|
|
||||||
hasOpened.current = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!netInterfaces.length) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Sheet open={netInterfacesOpen} onOpenChange={setNetInterfacesOpen}>
|
|
||||||
<SheetTrigger asChild>
|
|
||||||
<Button
|
|
||||||
aria-label={t`View more`}
|
|
||||||
variant="outline"
|
|
||||||
size="icon"
|
|
||||||
className="shrink-0 max-sm:absolute max-sm:top-3 max-sm:end-3"
|
|
||||||
>
|
|
||||||
<MoreHorizontalIcon />
|
|
||||||
</Button>
|
|
||||||
</SheetTrigger>
|
|
||||||
{hasOpened.current && (
|
|
||||||
<SheetContent className="overflow-auto w-200 !max-w-full p-4 sm:p-6">
|
|
||||||
<ChartTimeSelect className="w-[calc(100%-2em)]" />
|
|
||||||
<ChartCard
|
|
||||||
empty={dataEmpty}
|
|
||||||
grid={grid}
|
|
||||||
title={t`Download`}
|
|
||||||
description={t`Network traffic of public interfaces`}
|
|
||||||
legend={showNetLegend}
|
|
||||||
className="min-h-auto"
|
|
||||||
>
|
|
||||||
<AreaChartDefault
|
|
||||||
chartData={chartData}
|
|
||||||
maxToggled={maxValues}
|
|
||||||
itemSorter={(a, b) => b.value - a.value}
|
|
||||||
dataPoints={netInterfaces.data(1)}
|
|
||||||
legend={showNetLegend}
|
|
||||||
tickFormatter={(val) => {
|
|
||||||
const { value, unit } = formatBytes(val, true, userSettings.unitNet, false)
|
|
||||||
return `${toFixedFloat(value, value >= 10 ? 0 : 1)} ${unit}`
|
|
||||||
}}
|
|
||||||
contentFormatter={({ value }) => {
|
|
||||||
const { value: convertedValue, unit } = formatBytes(value, true, userSettings.unitNet, false)
|
|
||||||
return `${decimalString(convertedValue, convertedValue >= 100 ? 1 : 2)} ${unit}`
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</ChartCard>
|
|
||||||
|
|
||||||
<ChartCard
|
|
||||||
empty={dataEmpty}
|
|
||||||
grid={grid}
|
|
||||||
title={t`Upload`}
|
|
||||||
description={t`Network traffic of public interfaces`}
|
|
||||||
legend={showNetLegend}
|
|
||||||
className="min-h-auto"
|
|
||||||
>
|
|
||||||
<AreaChartDefault
|
|
||||||
chartData={chartData}
|
|
||||||
maxToggled={maxValues}
|
|
||||||
itemSorter={(a, b) => b.value - a.value}
|
|
||||||
legend={showNetLegend}
|
|
||||||
dataPoints={netInterfaces.data(0)}
|
|
||||||
tickFormatter={(val) => {
|
|
||||||
const { value, unit } = formatBytes(val, true, userSettings.unitNet, false)
|
|
||||||
return `${toFixedFloat(value, value >= 10 ? 0 : 1)} ${unit}`
|
|
||||||
}}
|
|
||||||
contentFormatter={({ value }) => {
|
|
||||||
const { value: convertedValue, unit } = formatBytes(value, true, userSettings.unitNet, false)
|
|
||||||
return `${decimalString(convertedValue, convertedValue >= 100 ? 1 : 2)} ${unit}`
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</ChartCard>
|
|
||||||
|
|
||||||
<ChartCard
|
|
||||||
empty={dataEmpty}
|
|
||||||
grid={grid}
|
|
||||||
title={t`Cumulative Download`}
|
|
||||||
description={t`Total data received for each interface`}
|
|
||||||
legend={showNetLegend}
|
|
||||||
className="min-h-auto"
|
|
||||||
>
|
|
||||||
<AreaChartDefault
|
|
||||||
chartData={chartData}
|
|
||||||
legend={showNetLegend}
|
|
||||||
dataPoints={netInterfaces.data(3)}
|
|
||||||
tickFormatter={(val) => {
|
|
||||||
const { value, unit } = formatBytes(val, false, userSettings.unitNet, false)
|
|
||||||
return `${toFixedFloat(value, value >= 10 ? 0 : 1)} ${unit}`
|
|
||||||
}}
|
|
||||||
contentFormatter={({ value }) => {
|
|
||||||
const { value: convertedValue, unit } = formatBytes(value, false, userSettings.unitNet, false)
|
|
||||||
return `${decimalString(convertedValue, convertedValue >= 100 ? 1 : 2)} ${unit}`
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</ChartCard>
|
|
||||||
|
|
||||||
<ChartCard
|
|
||||||
empty={dataEmpty}
|
|
||||||
grid={grid}
|
|
||||||
title={t`Cumulative Upload`}
|
|
||||||
description={t`Total data sent for each interface`}
|
|
||||||
legend={showNetLegend}
|
|
||||||
className="min-h-auto"
|
|
||||||
>
|
|
||||||
<AreaChartDefault
|
|
||||||
chartData={chartData}
|
|
||||||
legend={showNetLegend}
|
|
||||||
dataPoints={netInterfaces.data(2)}
|
|
||||||
tickFormatter={(val) => {
|
|
||||||
const { value, unit } = formatBytes(val, false, userSettings.unitNet, false)
|
|
||||||
return `${toFixedFloat(value, value >= 10 ? 0 : 1)} ${unit}`
|
|
||||||
}}
|
|
||||||
contentFormatter={({ value }) => {
|
|
||||||
const { value: convertedValue, unit } = formatBytes(value, false, userSettings.unitNet, false)
|
|
||||||
return `${decimalString(convertedValue, convertedValue >= 100 ? 1 : 2)} ${unit}`
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</ChartCard>
|
|
||||||
</SheetContent>
|
|
||||||
)}
|
|
||||||
</Sheet>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { LoaderCircleIcon } from "lucide-react"
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
import { LoaderCircleIcon } from "lucide-react"
|
||||||
|
|
||||||
export default function ({ msg, className }: { msg?: string; className?: string }) {
|
export default function ({ msg, className }: { msg?: string; className?: string }) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,12 +1,8 @@
|
|||||||
import { t } from "@lingui/core/macro"
|
import { SystemRecord } from "@/types"
|
||||||
import { Trans, useLingui } from "@lingui/react/macro"
|
import { CellContext, ColumnDef, HeaderContext } from "@tanstack/react-table"
|
||||||
import { useStore } from "@nanostores/react"
|
import { ClassValue } from "clsx"
|
||||||
import { getPagePath } from "@nanostores/router"
|
|
||||||
import type { CellContext, ColumnDef, HeaderContext } from "@tanstack/react-table"
|
|
||||||
import type { ClassValue } from "clsx"
|
|
||||||
import {
|
import {
|
||||||
ArrowUpDownIcon,
|
ArrowUpDownIcon,
|
||||||
ChevronRightSquareIcon,
|
|
||||||
CopyIcon,
|
CopyIcon,
|
||||||
CpuIcon,
|
CpuIcon,
|
||||||
HardDriveIcon,
|
HardDriveIcon,
|
||||||
@@ -19,10 +15,7 @@ import {
|
|||||||
Trash2Icon,
|
Trash2Icon,
|
||||||
WifiIcon,
|
WifiIcon,
|
||||||
} from "lucide-react"
|
} from "lucide-react"
|
||||||
import { memo, useMemo, useRef, useState } from "react"
|
import { Button } from "../ui/button"
|
||||||
import { isReadOnlyUser, pb } from "@/lib/api"
|
|
||||||
import { ConnectionType, MeterState, SystemStatus } from "@/lib/enums"
|
|
||||||
import { $longestSystemNameLen, $userSettings } from "@/lib/stores"
|
|
||||||
import {
|
import {
|
||||||
cn,
|
cn,
|
||||||
copyToClipboard,
|
copyToClipboard,
|
||||||
@@ -32,12 +25,24 @@ import {
|
|||||||
getMeterState,
|
getMeterState,
|
||||||
parseSemVer,
|
parseSemVer,
|
||||||
} from "@/lib/utils"
|
} from "@/lib/utils"
|
||||||
import type { SystemRecord } from "@/types"
|
import { EthernetIcon, GpuIcon, HourglassIcon, ThermometerIcon } from "../ui/icons"
|
||||||
import { SystemDialog } from "../add-system"
|
import { useStore } from "@nanostores/react"
|
||||||
import AlertButton from "../alerts/alert-button"
|
import { $longestSystemNameLen, $userSettings } from "@/lib/stores"
|
||||||
import { $router, Link } from "../router"
|
import { Trans, useLingui } from "@lingui/react/macro"
|
||||||
|
import { useMemo, useRef, useState } from "react"
|
||||||
|
import { memo } from "react"
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuSeparator,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "../ui/dropdown-menu"
|
||||||
|
import AlertButton from "../alerts/alert-button"
|
||||||
|
import { Dialog } from "../ui/dialog"
|
||||||
|
import { SystemDialog } from "../add-system"
|
||||||
|
import { AlertDialog } from "../ui/alert-dialog"
|
||||||
import {
|
import {
|
||||||
AlertDialog,
|
|
||||||
AlertDialogAction,
|
AlertDialogAction,
|
||||||
AlertDialogCancel,
|
AlertDialogCancel,
|
||||||
AlertDialogContent,
|
AlertDialogContent,
|
||||||
@@ -46,16 +51,12 @@ import {
|
|||||||
AlertDialogHeader,
|
AlertDialogHeader,
|
||||||
AlertDialogTitle,
|
AlertDialogTitle,
|
||||||
} from "../ui/alert-dialog"
|
} from "../ui/alert-dialog"
|
||||||
import { Button, buttonVariants } from "../ui/button"
|
import { buttonVariants } from "../ui/button"
|
||||||
import { Dialog } from "../ui/dialog"
|
import { t } from "@lingui/core/macro"
|
||||||
import {
|
import { MeterState, SystemStatus } from "@/lib/enums"
|
||||||
DropdownMenu,
|
import { $router, Link } from "../router"
|
||||||
DropdownMenuContent,
|
import { getPagePath } from "@nanostores/router"
|
||||||
DropdownMenuItem,
|
import { isReadOnlyUser, pb } from "@/lib/api"
|
||||||
DropdownMenuSeparator,
|
|
||||||
DropdownMenuTrigger,
|
|
||||||
} from "../ui/dropdown-menu"
|
|
||||||
import { EthernetIcon, GpuIcon, HourglassIcon, ThermometerIcon, WebSocketIcon } from "../ui/icons"
|
|
||||||
|
|
||||||
const STATUS_COLORS = {
|
const STATUS_COLORS = {
|
||||||
[SystemStatus.Up]: "bg-green-500",
|
[SystemStatus.Up]: "bg-green-500",
|
||||||
@@ -272,24 +273,24 @@ export default function SystemsTableColumns(viewMode: "table" | "grid"): ColumnD
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
const system = info.row.original
|
const system = info.row.original
|
||||||
const color = {
|
|
||||||
"text-green-500": version === globalThis.BESZEL.HUB_VERSION,
|
|
||||||
"text-yellow-500": version !== globalThis.BESZEL.HUB_VERSION,
|
|
||||||
"text-red-500": system.status !== SystemStatus.Up,
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
<div className={cn("flex gap-1.5 items-center md:pe-5 tabular-nums", viewMode === "table" && "ps-0.5")}>
|
<span className={cn("flex gap-1.5 items-center md:pe-5 tabular-nums", viewMode === "table" && "ps-0.5")}>
|
||||||
{system.info.ct === ConnectionType.WebSocket && <WebSocketIcon className={cn("size-3", color)} />}
|
<IndicatorDot
|
||||||
{system.info.ct === ConnectionType.SSH && <ChevronRightSquareIcon className={cn("size-3", color)} />}
|
system={system}
|
||||||
{!system.info.ct && <IndicatorDot system={system} className={cn(color, "bg-current mx-0.5")} />}
|
className={
|
||||||
|
(system.status !== SystemStatus.Up && STATUS_COLORS[SystemStatus.Paused]) ||
|
||||||
|
(version === globalThis.BESZEL.HUB_VERSION && STATUS_COLORS[SystemStatus.Up]) ||
|
||||||
|
STATUS_COLORS[SystemStatus.Pending]
|
||||||
|
}
|
||||||
|
/>
|
||||||
<span className="truncate max-w-14">{info.getValue() as string}</span>
|
<span className="truncate max-w-14">{info.getValue() as string}</span>
|
||||||
</div>
|
</span>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "actions",
|
id: "actions",
|
||||||
// @ts-expect-error
|
// @ts-ignore
|
||||||
name: () => t({ message: "Actions", comment: "Table column" }),
|
name: () => t({ message: "Actions", comment: "Table column" }),
|
||||||
size: 50,
|
size: 50,
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => (
|
||||||
@@ -304,13 +305,12 @@ export default function SystemsTableColumns(viewMode: "table" | "grid"): ColumnD
|
|||||||
|
|
||||||
function sortableHeader(context: HeaderContext<SystemRecord, unknown>) {
|
function sortableHeader(context: HeaderContext<SystemRecord, unknown>) {
|
||||||
const { column } = context
|
const { column } = context
|
||||||
// @ts-expect-error
|
// @ts-ignore
|
||||||
const { Icon, hideSort, name }: { Icon: React.ElementType; name: () => string; hideSort: boolean } = column.columnDef
|
const { Icon, hideSort, name }: { Icon: React.ElementType; name: () => string; hideSort: boolean } = column.columnDef
|
||||||
const isSorted = column.getIsSorted()
|
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
className={cn("h-9 px-3 flex duration-50", isSorted && "bg-accent/70 light:bg-accent text-accent-foreground/90")}
|
className="h-9 px-3 flex"
|
||||||
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
||||||
>
|
>
|
||||||
{Icon && <Icon className="me-2 size-4" />}
|
{Icon && <Icon className="me-2 size-4" />}
|
||||||
@@ -353,7 +353,7 @@ export function IndicatorDot({ system, className }: { system: SystemRecord; clas
|
|||||||
export const ActionsButton = memo(({ system }: { system: SystemRecord }) => {
|
export const ActionsButton = memo(({ system }: { system: SystemRecord }) => {
|
||||||
const [deleteOpen, setDeleteOpen] = useState(false)
|
const [deleteOpen, setDeleteOpen] = useState(false)
|
||||||
const [editOpen, setEditOpen] = useState(false)
|
const [editOpen, setEditOpen] = useState(false)
|
||||||
const editOpened = useRef(false)
|
let editOpened = useRef(false)
|
||||||
const { t } = useLingui()
|
const { t } = useLingui()
|
||||||
const { id, status, host, name } = system
|
const { id, status, host, name } = system
|
||||||
|
|
||||||
|
|||||||
@@ -1,31 +1,17 @@
|
|||||||
import { Trans, useLingui } from "@lingui/react/macro"
|
|
||||||
import { useStore } from "@nanostores/react"
|
|
||||||
import { getPagePath } from "@nanostores/router"
|
|
||||||
import {
|
import {
|
||||||
type ColumnDef,
|
ColumnDef,
|
||||||
type ColumnFiltersState,
|
ColumnFiltersState,
|
||||||
flexRender,
|
|
||||||
getCoreRowModel,
|
|
||||||
getFilteredRowModel,
|
getFilteredRowModel,
|
||||||
|
SortingState,
|
||||||
getSortedRowModel,
|
getSortedRowModel,
|
||||||
type Row,
|
flexRender,
|
||||||
type SortingState,
|
VisibilityState,
|
||||||
type Table as TableType,
|
getCoreRowModel,
|
||||||
useReactTable,
|
useReactTable,
|
||||||
type VisibilityState,
|
Row,
|
||||||
|
Table as TableType,
|
||||||
} from "@tanstack/react-table"
|
} from "@tanstack/react-table"
|
||||||
import { useVirtualizer, type VirtualItem } from "@tanstack/react-virtual"
|
import { TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
|
||||||
import {
|
|
||||||
ArrowDownIcon,
|
|
||||||
ArrowUpDownIcon,
|
|
||||||
ArrowUpIcon,
|
|
||||||
EyeIcon,
|
|
||||||
FilterIcon,
|
|
||||||
LayoutGridIcon,
|
|
||||||
LayoutListIcon,
|
|
||||||
Settings2Icon,
|
|
||||||
} from "lucide-react"
|
|
||||||
import { memo, useEffect, useMemo, useRef, useState } from "react"
|
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
@@ -38,16 +24,30 @@ import {
|
|||||||
DropdownMenuSeparator,
|
DropdownMenuSeparator,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu"
|
} from "@/components/ui/dropdown-menu"
|
||||||
import { Input } from "@/components/ui/input"
|
import { SystemRecord } from "@/types"
|
||||||
import { TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
|
import {
|
||||||
import { SystemStatus } from "@/lib/enums"
|
ArrowUpDownIcon,
|
||||||
import { $downSystems, $pausedSystems, $systems, $upSystems } from "@/lib/stores"
|
LayoutGridIcon,
|
||||||
|
LayoutListIcon,
|
||||||
|
ArrowDownIcon,
|
||||||
|
ArrowUpIcon,
|
||||||
|
Settings2Icon,
|
||||||
|
EyeIcon,
|
||||||
|
FilterIcon,
|
||||||
|
} from "lucide-react"
|
||||||
|
import { memo, useEffect, useMemo, useRef, useState } from "react"
|
||||||
|
import { $pausedSystems, $downSystems, $upSystems, $systems } from "@/lib/stores"
|
||||||
|
import { useStore } from "@nanostores/react"
|
||||||
import { cn, runOnce, useBrowserStorage } from "@/lib/utils"
|
import { cn, runOnce, useBrowserStorage } from "@/lib/utils"
|
||||||
import type { SystemRecord } from "@/types"
|
|
||||||
import AlertButton from "../alerts/alert-button"
|
|
||||||
import { $router, Link } from "../router"
|
import { $router, Link } from "../router"
|
||||||
|
import { useLingui, Trans } from "@lingui/react/macro"
|
||||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../ui/card"
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../ui/card"
|
||||||
|
import { Input } from "@/components/ui/input"
|
||||||
|
import { getPagePath } from "@nanostores/router"
|
||||||
import SystemsTableColumns, { ActionsButton, IndicatorDot } from "./systems-table-columns"
|
import SystemsTableColumns, { ActionsButton, IndicatorDot } from "./systems-table-columns"
|
||||||
|
import AlertButton from "../alerts/alert-button"
|
||||||
|
import { SystemStatus } from "@/lib/enums"
|
||||||
|
import { useVirtualizer, VirtualItem } from "@tanstack/react-virtual"
|
||||||
|
|
||||||
type ViewMode = "table" | "grid"
|
type ViewMode = "table" | "grid"
|
||||||
type StatusFilter = "all" | SystemRecord["status"]
|
type StatusFilter = "all" | SystemRecord["status"]
|
||||||
@@ -309,121 +309,128 @@ export default function SystemsTable() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const AllSystemsTable = memo(
|
const AllSystemsTable = memo(function ({
|
||||||
({ table, rows, colLength }: { table: TableType<SystemRecord>; rows: Row<SystemRecord>[]; colLength: number }) => {
|
table,
|
||||||
// The virtualizer will need a reference to the scrollable container element
|
rows,
|
||||||
const scrollRef = useRef<HTMLDivElement>(null)
|
colLength,
|
||||||
|
}: {
|
||||||
|
table: TableType<SystemRecord>
|
||||||
|
rows: Row<SystemRecord>[]
|
||||||
|
colLength: number
|
||||||
|
}) {
|
||||||
|
// The virtualizer will need a reference to the scrollable container element
|
||||||
|
const scrollRef = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
const virtualizer = useVirtualizer<HTMLDivElement, HTMLTableRowElement>({
|
const virtualizer = useVirtualizer<HTMLDivElement, HTMLTableRowElement>({
|
||||||
count: rows.length,
|
count: rows.length,
|
||||||
estimateSize: () => (rows.length > 10 ? 56 : 60),
|
estimateSize: () => (rows.length > 10 ? 56 : 60),
|
||||||
getScrollElement: () => scrollRef.current,
|
getScrollElement: () => scrollRef.current,
|
||||||
overscan: 5,
|
overscan: 5,
|
||||||
})
|
})
|
||||||
const virtualRows = virtualizer.getVirtualItems()
|
const virtualRows = virtualizer.getVirtualItems()
|
||||||
|
|
||||||
const paddingTop = Math.max(0, virtualRows[0]?.start ?? 0 - virtualizer.options.scrollMargin)
|
const paddingTop = Math.max(0, virtualRows[0]?.start ?? 0 - virtualizer.options.scrollMargin)
|
||||||
const paddingBottom = Math.max(0, virtualizer.getTotalSize() - (virtualRows[virtualRows.length - 1]?.end ?? 0))
|
const paddingBottom = Math.max(0, virtualizer.getTotalSize() - (virtualRows[virtualRows.length - 1]?.end ?? 0))
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={cn(
|
|
||||||
"h-min max-h-[calc(100dvh-17rem)] max-w-full relative overflow-auto border rounded-md",
|
|
||||||
// don't set min height if there are less than 2 rows, do set if we need to display the empty state
|
|
||||||
(!rows.length || rows.length > 2) && "min-h-50"
|
|
||||||
)}
|
|
||||||
ref={scrollRef}
|
|
||||||
>
|
|
||||||
{/* add header height to table size */}
|
|
||||||
<div style={{ height: `${virtualizer.getTotalSize() + 50}px`, paddingTop, paddingBottom }}>
|
|
||||||
<table className="text-sm w-full h-full">
|
|
||||||
<SystemsTableHead table={table} />
|
|
||||||
<TableBody onMouseEnter={preloadSystemDetail}>
|
|
||||||
{rows.length ? (
|
|
||||||
virtualRows.map((virtualRow) => {
|
|
||||||
const row = rows[virtualRow.index] as Row<SystemRecord>
|
|
||||||
return (
|
|
||||||
<SystemTableRow
|
|
||||||
key={row.id}
|
|
||||||
row={row}
|
|
||||||
virtualRow={virtualRow}
|
|
||||||
length={rows.length}
|
|
||||||
colLength={colLength}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
) : (
|
|
||||||
<TableRow>
|
|
||||||
<TableCell colSpan={colLength} className="h-37 text-center pointer-events-none">
|
|
||||||
<Trans>No systems found.</Trans>
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
)}
|
|
||||||
</TableBody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
function SystemsTableHead({ table }: { table: TableType<SystemRecord> }) {
|
|
||||||
const { t } = useLingui()
|
|
||||||
return (
|
return (
|
||||||
<TableHeader className="sticky top-0 z-20 w-full border-b-2">
|
<div
|
||||||
{table.getHeaderGroups().map((headerGroup) => (
|
className={cn(
|
||||||
<tr key={headerGroup.id}>
|
"h-min max-h-[calc(100dvh-17rem)] max-w-full relative overflow-auto border rounded-md",
|
||||||
{headerGroup.headers.map((header) => {
|
// don't set min height if there are less than 2 rows, do set if we need to display the empty state
|
||||||
return (
|
(!rows.length || rows.length > 2) && "min-h-50"
|
||||||
<TableHead className="px-1.5" key={header.id}>
|
)}
|
||||||
{flexRender(header.column.columnDef.header, header.getContext())}
|
ref={scrollRef}
|
||||||
</TableHead>
|
>
|
||||||
)
|
{/* add header height to table size */}
|
||||||
})}
|
<div style={{ height: `${virtualizer.getTotalSize() + 50}px`, paddingTop, paddingBottom }}>
|
||||||
</tr>
|
<table className="text-sm w-full h-full">
|
||||||
))}
|
<SystemsTableHead table={table} colLength={colLength} />
|
||||||
</TableHeader>
|
<TableBody onMouseEnter={preloadSystemDetail}>
|
||||||
|
{rows.length ? (
|
||||||
|
virtualRows.map((virtualRow) => {
|
||||||
|
const row = rows[virtualRow.index] as Row<SystemRecord>
|
||||||
|
return (
|
||||||
|
<SystemTableRow
|
||||||
|
key={row.id}
|
||||||
|
row={row}
|
||||||
|
virtualRow={virtualRow}
|
||||||
|
length={rows.length}
|
||||||
|
colLength={colLength}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
) : (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colSpan={colLength} className="h-37 text-center pointer-events-none">
|
||||||
|
<Trans>No systems found.</Trans>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
function SystemsTableHead({ table, colLength }: { table: TableType<SystemRecord>; colLength: number }) {
|
||||||
|
const { i18n } = useLingui()
|
||||||
|
|
||||||
|
return useMemo(() => {
|
||||||
|
return (
|
||||||
|
<TableHeader className="sticky top-0 z-20 w-full border-b-2">
|
||||||
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
|
<tr key={headerGroup.id}>
|
||||||
|
{headerGroup.headers.map((header) => {
|
||||||
|
return (
|
||||||
|
<TableHead className="px-1.5" key={header.id}>
|
||||||
|
{flexRender(header.column.columnDef.header, header.getContext())}
|
||||||
|
</TableHead>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</TableHeader>
|
||||||
|
)
|
||||||
|
}, [i18n.locale, colLength])
|
||||||
}
|
}
|
||||||
|
|
||||||
const SystemTableRow = memo(
|
const SystemTableRow = memo(function ({
|
||||||
({
|
row,
|
||||||
row,
|
virtualRow,
|
||||||
virtualRow,
|
colLength,
|
||||||
colLength,
|
}: {
|
||||||
}: {
|
row: Row<SystemRecord>
|
||||||
row: Row<SystemRecord>
|
virtualRow: VirtualItem
|
||||||
virtualRow: VirtualItem
|
length: number
|
||||||
length: number
|
colLength: number
|
||||||
colLength: number
|
}) {
|
||||||
}) => {
|
const system = row.original
|
||||||
const system = row.original
|
const { t } = useLingui()
|
||||||
const { t } = useLingui()
|
return useMemo(() => {
|
||||||
return useMemo(() => {
|
return (
|
||||||
return (
|
<TableRow
|
||||||
<TableRow
|
// data-state={row.getIsSelected() && "selected"}
|
||||||
// data-state={row.getIsSelected() && "selected"}
|
className={cn("cursor-pointer transition-opacity relative safari:transform-3d", {
|
||||||
className={cn("cursor-pointer transition-opacity relative safari:transform-3d", {
|
"opacity-50": system.status === SystemStatus.Paused,
|
||||||
"opacity-50": system.status === SystemStatus.Paused,
|
})}
|
||||||
})}
|
>
|
||||||
>
|
{row.getVisibleCells().map((cell) => (
|
||||||
{row.getVisibleCells().map((cell) => (
|
<TableCell
|
||||||
<TableCell
|
key={cell.id}
|
||||||
key={cell.id}
|
style={{
|
||||||
style={{
|
width: cell.column.getSize(),
|
||||||
width: cell.column.getSize(),
|
height: virtualRow.size,
|
||||||
height: virtualRow.size,
|
}}
|
||||||
}}
|
className="py-0"
|
||||||
className="py-0"
|
>
|
||||||
>
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
</TableCell>
|
||||||
</TableCell>
|
))}
|
||||||
))}
|
</TableRow>
|
||||||
</TableRow>
|
)
|
||||||
)
|
}, [system, system.status, colLength, t])
|
||||||
}, [system, system.status, colLength, t])
|
})
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const SystemCard = memo(
|
const SystemCard = memo(
|
||||||
({ row, table, colLength }: { row: Row<SystemRecord>; table: TableType<SystemRecord>; colLength: number }) => {
|
({ row, table, colLength }: { row: Row<SystemRecord>; table: TableType<SystemRecord>; colLength: number }) => {
|
||||||
@@ -464,7 +471,7 @@ const SystemCard = memo(
|
|||||||
if (!column.getIsVisible() || column.id === "system" || column.id === "actions") return null
|
if (!column.getIsVisible() || column.id === "system" || column.id === "actions") return null
|
||||||
const cell = row.getAllCells().find((cell) => cell.column.id === column.id)
|
const cell = row.getAllCells().find((cell) => cell.column.id === column.id)
|
||||||
if (!cell) return null
|
if (!cell) return null
|
||||||
// @ts-expect-error
|
// @ts-ignore
|
||||||
const { Icon, name } = column.columnDef as ColumnDef<SystemRecord, unknown>
|
const { Icon, name } = column.columnDef as ColumnDef<SystemRecord, unknown>
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -130,12 +130,3 @@ export function HourglassIcon(props: SVGProps<SVGSVGElement>) {
|
|||||||
</svg>
|
</svg>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function WebSocketIcon(props: SVGProps<SVGSVGElement>) {
|
|
||||||
return (
|
|
||||||
<svg viewBox="0 0 256 193" {...props} fill="currentColor">
|
|
||||||
<title>WebSocket</title>
|
|
||||||
<path d="M192 145h32V68l-36-35-22 22 26 27zm32 16H113l-26-27 11-11 22 22h45l-44-45 11-11 44 44V88l-21-22 11-11-55-55H0l32 32h65l24 23-34 34-24-23V48H32v31l55 55-23 22 36 36h156z" />
|
|
||||||
</svg>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -53,9 +53,3 @@ export enum HourFormat {
|
|||||||
"12h" = "12h",
|
"12h" = "12h",
|
||||||
"24h" = "24h",
|
"24h" = "24h",
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Connection type */
|
|
||||||
export enum ConnectionType {
|
|
||||||
SSH = 1,
|
|
||||||
WebSocket,
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
/** biome-ignore-all lint/suspicious/noAssignInExpressions: it's fine :) */
|
|
||||||
import type { PreinitializedMapStore } from "nanostores"
|
import type { PreinitializedMapStore } from "nanostores"
|
||||||
import { pb, verifyAuth } from "@/lib/api"
|
import { pb, verifyAuth } from "@/lib/api"
|
||||||
import {
|
import {
|
||||||
@@ -17,10 +16,9 @@ const COLLECTION = pb.collection<SystemRecord>("systems")
|
|||||||
const FIELDS_DEFAULT = "id,name,host,port,info,status"
|
const FIELDS_DEFAULT = "id,name,host,port,info,status"
|
||||||
|
|
||||||
/** Maximum system name length for display purposes */
|
/** Maximum system name length for display purposes */
|
||||||
const MAX_SYSTEM_NAME_LENGTH = 22
|
const MAX_SYSTEM_NAME_LENGTH = 20
|
||||||
|
|
||||||
let initialized = false
|
let initialized = false
|
||||||
// biome-ignore lint/suspicious/noConfusingVoidType: typescript rocks
|
|
||||||
let unsub: (() => void) | undefined | void
|
let unsub: (() => void) | undefined | void
|
||||||
|
|
||||||
/** Initialize the systems manager and set up listeners */
|
/** Initialize the systems manager and set up listeners */
|
||||||
@@ -106,37 +104,20 @@ async function fetchSystems(): Promise<SystemRecord[]> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Makes sure the system has valid info object and throws if not */
|
|
||||||
function validateSystemInfo(system: SystemRecord) {
|
|
||||||
if (!("cpu" in system.info)) {
|
|
||||||
throw new Error(`${system.name} has no CPU info`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Add system to both name and ID stores */
|
/** Add system to both name and ID stores */
|
||||||
export function add(system: SystemRecord) {
|
export function add(system: SystemRecord) {
|
||||||
try {
|
$allSystemsByName.setKey(system.name, system)
|
||||||
validateSystemInfo(system)
|
$allSystemsById.setKey(system.id, system)
|
||||||
$allSystemsByName.setKey(system.name, system)
|
|
||||||
$allSystemsById.setKey(system.id, system)
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Update system in stores */
|
/** Update system in stores */
|
||||||
export function update(system: SystemRecord) {
|
export function update(system: SystemRecord) {
|
||||||
try {
|
// if name changed, make sure old name is removed from the name store
|
||||||
validateSystemInfo(system)
|
const oldName = $allSystemsById.get()[system.id]?.name
|
||||||
// if name changed, make sure old name is removed from the name store
|
if (oldName !== system.name) {
|
||||||
const oldName = $allSystemsById.get()[system.id]?.name
|
$allSystemsByName.setKey(oldName, undefined as any)
|
||||||
if (oldName !== system.name) {
|
|
||||||
$allSystemsByName.setKey(oldName, undefined as unknown as SystemRecord)
|
|
||||||
}
|
|
||||||
add(system)
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error)
|
|
||||||
}
|
}
|
||||||
|
add(system)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Remove system from stores */
|
/** Remove system from stores */
|
||||||
@@ -151,7 +132,7 @@ export function remove(system: SystemRecord) {
|
|||||||
/** Remove system from specific store */
|
/** Remove system from specific store */
|
||||||
function removeFromStore(system: SystemRecord, store: PreinitializedMapStore<Record<string, SystemRecord>>) {
|
function removeFromStore(system: SystemRecord, store: PreinitializedMapStore<Record<string, SystemRecord>>) {
|
||||||
const key = store === $allSystemsByName ? system.name : system.id
|
const key = store === $allSystemsByName ? system.name : system.id
|
||||||
store.setKey(key, undefined as unknown as SystemRecord)
|
store.setKey(key, undefined as any)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Action functions for subscription */
|
/** Action functions for subscription */
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { t } from "@lingui/core/macro"
|
import { t } from "@lingui/core/macro"
|
||||||
import { type ClassValue, clsx } from "clsx"
|
import { type ClassValue, clsx } from "clsx"
|
||||||
import { timeDay, timeHour } from "d3-time"
|
import { timeDay, timeHour } from "d3-time"
|
||||||
import { listenKeys } from "nanostores"
|
|
||||||
import { useEffect, useState } from "react"
|
import { useEffect, useState } from "react"
|
||||||
import { twMerge } from "tailwind-merge"
|
import { twMerge } from "tailwind-merge"
|
||||||
import { prependBasePath } from "@/components/router"
|
import { prependBasePath } from "@/components/router"
|
||||||
@@ -9,6 +8,7 @@ import { toast } from "@/components/ui/use-toast"
|
|||||||
import type { ChartTimeData, FingerprintRecord, SemVer, SystemRecord } from "@/types"
|
import type { ChartTimeData, FingerprintRecord, SemVer, SystemRecord } from "@/types"
|
||||||
import { HourFormat, MeterState, Unit } from "./enums"
|
import { HourFormat, MeterState, Unit } from "./enums"
|
||||||
import { $copyContent, $userSettings } from "./stores"
|
import { $copyContent, $userSettings } from "./stores"
|
||||||
|
import { listenKeys } from "nanostores"
|
||||||
|
|
||||||
export const FAVICON_DEFAULT = "favicon.svg"
|
export const FAVICON_DEFAULT = "favicon.svg"
|
||||||
export const FAVICON_GREEN = "favicon-green.svg"
|
export const FAVICON_GREEN = "favicon-green.svg"
|
||||||
@@ -179,8 +179,8 @@ export function formatTemperature(celsius: number, unit?: Unit): { value: number
|
|||||||
if (!unit) {
|
if (!unit) {
|
||||||
unit = $userSettings.get().unitTemp || Unit.Celsius
|
unit = $userSettings.get().unitTemp || Unit.Celsius
|
||||||
}
|
}
|
||||||
// biome-ignore lint/suspicious/noDoubleEquals: need loose equality check due to form data being strings
|
// need loose equality check due to form data being strings
|
||||||
if (unit == Unit.Fahrenheit) {
|
if (unit === Unit.Fahrenheit) {
|
||||||
return {
|
return {
|
||||||
value: celsius * 1.8 + 32,
|
value: celsius * 1.8 + 32,
|
||||||
unit: "°F",
|
unit: "°F",
|
||||||
@@ -202,8 +202,8 @@ export function formatBytes(
|
|||||||
// Convert MB to bytes if isMegabytes is true
|
// Convert MB to bytes if isMegabytes is true
|
||||||
if (isMegabytes) size *= 1024 * 1024
|
if (isMegabytes) size *= 1024 * 1024
|
||||||
|
|
||||||
// biome-ignore lint/suspicious/noDoubleEquals: need loose equality check due to form data being strings
|
// need loose equality check due to form data being strings
|
||||||
if (unit == Unit.Bits) {
|
if (unit === Unit.Bits) {
|
||||||
const bits = size * 8
|
const bits = size * 8
|
||||||
const suffix = perSecond ? "ps" : ""
|
const suffix = perSecond ? "ps" : ""
|
||||||
if (bits < 1000) return { value: bits, unit: `b${suffix}` }
|
if (bits < 1000) return { value: bits, unit: `b${suffix}` }
|
||||||
|
|||||||
@@ -8,15 +8,15 @@ msgstr ""
|
|||||||
"Language: ar\n"
|
"Language: ar\n"
|
||||||
"Project-Id-Version: beszel\n"
|
"Project-Id-Version: beszel\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"PO-Revision-Date: 2025-08-28 23:21\n"
|
"PO-Revision-Date: 2025-09-17 18:45\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Arabic\n"
|
"Language-Team: Arabic\n"
|
||||||
"Plural-Forms: nplurals=6; plural=(n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5);\n"
|
"Plural-Forms: nplurals=6; plural=(n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5);\n"
|
||||||
"X-Crowdin-Project: beszel\n"
|
"X-Crowdin-Project: beszel\n"
|
||||||
"X-Crowdin-Project-ID: 733311\n"
|
"X-Crowdin-Project-ID: 733311\n"
|
||||||
"X-Crowdin-Language: ar\n"
|
"X-Crowdin-Language: ar\n"
|
||||||
"X-Crowdin-File: /main/beszel/site/src/locales/en/en.po\n"
|
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
|
||||||
"X-Crowdin-File-ID: 16\n"
|
"X-Crowdin-File-ID: 32\n"
|
||||||
|
|
||||||
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -1094,7 +1094,7 @@ msgstr "يتم التفعيل عندما يتجاوز متوسط التحميل
|
|||||||
|
|
||||||
#: src/lib/alerts.ts
|
#: src/lib/alerts.ts
|
||||||
msgid "Triggers when any sensor exceeds a threshold"
|
msgid "Triggers when any sensor exceeds a threshold"
|
||||||
msgstr "يتم التفعيل عندما يتجاوز أي مستشعر عتبة معينة"
|
msgstr "يتم التفعيل عندما <EFBFBD><EFBFBD>تجاوز أي مستشعر عتبة معينة"
|
||||||
|
|
||||||
#: src/lib/alerts.ts
|
#: src/lib/alerts.ts
|
||||||
msgid "Triggers when combined up/down exceeds a threshold"
|
msgid "Triggers when combined up/down exceeds a threshold"
|
||||||
@@ -1233,3 +1233,4 @@ msgstr "تكوين YAML"
|
|||||||
#: src/components/routes/settings/layout.tsx
|
#: src/components/routes/settings/layout.tsx
|
||||||
msgid "Your user settings have been updated."
|
msgid "Your user settings have been updated."
|
||||||
msgstr "تم تحديث إعدادات المستخدم الخاصة بك."
|
msgstr "تم تحديث إعدادات المستخدم الخاصة بك."
|
||||||
|
|
||||||
|
|||||||
@@ -8,15 +8,15 @@ msgstr ""
|
|||||||
"Language: bg\n"
|
"Language: bg\n"
|
||||||
"Project-Id-Version: beszel\n"
|
"Project-Id-Version: beszel\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"PO-Revision-Date: 2025-08-28 23:21\n"
|
"PO-Revision-Date: 2025-09-17 18:45\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Bulgarian\n"
|
"Language-Team: Bulgarian\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
"X-Crowdin-Project: beszel\n"
|
"X-Crowdin-Project: beszel\n"
|
||||||
"X-Crowdin-Project-ID: 733311\n"
|
"X-Crowdin-Project-ID: 733311\n"
|
||||||
"X-Crowdin-Language: bg\n"
|
"X-Crowdin-Language: bg\n"
|
||||||
"X-Crowdin-File: /main/beszel/site/src/locales/en/en.po\n"
|
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
|
||||||
"X-Crowdin-File-ID: 16\n"
|
"X-Crowdin-File-ID: 32\n"
|
||||||
|
|
||||||
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -1233,3 +1233,4 @@ msgstr "YAML конфигурация"
|
|||||||
#: src/components/routes/settings/layout.tsx
|
#: src/components/routes/settings/layout.tsx
|
||||||
msgid "Your user settings have been updated."
|
msgid "Your user settings have been updated."
|
||||||
msgstr "Настройките за потребителя ти са обновени."
|
msgstr "Настройките за потребителя ти са обновени."
|
||||||
|
|
||||||
|
|||||||
@@ -8,15 +8,15 @@ msgstr ""
|
|||||||
"Language: cs\n"
|
"Language: cs\n"
|
||||||
"Project-Id-Version: beszel\n"
|
"Project-Id-Version: beszel\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"PO-Revision-Date: 2025-08-28 23:21\n"
|
"PO-Revision-Date: 2025-09-17 18:45\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Czech\n"
|
"Language-Team: Czech\n"
|
||||||
"Plural-Forms: nplurals=4; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 3;\n"
|
"Plural-Forms: nplurals=4; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 3;\n"
|
||||||
"X-Crowdin-Project: beszel\n"
|
"X-Crowdin-Project: beszel\n"
|
||||||
"X-Crowdin-Project-ID: 733311\n"
|
"X-Crowdin-Project-ID: 733311\n"
|
||||||
"X-Crowdin-Language: cs\n"
|
"X-Crowdin-Language: cs\n"
|
||||||
"X-Crowdin-File: /main/beszel/site/src/locales/en/en.po\n"
|
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
|
||||||
"X-Crowdin-File-ID: 16\n"
|
"X-Crowdin-File-ID: 32\n"
|
||||||
|
|
||||||
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -46,7 +46,7 @@ msgstr "1 hodina"
|
|||||||
#. Load average
|
#. Load average
|
||||||
#: src/components/charts/load-average-chart.tsx
|
#: src/components/charts/load-average-chart.tsx
|
||||||
msgid "1 min"
|
msgid "1 min"
|
||||||
msgstr "1 min"
|
msgstr ""
|
||||||
|
|
||||||
#: src/lib/utils.ts
|
#: src/lib/utils.ts
|
||||||
msgid "1 week"
|
msgid "1 week"
|
||||||
@@ -59,7 +59,7 @@ msgstr "12 hodin"
|
|||||||
#. Load average
|
#. Load average
|
||||||
#: src/components/charts/load-average-chart.tsx
|
#: src/components/charts/load-average-chart.tsx
|
||||||
msgid "15 min"
|
msgid "15 min"
|
||||||
msgstr "15 min"
|
msgstr ""
|
||||||
|
|
||||||
#: src/lib/utils.ts
|
#: src/lib/utils.ts
|
||||||
msgid "24 hours"
|
msgid "24 hours"
|
||||||
@@ -72,7 +72,7 @@ msgstr "30 dní"
|
|||||||
#. Load average
|
#. Load average
|
||||||
#: src/components/charts/load-average-chart.tsx
|
#: src/components/charts/load-average-chart.tsx
|
||||||
msgid "5 min"
|
msgid "5 min"
|
||||||
msgstr "5 min"
|
msgstr ""
|
||||||
|
|
||||||
#. Table column
|
#. Table column
|
||||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||||
@@ -112,11 +112,11 @@ msgstr "Upravit možnosti zobrazení pro grafy."
|
|||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
msgid "Admin"
|
msgid "Admin"
|
||||||
msgstr "Admin"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "Agent"
|
msgid "Agent"
|
||||||
msgstr "Agent"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||||
@@ -197,17 +197,17 @@ msgstr "Beszel používá <0>Shoutrrr</0> k integraci s populárními notifikač
|
|||||||
|
|
||||||
#: src/components/add-system.tsx
|
#: src/components/add-system.tsx
|
||||||
msgid "Binary"
|
msgid "Binary"
|
||||||
msgstr "Binary"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "Bits (Kbps, Mbps, Gbps)"
|
msgid "Bits (Kbps, Mbps, Gbps)"
|
||||||
msgstr "Bits (Kbps, Mbps, Gbps)"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "Bytes (KB/s, MB/s, GB/s)"
|
msgid "Bytes (KB/s, MB/s, GB/s)"
|
||||||
msgstr "Bytes (KB/s, MB/s, GB/s)"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/charts/mem-chart.tsx
|
#: src/components/charts/mem-chart.tsx
|
||||||
msgid "Cache / Buffers"
|
msgid "Cache / Buffers"
|
||||||
@@ -401,11 +401,11 @@ msgstr "Vybíjení"
|
|||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "Disk"
|
msgid "Disk"
|
||||||
msgstr "Disk"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "Disk I/O"
|
msgid "Disk I/O"
|
||||||
msgstr "Disk I/O"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "Disk unit"
|
msgid "Disk unit"
|
||||||
@@ -466,7 +466,7 @@ msgstr "Upravit"
|
|||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
#: src/components/login/otp-forms.tsx
|
#: src/components/login/otp-forms.tsx
|
||||||
msgid "Email"
|
msgid "Email"
|
||||||
msgstr "Email"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/notifications.tsx
|
#: src/components/routes/settings/notifications.tsx
|
||||||
msgid "Email notifications"
|
msgid "Email notifications"
|
||||||
@@ -510,7 +510,7 @@ msgstr "Stávající systémy, které nejsou definovány v <0>config.yml</0>, bu
|
|||||||
|
|
||||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||||
msgid "Export"
|
msgid "Export"
|
||||||
msgstr "Export"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/config-yaml.tsx
|
#: src/components/routes/settings/config-yaml.tsx
|
||||||
msgid "Export configuration"
|
msgid "Export configuration"
|
||||||
@@ -610,7 +610,7 @@ msgstr "Neplatná e-mailová adresa."
|
|||||||
#. Linux kernel
|
#. Linux kernel
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "Kernel"
|
msgid "Kernel"
|
||||||
msgstr "Kernel"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "Language"
|
msgid "Language"
|
||||||
@@ -837,7 +837,7 @@ msgstr "Přihlaste se prosím k vašemu účtu"
|
|||||||
|
|
||||||
#: src/components/add-system.tsx
|
#: src/components/add-system.tsx
|
||||||
msgid "Port"
|
msgid "Port"
|
||||||
msgstr "Port"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -1010,7 +1010,7 @@ msgstr "Teploty systémových senzorů"
|
|||||||
|
|
||||||
#: src/components/routes/settings/notifications.tsx
|
#: src/components/routes/settings/notifications.tsx
|
||||||
msgid "Test <0>URL</0>"
|
msgid "Test <0>URL</0>"
|
||||||
msgstr "Test <0>URL</0>"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/notifications.tsx
|
#: src/components/routes/settings/notifications.tsx
|
||||||
msgid "Test notification sent"
|
msgid "Test notification sent"
|
||||||
@@ -1056,7 +1056,7 @@ msgstr "Přepnout motiv"
|
|||||||
#: src/components/add-system.tsx
|
#: src/components/add-system.tsx
|
||||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||||
msgid "Token"
|
msgid "Token"
|
||||||
msgstr "Token"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/settings/layout.tsx
|
#: src/components/routes/settings/layout.tsx
|
||||||
@@ -1233,3 +1233,4 @@ msgstr "YAML konfigurace"
|
|||||||
#: src/components/routes/settings/layout.tsx
|
#: src/components/routes/settings/layout.tsx
|
||||||
msgid "Your user settings have been updated."
|
msgid "Your user settings have been updated."
|
||||||
msgstr "Vaše uživatelská nastavení byla aktualizována."
|
msgstr "Vaše uživatelská nastavení byla aktualizována."
|
||||||
|
|
||||||
|
|||||||
@@ -8,25 +8,25 @@ msgstr ""
|
|||||||
"Language: da\n"
|
"Language: da\n"
|
||||||
"Project-Id-Version: beszel\n"
|
"Project-Id-Version: beszel\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"PO-Revision-Date: 2025-08-28 23:21\n"
|
"PO-Revision-Date: 2025-09-17 18:45\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Danish\n"
|
"Language-Team: Danish\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
"X-Crowdin-Project: beszel\n"
|
"X-Crowdin-Project: beszel\n"
|
||||||
"X-Crowdin-Project-ID: 733311\n"
|
"X-Crowdin-Project-ID: 733311\n"
|
||||||
"X-Crowdin-Language: da\n"
|
"X-Crowdin-Language: da\n"
|
||||||
"X-Crowdin-File: /main/beszel/site/src/locales/en/en.po\n"
|
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
|
||||||
"X-Crowdin-File-ID: 16\n"
|
"X-Crowdin-File-ID: 32\n"
|
||||||
|
|
||||||
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "{0, plural, one {# day} other {# days}}"
|
msgid "{0, plural, one {# day} other {# days}}"
|
||||||
msgstr "{0, plural, one {# day} other {# days}}"
|
msgstr ""
|
||||||
|
|
||||||
#. placeholder {0}: Math.trunc(system.info.u / 3600)
|
#. placeholder {0}: Math.trunc(system.info.u / 3600)
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "{0, plural, one {# hour} other {# hours}}"
|
msgid "{0, plural, one {# hour} other {# hours}}"
|
||||||
msgstr "{0, plural, one {# hour} other {# hours}}"
|
msgstr ""
|
||||||
|
|
||||||
#. placeholder {0}: Math.trunc(system.info.u / 60)
|
#. placeholder {0}: Math.trunc(system.info.u / 60)
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -112,11 +112,11 @@ msgstr "Juster visningsindstillinger for diagrammer."
|
|||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
msgid "Admin"
|
msgid "Admin"
|
||||||
msgstr "Admin"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "Agent"
|
msgid "Agent"
|
||||||
msgstr "Agent"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||||
@@ -342,7 +342,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "CPU"
|
msgid "CPU"
|
||||||
msgstr "CPU"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -401,11 +401,11 @@ msgstr "Aflader"
|
|||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "Disk"
|
msgid "Disk"
|
||||||
msgstr "Disk"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "Disk I/O"
|
msgid "Disk I/O"
|
||||||
msgstr "Disk I/O"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "Disk unit"
|
msgid "Disk unit"
|
||||||
@@ -451,7 +451,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: src/components/routes/system/network-sheet.tsx
|
#: src/components/routes/system/network-sheet.tsx
|
||||||
msgid "Download"
|
msgid "Download"
|
||||||
msgstr "Download"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/alerts-history-columns.tsx
|
#: src/components/alerts-history-columns.tsx
|
||||||
msgid "Duration"
|
msgid "Duration"
|
||||||
@@ -545,7 +545,7 @@ msgstr "Kunne ikke opdatere alarm"
|
|||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "Filter..."
|
msgid "Filter..."
|
||||||
msgstr "Filter..."
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||||
msgid "Fingerprint"
|
msgid "Fingerprint"
|
||||||
@@ -610,7 +610,7 @@ msgstr "Ugyldig email adresse."
|
|||||||
#. Linux kernel
|
#. Linux kernel
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "Kernel"
|
msgid "Kernel"
|
||||||
msgstr "Kernel"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "Language"
|
msgid "Language"
|
||||||
@@ -618,7 +618,7 @@ msgstr "Sprog"
|
|||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "Layout"
|
msgid "Layout"
|
||||||
msgstr "Layout"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "Load Average"
|
msgid "Load Average"
|
||||||
@@ -657,7 +657,7 @@ msgstr "Loginforsøg mislykkedes"
|
|||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/navbar.tsx
|
#: src/components/navbar.tsx
|
||||||
msgid "Logs"
|
msgid "Logs"
|
||||||
msgstr "Logs"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/notifications.tsx
|
#: src/components/routes/settings/notifications.tsx
|
||||||
msgid "Looking instead for where to create alerts? Click the bell <0/> icons in the systems table."
|
msgid "Looking instead for where to create alerts? Click the bell <0/> icons in the systems table."
|
||||||
@@ -697,7 +697,7 @@ msgstr "Navn"
|
|||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "Net"
|
msgid "Net"
|
||||||
msgstr "Net"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "Network traffic of docker containers"
|
msgid "Network traffic of docker containers"
|
||||||
@@ -792,7 +792,7 @@ msgstr "Anmodning om nulstilling af adgangskode modtaget"
|
|||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "Pause"
|
msgid "Pause"
|
||||||
msgstr "Pause"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "Paused"
|
msgid "Paused"
|
||||||
@@ -837,7 +837,7 @@ msgstr "Log venligst ind på din konto"
|
|||||||
|
|
||||||
#: src/components/add-system.tsx
|
#: src/components/add-system.tsx
|
||||||
msgid "Port"
|
msgid "Port"
|
||||||
msgstr "Port"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -957,7 +957,7 @@ msgstr ""
|
|||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
#: src/lib/alerts.ts
|
#: src/lib/alerts.ts
|
||||||
msgid "Status"
|
msgid "Status"
|
||||||
msgstr "Status"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "Swap space used by the system"
|
msgid "Swap space used by the system"
|
||||||
@@ -972,7 +972,7 @@ msgstr "Swap forbrug"
|
|||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
#: src/lib/alerts.ts
|
#: src/lib/alerts.ts
|
||||||
msgid "System"
|
msgid "System"
|
||||||
msgstr "System"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "System load averages over time"
|
msgid "System load averages over time"
|
||||||
@@ -1010,7 +1010,7 @@ msgstr "Temperaturer i systemsensorer"
|
|||||||
|
|
||||||
#: src/components/routes/settings/notifications.tsx
|
#: src/components/routes/settings/notifications.tsx
|
||||||
msgid "Test <0>URL</0>"
|
msgid "Test <0>URL</0>"
|
||||||
msgstr "Test <0>URL</0>"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/notifications.tsx
|
#: src/components/routes/settings/notifications.tsx
|
||||||
msgid "Test notification sent"
|
msgid "Test notification sent"
|
||||||
@@ -1143,7 +1143,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: src/components/routes/system/network-sheet.tsx
|
#: src/components/routes/system/network-sheet.tsx
|
||||||
msgid "Upload"
|
msgid "Upload"
|
||||||
msgstr "Upload"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "Uptime"
|
msgid "Uptime"
|
||||||
@@ -1233,3 +1233,4 @@ msgstr "YAML Konfiguration"
|
|||||||
#: src/components/routes/settings/layout.tsx
|
#: src/components/routes/settings/layout.tsx
|
||||||
msgid "Your user settings have been updated."
|
msgid "Your user settings have been updated."
|
||||||
msgstr "Dine brugerindstillinger er opdateret."
|
msgstr "Dine brugerindstillinger er opdateret."
|
||||||
|
|
||||||
|
|||||||
@@ -8,15 +8,15 @@ msgstr ""
|
|||||||
"Language: de\n"
|
"Language: de\n"
|
||||||
"Project-Id-Version: beszel\n"
|
"Project-Id-Version: beszel\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"PO-Revision-Date: 2025-08-28 23:21\n"
|
"PO-Revision-Date: 2025-09-17 18:45\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: German\n"
|
"Language-Team: German\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
"X-Crowdin-Project: beszel\n"
|
"X-Crowdin-Project: beszel\n"
|
||||||
"X-Crowdin-Project-ID: 733311\n"
|
"X-Crowdin-Project-ID: 733311\n"
|
||||||
"X-Crowdin-Language: de\n"
|
"X-Crowdin-Language: de\n"
|
||||||
"X-Crowdin-File: /main/beszel/site/src/locales/en/en.po\n"
|
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
|
||||||
"X-Crowdin-File-ID: 16\n"
|
"X-Crowdin-File-ID: 32\n"
|
||||||
|
|
||||||
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -112,11 +112,11 @@ msgstr "Anzeigeoptionen für Diagramme anpassen."
|
|||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
msgid "Admin"
|
msgid "Admin"
|
||||||
msgstr "Admin"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "Agent"
|
msgid "Agent"
|
||||||
msgstr "Agent"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||||
@@ -176,7 +176,7 @@ msgstr "Durchschnittliche Auslastung von {0}"
|
|||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/navbar.tsx
|
#: src/components/navbar.tsx
|
||||||
msgid "Backups"
|
msgid "Backups"
|
||||||
msgstr "Backups"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
#: src/lib/alerts.ts
|
#: src/lib/alerts.ts
|
||||||
@@ -202,12 +202,12 @@ msgstr "Binär"
|
|||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "Bits (Kbps, Mbps, Gbps)"
|
msgid "Bits (Kbps, Mbps, Gbps)"
|
||||||
msgstr "Bits (Kbps, Mbps, Gbps)"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "Bytes (KB/s, MB/s, GB/s)"
|
msgid "Bytes (KB/s, MB/s, GB/s)"
|
||||||
msgstr "Bytes (KB/s, MB/s, GB/s)"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/charts/mem-chart.tsx
|
#: src/components/charts/mem-chart.tsx
|
||||||
msgid "Cache / Buffers"
|
msgid "Cache / Buffers"
|
||||||
@@ -224,7 +224,7 @@ msgstr "Vorsicht - potenzieller Datenverlust"
|
|||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "Celsius (°C)"
|
msgid "Celsius (°C)"
|
||||||
msgstr "Celsius (°C)"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "Change display units for metrics."
|
msgid "Change display units for metrics."
|
||||||
@@ -342,7 +342,7 @@ msgstr "YAML kopieren"
|
|||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "CPU"
|
msgid "CPU"
|
||||||
msgstr "CPU"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -379,7 +379,7 @@ msgstr "Aktueller Zustand"
|
|||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "Dashboard"
|
msgid "Dashboard"
|
||||||
msgstr "Dashboard"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "Default time period"
|
msgid "Default time period"
|
||||||
@@ -451,7 +451,7 @@ msgstr "Offline ({downSystemsLength})"
|
|||||||
|
|
||||||
#: src/components/routes/system/network-sheet.tsx
|
#: src/components/routes/system/network-sheet.tsx
|
||||||
msgid "Download"
|
msgid "Download"
|
||||||
msgstr "Download"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/alerts-history-columns.tsx
|
#: src/components/alerts-history-columns.tsx
|
||||||
msgid "Duration"
|
msgid "Duration"
|
||||||
@@ -522,7 +522,7 @@ msgstr "Exportiere die aktuelle Systemkonfiguration."
|
|||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "Fahrenheit (°F)"
|
msgid "Fahrenheit (°F)"
|
||||||
msgstr "Fahrenheit (°F)"
|
msgstr ""
|
||||||
|
|
||||||
#: src/lib/api.ts
|
#: src/lib/api.ts
|
||||||
msgid "Failed to authenticate"
|
msgid "Failed to authenticate"
|
||||||
@@ -545,7 +545,7 @@ msgstr "Warnung konnte nicht aktualisiert werden"
|
|||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "Filter..."
|
msgid "Filter..."
|
||||||
msgstr "Filter..."
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||||
msgid "Fingerprint"
|
msgid "Fingerprint"
|
||||||
@@ -592,7 +592,7 @@ msgstr "Homebrew-Befehl"
|
|||||||
|
|
||||||
#: src/components/add-system.tsx
|
#: src/components/add-system.tsx
|
||||||
msgid "Host / IP"
|
msgid "Host / IP"
|
||||||
msgstr "Host / IP"
|
msgstr ""
|
||||||
|
|
||||||
#. Context: Battery state
|
#. Context: Battery state
|
||||||
#: src/lib/i18n.ts
|
#: src/lib/i18n.ts
|
||||||
@@ -610,7 +610,7 @@ msgstr "Ungültige E-Mail-Adresse."
|
|||||||
#. Linux kernel
|
#. Linux kernel
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "Kernel"
|
msgid "Kernel"
|
||||||
msgstr "Kernel"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "Language"
|
msgid "Language"
|
||||||
@@ -693,7 +693,7 @@ msgstr "Arbeitsspeichernutzung der Docker-Container"
|
|||||||
#: src/components/add-system.tsx
|
#: src/components/add-system.tsx
|
||||||
#: src/components/alerts-history-columns.tsx
|
#: src/components/alerts-history-columns.tsx
|
||||||
msgid "Name"
|
msgid "Name"
|
||||||
msgstr "Name"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "Net"
|
msgid "Net"
|
||||||
@@ -792,7 +792,7 @@ msgstr "Anfrage zum Zurücksetzen des Passworts erhalten"
|
|||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "Pause"
|
msgid "Pause"
|
||||||
msgstr "Pause"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "Paused"
|
msgid "Paused"
|
||||||
@@ -837,7 +837,7 @@ msgstr "Bitte melde dich bei deinem Konto an"
|
|||||||
|
|
||||||
#: src/components/add-system.tsx
|
#: src/components/add-system.tsx
|
||||||
msgid "Port"
|
msgid "Port"
|
||||||
msgstr "Port"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -957,7 +957,7 @@ msgstr "Status"
|
|||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
#: src/lib/alerts.ts
|
#: src/lib/alerts.ts
|
||||||
msgid "Status"
|
msgid "Status"
|
||||||
msgstr "Status"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "Swap space used by the system"
|
msgid "Swap space used by the system"
|
||||||
@@ -972,7 +972,7 @@ msgstr "Swap-Nutzung"
|
|||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
#: src/lib/alerts.ts
|
#: src/lib/alerts.ts
|
||||||
msgid "System"
|
msgid "System"
|
||||||
msgstr "System"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "System load averages over time"
|
msgid "System load averages over time"
|
||||||
@@ -1010,7 +1010,7 @@ msgstr "Temperaturen der Systemsensoren"
|
|||||||
|
|
||||||
#: src/components/routes/settings/notifications.tsx
|
#: src/components/routes/settings/notifications.tsx
|
||||||
msgid "Test <0>URL</0>"
|
msgid "Test <0>URL</0>"
|
||||||
msgstr "Test <0>URL</0>"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/notifications.tsx
|
#: src/components/routes/settings/notifications.tsx
|
||||||
msgid "Test notification sent"
|
msgid "Test notification sent"
|
||||||
@@ -1056,7 +1056,7 @@ msgstr "Darstellung umschalten"
|
|||||||
#: src/components/add-system.tsx
|
#: src/components/add-system.tsx
|
||||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||||
msgid "Token"
|
msgid "Token"
|
||||||
msgstr "Token"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/settings/layout.tsx
|
#: src/components/routes/settings/layout.tsx
|
||||||
@@ -1143,7 +1143,7 @@ msgstr "aktiv ({upSystemsLength})"
|
|||||||
|
|
||||||
#: src/components/routes/system/network-sheet.tsx
|
#: src/components/routes/system/network-sheet.tsx
|
||||||
msgid "Upload"
|
msgid "Upload"
|
||||||
msgstr "Upload"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "Uptime"
|
msgid "Uptime"
|
||||||
@@ -1233,3 +1233,4 @@ msgstr "YAML-Konfiguration"
|
|||||||
#: src/components/routes/settings/layout.tsx
|
#: src/components/routes/settings/layout.tsx
|
||||||
msgid "Your user settings have been updated."
|
msgid "Your user settings have been updated."
|
||||||
msgstr "Deine Benutzereinstellungen wurden aktualisiert."
|
msgstr "Deine Benutzereinstellungen wurden aktualisiert."
|
||||||
|
|
||||||
|
|||||||
1236
internal/site/src/locales/el/el.po
Normal file
1236
internal/site/src/locales/el/el.po
Normal file
File diff suppressed because it is too large
Load Diff
@@ -358,14 +358,6 @@ msgstr "Created"
|
|||||||
msgid "Critical (%)"
|
msgid "Critical (%)"
|
||||||
msgstr "Critical (%)"
|
msgstr "Critical (%)"
|
||||||
|
|
||||||
#: src/components/routes/system/network-sheet.tsx
|
|
||||||
msgid "Cumulative Download"
|
|
||||||
msgstr "Cumulative Download"
|
|
||||||
|
|
||||||
#: src/components/routes/system/network-sheet.tsx
|
|
||||||
msgid "Cumulative Upload"
|
|
||||||
msgstr "Cumulative Upload"
|
|
||||||
|
|
||||||
#. Context: Battery state
|
#. Context: Battery state
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "Current state"
|
msgid "Current state"
|
||||||
@@ -444,10 +436,6 @@ msgstr "Down"
|
|||||||
msgid "Down ({downSystemsLength})"
|
msgid "Down ({downSystemsLength})"
|
||||||
msgstr "Down ({downSystemsLength})"
|
msgstr "Down ({downSystemsLength})"
|
||||||
|
|
||||||
#: src/components/routes/system/network-sheet.tsx
|
|
||||||
msgid "Download"
|
|
||||||
msgstr "Download"
|
|
||||||
|
|
||||||
#: src/components/alerts-history-columns.tsx
|
#: src/components/alerts-history-columns.tsx
|
||||||
msgid "Duration"
|
msgid "Duration"
|
||||||
msgstr "Duration"
|
msgstr "Duration"
|
||||||
@@ -459,7 +447,6 @@ msgstr "Edit"
|
|||||||
|
|
||||||
#: src/components/login/auth-form.tsx
|
#: src/components/login/auth-form.tsx
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
#: src/components/login/otp-forms.tsx
|
|
||||||
msgid "Email"
|
msgid "Email"
|
||||||
msgstr "Email"
|
msgstr "Email"
|
||||||
|
|
||||||
@@ -480,10 +467,6 @@ msgstr "Enter email address to reset password"
|
|||||||
msgid "Enter email address..."
|
msgid "Enter email address..."
|
||||||
msgstr "Enter email address..."
|
msgstr "Enter email address..."
|
||||||
|
|
||||||
#: src/components/login/otp-forms.tsx
|
|
||||||
msgid "Enter your one-time password."
|
|
||||||
msgstr "Enter your one-time password."
|
|
||||||
|
|
||||||
#: src/components/login/auth-form.tsx
|
#: src/components/login/auth-form.tsx
|
||||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||||
#: src/components/routes/settings/config-yaml.tsx
|
#: src/components/routes/settings/config-yaml.tsx
|
||||||
@@ -554,12 +537,6 @@ msgstr "For <0>{min}</0> {min, plural, one {minute} other {minutes}}"
|
|||||||
msgid "Forgot password?"
|
msgid "Forgot password?"
|
||||||
msgstr "Forgot password?"
|
msgstr "Forgot password?"
|
||||||
|
|
||||||
#: src/components/add-system.tsx
|
|
||||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
|
||||||
msgctxt "Button to copy install command"
|
|
||||||
msgid "FreeBSD command"
|
|
||||||
msgstr "FreeBSD command"
|
|
||||||
|
|
||||||
#. Context: Battery state
|
#. Context: Battery state
|
||||||
#: src/lib/i18n.ts
|
#: src/lib/i18n.ts
|
||||||
msgid "Full"
|
msgid "Full"
|
||||||
@@ -663,7 +640,6 @@ msgid "Manage display and notification preferences."
|
|||||||
msgstr "Manage display and notification preferences."
|
msgstr "Manage display and notification preferences."
|
||||||
|
|
||||||
#: src/components/add-system.tsx
|
#: src/components/add-system.tsx
|
||||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
|
||||||
msgid "Manual setup instructions"
|
msgid "Manual setup instructions"
|
||||||
msgstr "Manual setup instructions"
|
msgstr "Manual setup instructions"
|
||||||
|
|
||||||
@@ -699,8 +675,6 @@ msgid "Network traffic of docker containers"
|
|||||||
msgstr "Network traffic of docker containers"
|
msgstr "Network traffic of docker containers"
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
#: src/components/routes/system/network-sheet.tsx
|
|
||||||
#: src/components/routes/system/network-sheet.tsx
|
|
||||||
msgid "Network traffic of public interfaces"
|
msgid "Network traffic of public interfaces"
|
||||||
msgstr "Network traffic of public interfaces"
|
msgstr "Network traffic of public interfaces"
|
||||||
|
|
||||||
@@ -736,10 +710,6 @@ msgstr "OAuth 2 / OIDC support"
|
|||||||
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
|
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
|
||||||
msgstr "On each restart, systems in the database will be updated to match the systems defined in the file."
|
msgstr "On each restart, systems in the database will be updated to match the systems defined in the file."
|
||||||
|
|
||||||
#: src/components/login/auth-form.tsx
|
|
||||||
msgid "One-time password"
|
|
||||||
msgstr "One-time password"
|
|
||||||
|
|
||||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
@@ -858,14 +828,6 @@ msgstr "Read"
|
|||||||
msgid "Received"
|
msgid "Received"
|
||||||
msgstr "Received"
|
msgstr "Received"
|
||||||
|
|
||||||
#: src/components/login/login.tsx
|
|
||||||
msgid "Request a one-time password"
|
|
||||||
msgstr "Request a one-time password"
|
|
||||||
|
|
||||||
#: src/components/login/otp-forms.tsx
|
|
||||||
msgid "Request OTP"
|
|
||||||
msgstr "Request OTP"
|
|
||||||
|
|
||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
msgid "Reset Password"
|
msgid "Reset Password"
|
||||||
msgstr "Reset Password"
|
msgstr "Reset Password"
|
||||||
@@ -921,6 +883,10 @@ msgstr "Sent"
|
|||||||
msgid "Set percentage thresholds for meter colors."
|
msgid "Set percentage thresholds for meter colors."
|
||||||
msgstr "Set percentage thresholds for meter colors."
|
msgstr "Set percentage thresholds for meter colors."
|
||||||
|
|
||||||
|
#: src/components/routes/settings/general.tsx
|
||||||
|
msgid "Sets the default time range for charts when a system is viewed."
|
||||||
|
msgstr "Sets the default time range for charts when a system is viewed."
|
||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/settings/layout.tsx
|
#: src/components/routes/settings/layout.tsx
|
||||||
@@ -1031,10 +997,6 @@ msgstr "Throughput of {extraFsName}"
|
|||||||
msgid "Throughput of root filesystem"
|
msgid "Throughput of root filesystem"
|
||||||
msgstr "Throughput of root filesystem"
|
msgstr "Throughput of root filesystem"
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
|
||||||
msgid "Time format"
|
|
||||||
msgstr "Time format"
|
|
||||||
|
|
||||||
#: src/components/routes/settings/notifications.tsx
|
#: src/components/routes/settings/notifications.tsx
|
||||||
msgid "To email(s)"
|
msgid "To email(s)"
|
||||||
msgstr "To email(s)"
|
msgstr "To email(s)"
|
||||||
@@ -1067,14 +1029,6 @@ msgstr "Tokens allow agents to connect and register. Fingerprints are stable ide
|
|||||||
msgid "Tokens and fingerprints are used to authenticate WebSocket connections to the hub."
|
msgid "Tokens and fingerprints are used to authenticate WebSocket connections to the hub."
|
||||||
msgstr "Tokens and fingerprints are used to authenticate WebSocket connections to the hub."
|
msgstr "Tokens and fingerprints are used to authenticate WebSocket connections to the hub."
|
||||||
|
|
||||||
#: src/components/routes/system/network-sheet.tsx
|
|
||||||
msgid "Total data received for each interface"
|
|
||||||
msgstr "Total data received for each interface"
|
|
||||||
|
|
||||||
#: src/components/routes/system/network-sheet.tsx
|
|
||||||
msgid "Total data sent for each interface"
|
|
||||||
msgstr "Total data sent for each interface"
|
|
||||||
|
|
||||||
#: src/lib/alerts.ts
|
#: src/lib/alerts.ts
|
||||||
msgid "Triggers when 1 minute load average exceeds a threshold"
|
msgid "Triggers when 1 minute load average exceeds a threshold"
|
||||||
msgstr "Triggers when 1 minute load average exceeds a threshold"
|
msgstr "Triggers when 1 minute load average exceeds a threshold"
|
||||||
@@ -1136,10 +1090,6 @@ msgstr "Up"
|
|||||||
msgid "Up ({upSystemsLength})"
|
msgid "Up ({upSystemsLength})"
|
||||||
msgstr "Up ({upSystemsLength})"
|
msgstr "Up ({upSystemsLength})"
|
||||||
|
|
||||||
#: src/components/routes/system/network-sheet.tsx
|
|
||||||
msgid "Upload"
|
|
||||||
msgstr "Upload"
|
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "Uptime"
|
msgid "Uptime"
|
||||||
msgstr "Uptime"
|
msgstr "Uptime"
|
||||||
|
|||||||
@@ -8,15 +8,15 @@ msgstr ""
|
|||||||
"Language: es\n"
|
"Language: es\n"
|
||||||
"Project-Id-Version: beszel\n"
|
"Project-Id-Version: beszel\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"PO-Revision-Date: 2025-08-28 23:21\n"
|
"PO-Revision-Date: 2025-09-17 18:45\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Spanish\n"
|
"Language-Team: Spanish\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
"X-Crowdin-Project: beszel\n"
|
"X-Crowdin-Project: beszel\n"
|
||||||
"X-Crowdin-Project-ID: 733311\n"
|
"X-Crowdin-Project-ID: 733311\n"
|
||||||
"X-Crowdin-Language: es-ES\n"
|
"X-Crowdin-Language: es-ES\n"
|
||||||
"X-Crowdin-File: /main/beszel/site/src/locales/en/en.po\n"
|
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
|
||||||
"X-Crowdin-File-ID: 16\n"
|
"X-Crowdin-File-ID: 32\n"
|
||||||
|
|
||||||
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -46,7 +46,7 @@ msgstr "1 hora"
|
|||||||
#. Load average
|
#. Load average
|
||||||
#: src/components/charts/load-average-chart.tsx
|
#: src/components/charts/load-average-chart.tsx
|
||||||
msgid "1 min"
|
msgid "1 min"
|
||||||
msgstr "1 min"
|
msgstr ""
|
||||||
|
|
||||||
#: src/lib/utils.ts
|
#: src/lib/utils.ts
|
||||||
msgid "1 week"
|
msgid "1 week"
|
||||||
@@ -59,7 +59,7 @@ msgstr "12 horas"
|
|||||||
#. Load average
|
#. Load average
|
||||||
#: src/components/charts/load-average-chart.tsx
|
#: src/components/charts/load-average-chart.tsx
|
||||||
msgid "15 min"
|
msgid "15 min"
|
||||||
msgstr "15 min"
|
msgstr ""
|
||||||
|
|
||||||
#: src/lib/utils.ts
|
#: src/lib/utils.ts
|
||||||
msgid "24 hours"
|
msgid "24 hours"
|
||||||
@@ -72,7 +72,7 @@ msgstr "30 días"
|
|||||||
#. Load average
|
#. Load average
|
||||||
#: src/components/charts/load-average-chart.tsx
|
#: src/components/charts/load-average-chart.tsx
|
||||||
msgid "5 min"
|
msgid "5 min"
|
||||||
msgstr "5 min"
|
msgstr ""
|
||||||
|
|
||||||
#. Table column
|
#. Table column
|
||||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||||
@@ -202,12 +202,12 @@ msgstr "Binario"
|
|||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "Bits (Kbps, Mbps, Gbps)"
|
msgid "Bits (Kbps, Mbps, Gbps)"
|
||||||
msgstr "Bits (Kbps, Mbps, Gbps)"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "Bytes (KB/s, MB/s, GB/s)"
|
msgid "Bytes (KB/s, MB/s, GB/s)"
|
||||||
msgstr "Bytes (KB/s, MB/s, GB/s)"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/charts/mem-chart.tsx
|
#: src/components/charts/mem-chart.tsx
|
||||||
msgid "Cache / Buffers"
|
msgid "Cache / Buffers"
|
||||||
@@ -224,7 +224,7 @@ msgstr "Precaución - posible pérdida de datos"
|
|||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "Celsius (°C)"
|
msgid "Celsius (°C)"
|
||||||
msgstr "Celsius (°C)"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "Change display units for metrics."
|
msgid "Change display units for metrics."
|
||||||
@@ -342,7 +342,7 @@ msgstr "Copiar YAML"
|
|||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "CPU"
|
msgid "CPU"
|
||||||
msgstr "CPU"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -495,7 +495,7 @@ msgstr "Ingrese su contraseña de un solo uso."
|
|||||||
#: src/components/routes/settings/notifications.tsx
|
#: src/components/routes/settings/notifications.tsx
|
||||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||||
msgid "Error"
|
msgid "Error"
|
||||||
msgstr "Error"
|
msgstr ""
|
||||||
|
|
||||||
#. placeholder {0}: alert.value
|
#. placeholder {0}: alert.value
|
||||||
#. placeholder {1}: info.unit
|
#. placeholder {1}: info.unit
|
||||||
@@ -522,7 +522,7 @@ msgstr "Exporte la configuración actual de sus sistemas."
|
|||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "Fahrenheit (°F)"
|
msgid "Fahrenheit (°F)"
|
||||||
msgstr "Fahrenheit (°F)"
|
msgstr ""
|
||||||
|
|
||||||
#: src/lib/api.ts
|
#: src/lib/api.ts
|
||||||
msgid "Failed to authenticate"
|
msgid "Failed to authenticate"
|
||||||
@@ -574,7 +574,7 @@ msgstr "Llena"
|
|||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
#: src/components/routes/settings/layout.tsx
|
#: src/components/routes/settings/layout.tsx
|
||||||
msgid "General"
|
msgid "General"
|
||||||
msgstr "General"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "GPU Power Draw"
|
msgid "GPU Power Draw"
|
||||||
@@ -592,7 +592,7 @@ msgstr "Comando Homebrew"
|
|||||||
|
|
||||||
#: src/components/add-system.tsx
|
#: src/components/add-system.tsx
|
||||||
msgid "Host / IP"
|
msgid "Host / IP"
|
||||||
msgstr "Host / IP"
|
msgstr ""
|
||||||
|
|
||||||
#. Context: Battery state
|
#. Context: Battery state
|
||||||
#: src/lib/i18n.ts
|
#: src/lib/i18n.ts
|
||||||
@@ -610,7 +610,7 @@ msgstr "Dirección de correo electrónico no válida."
|
|||||||
#. Linux kernel
|
#. Linux kernel
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "Kernel"
|
msgid "Kernel"
|
||||||
msgstr "Kernel"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "Language"
|
msgid "Language"
|
||||||
@@ -1056,7 +1056,7 @@ msgstr "Alternar tema"
|
|||||||
#: src/components/add-system.tsx
|
#: src/components/add-system.tsx
|
||||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||||
msgid "Token"
|
msgid "Token"
|
||||||
msgstr "Token"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/settings/layout.tsx
|
#: src/components/routes/settings/layout.tsx
|
||||||
@@ -1233,3 +1233,4 @@ msgstr "Configuración YAML"
|
|||||||
#: src/components/routes/settings/layout.tsx
|
#: src/components/routes/settings/layout.tsx
|
||||||
msgid "Your user settings have been updated."
|
msgid "Your user settings have been updated."
|
||||||
msgstr "Su configuración de usuario ha sido actualizada."
|
msgstr "Su configuración de usuario ha sido actualizada."
|
||||||
|
|
||||||
|
|||||||
@@ -8,15 +8,15 @@ msgstr ""
|
|||||||
"Language: fa\n"
|
"Language: fa\n"
|
||||||
"Project-Id-Version: beszel\n"
|
"Project-Id-Version: beszel\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"PO-Revision-Date: 2025-08-28 23:21\n"
|
"PO-Revision-Date: 2025-09-17 18:45\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Persian\n"
|
"Language-Team: Persian\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
"X-Crowdin-Project: beszel\n"
|
"X-Crowdin-Project: beszel\n"
|
||||||
"X-Crowdin-Project-ID: 733311\n"
|
"X-Crowdin-Project-ID: 733311\n"
|
||||||
"X-Crowdin-Language: fa\n"
|
"X-Crowdin-Language: fa\n"
|
||||||
"X-Crowdin-File: /main/beszel/site/src/locales/en/en.po\n"
|
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
|
||||||
"X-Crowdin-File-ID: 16\n"
|
"X-Crowdin-File-ID: 32\n"
|
||||||
|
|
||||||
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -1233,3 +1233,4 @@ msgstr "پیکربندی YAML"
|
|||||||
#: src/components/routes/settings/layout.tsx
|
#: src/components/routes/settings/layout.tsx
|
||||||
msgid "Your user settings have been updated."
|
msgid "Your user settings have been updated."
|
||||||
msgstr "تنظیمات کاربری شما بهروزرسانی شد."
|
msgstr "تنظیمات کاربری شما بهروزرسانی شد."
|
||||||
|
|
||||||
|
|||||||
@@ -8,15 +8,15 @@ msgstr ""
|
|||||||
"Language: fr\n"
|
"Language: fr\n"
|
||||||
"Project-Id-Version: beszel\n"
|
"Project-Id-Version: beszel\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"PO-Revision-Date: 2025-08-28 23:21\n"
|
"PO-Revision-Date: 2025-09-17 18:45\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: French\n"
|
"Language-Team: French\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||||
"X-Crowdin-Project: beszel\n"
|
"X-Crowdin-Project: beszel\n"
|
||||||
"X-Crowdin-Project-ID: 733311\n"
|
"X-Crowdin-Project-ID: 733311\n"
|
||||||
"X-Crowdin-Language: fr\n"
|
"X-Crowdin-Language: fr\n"
|
||||||
"X-Crowdin-File: /main/beszel/site/src/locales/en/en.po\n"
|
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
|
||||||
"X-Crowdin-File-ID: 16\n"
|
"X-Crowdin-File-ID: 32\n"
|
||||||
|
|
||||||
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -78,7 +78,7 @@ msgstr ""
|
|||||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "Actions"
|
msgid "Actions"
|
||||||
msgstr "Actions"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/alerts-history-columns.tsx
|
#: src/components/alerts-history-columns.tsx
|
||||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||||
@@ -112,11 +112,11 @@ msgstr "Ajuster les options d'affichage pour les graphiques."
|
|||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
msgid "Admin"
|
msgid "Admin"
|
||||||
msgstr "Admin"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "Agent"
|
msgid "Agent"
|
||||||
msgstr "Agent"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||||
@@ -236,7 +236,7 @@ msgstr "Modifier les options générales de l'application."
|
|||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "Charge"
|
msgid "Charge"
|
||||||
msgstr "Charge"
|
msgstr ""
|
||||||
|
|
||||||
#. Context: Battery state
|
#. Context: Battery state
|
||||||
#: src/lib/i18n.ts
|
#: src/lib/i18n.ts
|
||||||
@@ -342,7 +342,7 @@ msgstr "Copier YAML"
|
|||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "CPU"
|
msgid "CPU"
|
||||||
msgstr "CPU"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -435,7 +435,7 @@ msgstr "Entrée/Sortie réseau Docker"
|
|||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
msgid "Documentation"
|
msgid "Documentation"
|
||||||
msgstr "Documentation"
|
msgstr ""
|
||||||
|
|
||||||
#. Context: System is down
|
#. Context: System is down
|
||||||
#: src/components/alerts-history-columns.tsx
|
#: src/components/alerts-history-columns.tsx
|
||||||
@@ -466,7 +466,7 @@ msgstr "Éditer"
|
|||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
#: src/components/login/otp-forms.tsx
|
#: src/components/login/otp-forms.tsx
|
||||||
msgid "Email"
|
msgid "Email"
|
||||||
msgstr "Email"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/notifications.tsx
|
#: src/components/routes/settings/notifications.tsx
|
||||||
msgid "Email notifications"
|
msgid "Email notifications"
|
||||||
@@ -675,7 +675,7 @@ msgstr "Guide pour une installation manuelle"
|
|||||||
#. Chart select field. Please try to keep this short.
|
#. Chart select field. Please try to keep this short.
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "Max 1 min"
|
msgid "Max 1 min"
|
||||||
msgstr "Max 1 min"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "Memory"
|
msgid "Memory"
|
||||||
@@ -697,7 +697,7 @@ msgstr "Nom"
|
|||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "Net"
|
msgid "Net"
|
||||||
msgstr "Net"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "Network traffic of docker containers"
|
msgid "Network traffic of docker containers"
|
||||||
@@ -731,7 +731,7 @@ msgstr "Aucun système trouvé."
|
|||||||
#: src/components/routes/settings/layout.tsx
|
#: src/components/routes/settings/layout.tsx
|
||||||
#: src/components/routes/settings/notifications.tsx
|
#: src/components/routes/settings/notifications.tsx
|
||||||
msgid "Notifications"
|
msgid "Notifications"
|
||||||
msgstr "Notifications"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/auth-form.tsx
|
#: src/components/login/auth-form.tsx
|
||||||
msgid "OAuth 2 / OIDC support"
|
msgid "OAuth 2 / OIDC support"
|
||||||
@@ -761,7 +761,7 @@ msgstr "Écraser les alertes existantes"
|
|||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
msgid "Page"
|
msgid "Page"
|
||||||
msgstr "Page"
|
msgstr ""
|
||||||
|
|
||||||
#. placeholder {0}: table.getState().pagination.pageIndex + 1
|
#. placeholder {0}: table.getState().pagination.pageIndex + 1
|
||||||
#. placeholder {1}: table.getPageCount()
|
#. placeholder {1}: table.getPageCount()
|
||||||
@@ -792,7 +792,7 @@ msgstr "Demande de réinitialisation du mot de passe reçue"
|
|||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "Pause"
|
msgid "Pause"
|
||||||
msgstr "Pause"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "Paused"
|
msgid "Paused"
|
||||||
@@ -837,7 +837,7 @@ msgstr "Veuillez vous connecter à votre compte"
|
|||||||
|
|
||||||
#: src/components/add-system.tsx
|
#: src/components/add-system.tsx
|
||||||
msgid "Port"
|
msgid "Port"
|
||||||
msgstr "Port"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -1233,3 +1233,4 @@ msgstr "Configuration YAML"
|
|||||||
#: src/components/routes/settings/layout.tsx
|
#: src/components/routes/settings/layout.tsx
|
||||||
msgid "Your user settings have been updated."
|
msgid "Your user settings have been updated."
|
||||||
msgstr "Vos paramètres utilisateur ont été mis à jour."
|
msgstr "Vos paramètres utilisateur ont été mis à jour."
|
||||||
|
|
||||||
|
|||||||
@@ -8,15 +8,15 @@ msgstr ""
|
|||||||
"Language: hr\n"
|
"Language: hr\n"
|
||||||
"Project-Id-Version: beszel\n"
|
"Project-Id-Version: beszel\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"PO-Revision-Date: 2025-08-28 23:21\n"
|
"PO-Revision-Date: 2025-09-17 18:45\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Croatian\n"
|
"Language-Team: Croatian\n"
|
||||||
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
|
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
|
||||||
"X-Crowdin-Project: beszel\n"
|
"X-Crowdin-Project: beszel\n"
|
||||||
"X-Crowdin-Project-ID: 733311\n"
|
"X-Crowdin-Project-ID: 733311\n"
|
||||||
"X-Crowdin-Language: hr\n"
|
"X-Crowdin-Language: hr\n"
|
||||||
"X-Crowdin-File: /main/beszel/site/src/locales/en/en.po\n"
|
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
|
||||||
"X-Crowdin-File-ID: 16\n"
|
"X-Crowdin-File-ID: 32\n"
|
||||||
|
|
||||||
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -112,11 +112,11 @@ msgstr "Podesite opcije prikaza za grafikone."
|
|||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
msgid "Admin"
|
msgid "Admin"
|
||||||
msgstr "Admin"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "Agent"
|
msgid "Agent"
|
||||||
msgstr "Agent"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||||
@@ -401,11 +401,11 @@ msgstr "Prazni se"
|
|||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "Disk"
|
msgid "Disk"
|
||||||
msgstr "Disk"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "Disk I/O"
|
msgid "Disk I/O"
|
||||||
msgstr "Disk I/O"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "Disk unit"
|
msgid "Disk unit"
|
||||||
@@ -466,7 +466,7 @@ msgstr ""
|
|||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
#: src/components/login/otp-forms.tsx
|
#: src/components/login/otp-forms.tsx
|
||||||
msgid "Email"
|
msgid "Email"
|
||||||
msgstr "Email"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/notifications.tsx
|
#: src/components/routes/settings/notifications.tsx
|
||||||
msgid "Email notifications"
|
msgid "Email notifications"
|
||||||
@@ -545,7 +545,7 @@ msgstr "Ažuriranje upozorenja nije uspjelo"
|
|||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "Filter..."
|
msgid "Filter..."
|
||||||
msgstr "Filter..."
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||||
msgid "Fingerprint"
|
msgid "Fingerprint"
|
||||||
@@ -592,7 +592,7 @@ msgstr "Homebrew naredba"
|
|||||||
|
|
||||||
#: src/components/add-system.tsx
|
#: src/components/add-system.tsx
|
||||||
msgid "Host / IP"
|
msgid "Host / IP"
|
||||||
msgstr "Host / IP"
|
msgstr ""
|
||||||
|
|
||||||
#. Context: Battery state
|
#. Context: Battery state
|
||||||
#: src/lib/i18n.ts
|
#: src/lib/i18n.ts
|
||||||
@@ -610,7 +610,7 @@ msgstr "Nevažeća adresa e-pošte."
|
|||||||
#. Linux kernel
|
#. Linux kernel
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "Kernel"
|
msgid "Kernel"
|
||||||
msgstr "Kernel"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "Language"
|
msgid "Language"
|
||||||
@@ -837,7 +837,7 @@ msgstr "Molimo prijavite se u svoj račun"
|
|||||||
|
|
||||||
#: src/components/add-system.tsx
|
#: src/components/add-system.tsx
|
||||||
msgid "Port"
|
msgid "Port"
|
||||||
msgstr "Port"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -957,7 +957,7 @@ msgstr ""
|
|||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
#: src/lib/alerts.ts
|
#: src/lib/alerts.ts
|
||||||
msgid "Status"
|
msgid "Status"
|
||||||
msgstr "Status"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "Swap space used by the system"
|
msgid "Swap space used by the system"
|
||||||
@@ -1224,7 +1224,7 @@ msgstr "Piši"
|
|||||||
|
|
||||||
#: src/components/routes/settings/layout.tsx
|
#: src/components/routes/settings/layout.tsx
|
||||||
msgid "YAML Config"
|
msgid "YAML Config"
|
||||||
msgstr "YAML Config"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/config-yaml.tsx
|
#: src/components/routes/settings/config-yaml.tsx
|
||||||
msgid "YAML Configuration"
|
msgid "YAML Configuration"
|
||||||
@@ -1233,3 +1233,4 @@ msgstr "YAML Konfiguracija"
|
|||||||
#: src/components/routes/settings/layout.tsx
|
#: src/components/routes/settings/layout.tsx
|
||||||
msgid "Your user settings have been updated."
|
msgid "Your user settings have been updated."
|
||||||
msgstr "Vaše korisničke postavke su ažurirane."
|
msgstr "Vaše korisničke postavke su ažurirane."
|
||||||
|
|
||||||
|
|||||||
@@ -8,15 +8,15 @@ msgstr ""
|
|||||||
"Language: hu\n"
|
"Language: hu\n"
|
||||||
"Project-Id-Version: beszel\n"
|
"Project-Id-Version: beszel\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"PO-Revision-Date: 2025-08-28 23:21\n"
|
"PO-Revision-Date: 2025-09-17 18:45\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Hungarian\n"
|
"Language-Team: Hungarian\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
"X-Crowdin-Project: beszel\n"
|
"X-Crowdin-Project: beszel\n"
|
||||||
"X-Crowdin-Project-ID: 733311\n"
|
"X-Crowdin-Project-ID: 733311\n"
|
||||||
"X-Crowdin-Language: hu\n"
|
"X-Crowdin-Language: hu\n"
|
||||||
"X-Crowdin-File: /main/beszel/site/src/locales/en/en.po\n"
|
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
|
||||||
"X-Crowdin-File-ID: 16\n"
|
"X-Crowdin-File-ID: 32\n"
|
||||||
|
|
||||||
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -112,7 +112,7 @@ msgstr "Állítsa be a diagram megjelenítését."
|
|||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
msgid "Admin"
|
msgid "Admin"
|
||||||
msgstr "Admin"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "Agent"
|
msgid "Agent"
|
||||||
@@ -342,7 +342,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "CPU"
|
msgid "CPU"
|
||||||
msgstr "CPU"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -466,7 +466,7 @@ msgstr ""
|
|||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
#: src/components/login/otp-forms.tsx
|
#: src/components/login/otp-forms.tsx
|
||||||
msgid "Email"
|
msgid "Email"
|
||||||
msgstr "Email"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/notifications.tsx
|
#: src/components/routes/settings/notifications.tsx
|
||||||
msgid "Email notifications"
|
msgid "Email notifications"
|
||||||
@@ -610,7 +610,7 @@ msgstr "Érvénytelen e-mail cím."
|
|||||||
#. Linux kernel
|
#. Linux kernel
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "Kernel"
|
msgid "Kernel"
|
||||||
msgstr "Kernel"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "Language"
|
msgid "Language"
|
||||||
@@ -837,7 +837,7 @@ msgstr "Kérjük, jelentkezzen be a fiókjába"
|
|||||||
|
|
||||||
#: src/components/add-system.tsx
|
#: src/components/add-system.tsx
|
||||||
msgid "Port"
|
msgid "Port"
|
||||||
msgstr "Port"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -1233,3 +1233,4 @@ msgstr "YAML konfiguráció"
|
|||||||
#: src/components/routes/settings/layout.tsx
|
#: src/components/routes/settings/layout.tsx
|
||||||
msgid "Your user settings have been updated."
|
msgid "Your user settings have been updated."
|
||||||
msgstr "A felhasználói beállítások frissítésre kerültek."
|
msgstr "A felhasználói beállítások frissítésre kerültek."
|
||||||
|
|
||||||
|
|||||||
@@ -8,15 +8,15 @@ msgstr ""
|
|||||||
"Language: is\n"
|
"Language: is\n"
|
||||||
"Project-Id-Version: beszel\n"
|
"Project-Id-Version: beszel\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"PO-Revision-Date: 2025-08-28 23:21\n"
|
"PO-Revision-Date: 2025-09-17 18:45\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Icelandic\n"
|
"Language-Team: Icelandic\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
"X-Crowdin-Project: beszel\n"
|
"X-Crowdin-Project: beszel\n"
|
||||||
"X-Crowdin-Project-ID: 733311\n"
|
"X-Crowdin-Project-ID: 733311\n"
|
||||||
"X-Crowdin-Language: is\n"
|
"X-Crowdin-Language: is\n"
|
||||||
"X-Crowdin-File: /main/beszel/site/src/locales/en/en.po\n"
|
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
|
||||||
"X-Crowdin-File-ID: 16\n"
|
"X-Crowdin-File-ID: 32\n"
|
||||||
|
|
||||||
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -112,7 +112,7 @@ msgstr ""
|
|||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
msgid "Admin"
|
msgid "Admin"
|
||||||
msgstr "Admin"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "Agent"
|
msgid "Agent"
|
||||||
@@ -197,7 +197,7 @@ msgstr "Beszel notar <0>Shoutrrr</0> til að tengjast vinsælum tilkynningaþjó
|
|||||||
|
|
||||||
#: src/components/add-system.tsx
|
#: src/components/add-system.tsx
|
||||||
msgid "Binary"
|
msgid "Binary"
|
||||||
msgstr "Binary"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
@@ -568,7 +568,7 @@ msgstr ""
|
|||||||
#. Context: Battery state
|
#. Context: Battery state
|
||||||
#: src/lib/i18n.ts
|
#: src/lib/i18n.ts
|
||||||
msgid "Full"
|
msgid "Full"
|
||||||
msgstr "Full"
|
msgstr ""
|
||||||
|
|
||||||
#. Context: General settings
|
#. Context: General settings
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
@@ -592,7 +592,7 @@ msgstr "Homebrew skipun"
|
|||||||
|
|
||||||
#: src/components/add-system.tsx
|
#: src/components/add-system.tsx
|
||||||
msgid "Host / IP"
|
msgid "Host / IP"
|
||||||
msgstr "Host / IP"
|
msgstr ""
|
||||||
|
|
||||||
#. Context: Battery state
|
#. Context: Battery state
|
||||||
#: src/lib/i18n.ts
|
#: src/lib/i18n.ts
|
||||||
@@ -697,7 +697,7 @@ msgstr "Nafn"
|
|||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "Net"
|
msgid "Net"
|
||||||
msgstr "Net"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "Network traffic of docker containers"
|
msgid "Network traffic of docker containers"
|
||||||
@@ -837,7 +837,7 @@ msgstr "Vinsamlegast skráðu þig inn á aðganginn þinn"
|
|||||||
|
|
||||||
#: src/components/add-system.tsx
|
#: src/components/add-system.tsx
|
||||||
msgid "Port"
|
msgid "Port"
|
||||||
msgstr "Port"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -920,7 +920,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "Sent"
|
msgid "Sent"
|
||||||
msgstr "Sent"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "Set percentage thresholds for meter colors."
|
msgid "Set percentage thresholds for meter colors."
|
||||||
@@ -1233,3 +1233,4 @@ msgstr ""
|
|||||||
#: src/components/routes/settings/layout.tsx
|
#: src/components/routes/settings/layout.tsx
|
||||||
msgid "Your user settings have been updated."
|
msgid "Your user settings have been updated."
|
||||||
msgstr "Notenda stillingar vistaðar."
|
msgstr "Notenda stillingar vistaðar."
|
||||||
|
|
||||||
|
|||||||
@@ -8,15 +8,15 @@ msgstr ""
|
|||||||
"Language: it\n"
|
"Language: it\n"
|
||||||
"Project-Id-Version: beszel\n"
|
"Project-Id-Version: beszel\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"PO-Revision-Date: 2025-08-28 23:21\n"
|
"PO-Revision-Date: 2025-09-17 18:45\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Italian\n"
|
"Language-Team: Italian\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
"X-Crowdin-Project: beszel\n"
|
"X-Crowdin-Project: beszel\n"
|
||||||
"X-Crowdin-Project-ID: 733311\n"
|
"X-Crowdin-Project-ID: 733311\n"
|
||||||
"X-Crowdin-Language: it\n"
|
"X-Crowdin-Language: it\n"
|
||||||
"X-Crowdin-File: /main/beszel/site/src/locales/en/en.po\n"
|
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
|
||||||
"X-Crowdin-File-ID: 16\n"
|
"X-Crowdin-File-ID: 32\n"
|
||||||
|
|
||||||
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -46,7 +46,7 @@ msgstr "1 ora"
|
|||||||
#. Load average
|
#. Load average
|
||||||
#: src/components/charts/load-average-chart.tsx
|
#: src/components/charts/load-average-chart.tsx
|
||||||
msgid "1 min"
|
msgid "1 min"
|
||||||
msgstr "1 min"
|
msgstr ""
|
||||||
|
|
||||||
#: src/lib/utils.ts
|
#: src/lib/utils.ts
|
||||||
msgid "1 week"
|
msgid "1 week"
|
||||||
@@ -59,7 +59,7 @@ msgstr "12 ore"
|
|||||||
#. Load average
|
#. Load average
|
||||||
#: src/components/charts/load-average-chart.tsx
|
#: src/components/charts/load-average-chart.tsx
|
||||||
msgid "15 min"
|
msgid "15 min"
|
||||||
msgstr "15 min"
|
msgstr ""
|
||||||
|
|
||||||
#: src/lib/utils.ts
|
#: src/lib/utils.ts
|
||||||
msgid "24 hours"
|
msgid "24 hours"
|
||||||
@@ -72,7 +72,7 @@ msgstr "30 giorni"
|
|||||||
#. Load average
|
#. Load average
|
||||||
#: src/components/charts/load-average-chart.tsx
|
#: src/components/charts/load-average-chart.tsx
|
||||||
msgid "5 min"
|
msgid "5 min"
|
||||||
msgstr "5 min"
|
msgstr ""
|
||||||
|
|
||||||
#. Table column
|
#. Table column
|
||||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||||
@@ -224,7 +224,7 @@ msgstr "Attenzione - possibile perdita di dati"
|
|||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "Celsius (°C)"
|
msgid "Celsius (°C)"
|
||||||
msgstr "Celsius (°C)"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "Change display units for metrics."
|
msgid "Change display units for metrics."
|
||||||
@@ -342,7 +342,7 @@ msgstr "Copia YAML"
|
|||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "CPU"
|
msgid "CPU"
|
||||||
msgstr "CPU"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -466,7 +466,7 @@ msgstr "Modifica"
|
|||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
#: src/components/login/otp-forms.tsx
|
#: src/components/login/otp-forms.tsx
|
||||||
msgid "Email"
|
msgid "Email"
|
||||||
msgstr "Email"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/notifications.tsx
|
#: src/components/routes/settings/notifications.tsx
|
||||||
msgid "Email notifications"
|
msgid "Email notifications"
|
||||||
@@ -522,7 +522,7 @@ msgstr "Esporta la configurazione attuale dei tuoi sistemi."
|
|||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "Fahrenheit (°F)"
|
msgid "Fahrenheit (°F)"
|
||||||
msgstr "Fahrenheit (°F)"
|
msgstr ""
|
||||||
|
|
||||||
#: src/lib/api.ts
|
#: src/lib/api.ts
|
||||||
msgid "Failed to authenticate"
|
msgid "Failed to authenticate"
|
||||||
@@ -592,7 +592,7 @@ msgstr "Comando Homebrew"
|
|||||||
|
|
||||||
#: src/components/add-system.tsx
|
#: src/components/add-system.tsx
|
||||||
msgid "Host / IP"
|
msgid "Host / IP"
|
||||||
msgstr "Host / IP"
|
msgstr ""
|
||||||
|
|
||||||
#. Context: Battery state
|
#. Context: Battery state
|
||||||
#: src/lib/i18n.ts
|
#: src/lib/i18n.ts
|
||||||
@@ -610,7 +610,7 @@ msgstr "Indirizzo email non valido."
|
|||||||
#. Linux kernel
|
#. Linux kernel
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "Kernel"
|
msgid "Kernel"
|
||||||
msgstr "Kernel"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "Language"
|
msgid "Language"
|
||||||
@@ -675,7 +675,7 @@ msgstr "Istruzioni di configurazione manuale"
|
|||||||
#. Chart select field. Please try to keep this short.
|
#. Chart select field. Please try to keep this short.
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "Max 1 min"
|
msgid "Max 1 min"
|
||||||
msgstr "Max 1 min"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "Memory"
|
msgid "Memory"
|
||||||
@@ -776,7 +776,7 @@ msgstr "Pagine / Impostazioni"
|
|||||||
#: src/components/login/auth-form.tsx
|
#: src/components/login/auth-form.tsx
|
||||||
#: src/components/login/auth-form.tsx
|
#: src/components/login/auth-form.tsx
|
||||||
msgid "Password"
|
msgid "Password"
|
||||||
msgstr "Password"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/auth-form.tsx
|
#: src/components/login/auth-form.tsx
|
||||||
msgid "Password must be at least 8 characters."
|
msgid "Password must be at least 8 characters."
|
||||||
@@ -1010,7 +1010,7 @@ msgstr "Temperature dei sensori di sistema"
|
|||||||
|
|
||||||
#: src/components/routes/settings/notifications.tsx
|
#: src/components/routes/settings/notifications.tsx
|
||||||
msgid "Test <0>URL</0>"
|
msgid "Test <0>URL</0>"
|
||||||
msgstr "Test <0>URL</0>"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/notifications.tsx
|
#: src/components/routes/settings/notifications.tsx
|
||||||
msgid "Test notification sent"
|
msgid "Test notification sent"
|
||||||
@@ -1056,7 +1056,7 @@ msgstr "Attiva/disattiva tema"
|
|||||||
#: src/components/add-system.tsx
|
#: src/components/add-system.tsx
|
||||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||||
msgid "Token"
|
msgid "Token"
|
||||||
msgstr "Token"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/settings/layout.tsx
|
#: src/components/routes/settings/layout.tsx
|
||||||
@@ -1233,3 +1233,4 @@ msgstr "Configurazione YAML"
|
|||||||
#: src/components/routes/settings/layout.tsx
|
#: src/components/routes/settings/layout.tsx
|
||||||
msgid "Your user settings have been updated."
|
msgid "Your user settings have been updated."
|
||||||
msgstr "Le impostazioni utente sono state aggiornate."
|
msgstr "Le impostazioni utente sono state aggiornate."
|
||||||
|
|
||||||
|
|||||||
@@ -8,15 +8,15 @@ msgstr ""
|
|||||||
"Language: ja\n"
|
"Language: ja\n"
|
||||||
"Project-Id-Version: beszel\n"
|
"Project-Id-Version: beszel\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"PO-Revision-Date: 2025-08-28 23:21\n"
|
"PO-Revision-Date: 2025-09-17 18:45\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Japanese\n"
|
"Language-Team: Japanese\n"
|
||||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||||
"X-Crowdin-Project: beszel\n"
|
"X-Crowdin-Project: beszel\n"
|
||||||
"X-Crowdin-Project-ID: 733311\n"
|
"X-Crowdin-Project-ID: 733311\n"
|
||||||
"X-Crowdin-Language: ja\n"
|
"X-Crowdin-Language: ja\n"
|
||||||
"X-Crowdin-File: /main/beszel/site/src/locales/en/en.po\n"
|
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
|
||||||
"X-Crowdin-File-ID: 16\n"
|
"X-Crowdin-File-ID: 32\n"
|
||||||
|
|
||||||
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -342,7 +342,7 @@ msgstr "YAMLをコピー"
|
|||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "CPU"
|
msgid "CPU"
|
||||||
msgstr "CPU"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -1233,3 +1233,4 @@ msgstr "YAML設定"
|
|||||||
#: src/components/routes/settings/layout.tsx
|
#: src/components/routes/settings/layout.tsx
|
||||||
msgid "Your user settings have been updated."
|
msgid "Your user settings have been updated."
|
||||||
msgstr "ユーザー設定が更新されました。"
|
msgstr "ユーザー設定が更新されました。"
|
||||||
|
|
||||||
|
|||||||
@@ -8,15 +8,15 @@ msgstr ""
|
|||||||
"Language: ko\n"
|
"Language: ko\n"
|
||||||
"Project-Id-Version: beszel\n"
|
"Project-Id-Version: beszel\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"PO-Revision-Date: 2025-08-31 15:44\n"
|
"PO-Revision-Date: 2025-09-17 18:45\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Korean\n"
|
"Language-Team: Korean\n"
|
||||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||||
"X-Crowdin-Project: beszel\n"
|
"X-Crowdin-Project: beszel\n"
|
||||||
"X-Crowdin-Project-ID: 733311\n"
|
"X-Crowdin-Project-ID: 733311\n"
|
||||||
"X-Crowdin-Language: ko\n"
|
"X-Crowdin-Language: ko\n"
|
||||||
"X-Crowdin-File: /main/beszel/site/src/locales/en/en.po\n"
|
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
|
||||||
"X-Crowdin-File-ID: 16\n"
|
"X-Crowdin-File-ID: 32\n"
|
||||||
|
|
||||||
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -342,7 +342,7 @@ msgstr "YAML 복사"
|
|||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "CPU"
|
msgid "CPU"
|
||||||
msgstr "CPU"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -1233,3 +1233,4 @@ msgstr "YAML 구성"
|
|||||||
#: src/components/routes/settings/layout.tsx
|
#: src/components/routes/settings/layout.tsx
|
||||||
msgid "Your user settings have been updated."
|
msgid "Your user settings have been updated."
|
||||||
msgstr "사용자 설정이 업데이트되었습니다."
|
msgstr "사용자 설정이 업데이트되었습니다."
|
||||||
|
|
||||||
|
|||||||
@@ -8,15 +8,15 @@ msgstr ""
|
|||||||
"Language: nl\n"
|
"Language: nl\n"
|
||||||
"Project-Id-Version: beszel\n"
|
"Project-Id-Version: beszel\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"PO-Revision-Date: 2025-08-28 23:21\n"
|
"PO-Revision-Date: 2025-09-17 18:45\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Dutch\n"
|
"Language-Team: Dutch\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
"X-Crowdin-Project: beszel\n"
|
"X-Crowdin-Project: beszel\n"
|
||||||
"X-Crowdin-Project-ID: 733311\n"
|
"X-Crowdin-Project-ID: 733311\n"
|
||||||
"X-Crowdin-Language: nl\n"
|
"X-Crowdin-Language: nl\n"
|
||||||
"X-Crowdin-File: /main/beszel/site/src/locales/en/en.po\n"
|
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
|
||||||
"X-Crowdin-File-ID: 16\n"
|
"X-Crowdin-File-ID: 32\n"
|
||||||
|
|
||||||
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -50,7 +50,7 @@ msgstr "1 minuut"
|
|||||||
|
|
||||||
#: src/lib/utils.ts
|
#: src/lib/utils.ts
|
||||||
msgid "1 week"
|
msgid "1 week"
|
||||||
msgstr "1 week"
|
msgstr ""
|
||||||
|
|
||||||
#: src/lib/utils.ts
|
#: src/lib/utils.ts
|
||||||
msgid "12 hours"
|
msgid "12 hours"
|
||||||
@@ -112,11 +112,11 @@ msgstr "Weergaveopties voor grafieken aanpassen."
|
|||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
msgid "Admin"
|
msgid "Admin"
|
||||||
msgstr "Admin"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "Agent"
|
msgid "Agent"
|
||||||
msgstr "Agent"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||||
@@ -202,16 +202,16 @@ msgstr "Binair"
|
|||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "Bits (Kbps, Mbps, Gbps)"
|
msgid "Bits (Kbps, Mbps, Gbps)"
|
||||||
msgstr "Bits (Kbps, Mbps, Gbps)"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "Bytes (KB/s, MB/s, GB/s)"
|
msgid "Bytes (KB/s, MB/s, GB/s)"
|
||||||
msgstr "Bytes (KB/s, MB/s, GB/s)"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/charts/mem-chart.tsx
|
#: src/components/charts/mem-chart.tsx
|
||||||
msgid "Cache / Buffers"
|
msgid "Cache / Buffers"
|
||||||
msgstr "Cache / Buffers"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
@@ -224,7 +224,7 @@ msgstr "Opgelet - potentieel gegevensverlies"
|
|||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "Celsius (°C)"
|
msgid "Celsius (°C)"
|
||||||
msgstr "Celsius (°C)"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "Change display units for metrics."
|
msgid "Change display units for metrics."
|
||||||
@@ -342,7 +342,7 @@ msgstr "YAML kopiëren"
|
|||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "CPU"
|
msgid "CPU"
|
||||||
msgstr "CPU"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -379,7 +379,7 @@ msgstr "Huidige status"
|
|||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "Dashboard"
|
msgid "Dashboard"
|
||||||
msgstr "Dashboard"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "Default time period"
|
msgid "Default time period"
|
||||||
@@ -522,7 +522,7 @@ msgstr "Exporteer je huidige systeemconfiguratie."
|
|||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "Fahrenheit (°F)"
|
msgid "Fahrenheit (°F)"
|
||||||
msgstr "Fahrenheit (°F)"
|
msgstr ""
|
||||||
|
|
||||||
#: src/lib/api.ts
|
#: src/lib/api.ts
|
||||||
msgid "Failed to authenticate"
|
msgid "Failed to authenticate"
|
||||||
@@ -545,7 +545,7 @@ msgstr "Bijwerken waarschuwing mislukt"
|
|||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "Filter..."
|
msgid "Filter..."
|
||||||
msgstr "Filter..."
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||||
msgid "Fingerprint"
|
msgid "Fingerprint"
|
||||||
@@ -610,7 +610,7 @@ msgstr "Ongeldig e-mailadres."
|
|||||||
#. Linux kernel
|
#. Linux kernel
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "Kernel"
|
msgid "Kernel"
|
||||||
msgstr "Kernel"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "Language"
|
msgid "Language"
|
||||||
@@ -657,7 +657,7 @@ msgstr "Aanmelding mislukt"
|
|||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/navbar.tsx
|
#: src/components/navbar.tsx
|
||||||
msgid "Logs"
|
msgid "Logs"
|
||||||
msgstr "Logs"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/notifications.tsx
|
#: src/components/routes/settings/notifications.tsx
|
||||||
msgid "Looking instead for where to create alerts? Click the bell <0/> icons in the systems table."
|
msgid "Looking instead for where to create alerts? Click the bell <0/> icons in the systems table."
|
||||||
@@ -675,7 +675,7 @@ msgstr "Handmatige installatie-instructies"
|
|||||||
#. Chart select field. Please try to keep this short.
|
#. Chart select field. Please try to keep this short.
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "Max 1 min"
|
msgid "Max 1 min"
|
||||||
msgstr "Max 1 min"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "Memory"
|
msgid "Memory"
|
||||||
@@ -697,7 +697,7 @@ msgstr "Naam"
|
|||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "Net"
|
msgid "Net"
|
||||||
msgstr "Net"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "Network traffic of docker containers"
|
msgid "Network traffic of docker containers"
|
||||||
@@ -749,7 +749,7 @@ msgstr "Eenmalig wachtwoord"
|
|||||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "Open menu"
|
msgid "Open menu"
|
||||||
msgstr "Open menu"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/login/auth-form.tsx
|
#: src/components/login/auth-form.tsx
|
||||||
msgid "Or continue with"
|
msgid "Or continue with"
|
||||||
@@ -957,7 +957,7 @@ msgstr "Status"
|
|||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
#: src/lib/alerts.ts
|
#: src/lib/alerts.ts
|
||||||
msgid "Status"
|
msgid "Status"
|
||||||
msgstr "Status"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "Swap space used by the system"
|
msgid "Swap space used by the system"
|
||||||
@@ -1010,7 +1010,7 @@ msgstr "Temperatuur van systeem sensoren"
|
|||||||
|
|
||||||
#: src/components/routes/settings/notifications.tsx
|
#: src/components/routes/settings/notifications.tsx
|
||||||
msgid "Test <0>URL</0>"
|
msgid "Test <0>URL</0>"
|
||||||
msgstr "Test <0>URL</0>"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/notifications.tsx
|
#: src/components/routes/settings/notifications.tsx
|
||||||
msgid "Test notification sent"
|
msgid "Test notification sent"
|
||||||
@@ -1056,7 +1056,7 @@ msgstr "Schakel thema"
|
|||||||
#: src/components/add-system.tsx
|
#: src/components/add-system.tsx
|
||||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||||
msgid "Token"
|
msgid "Token"
|
||||||
msgstr "Token"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/settings/layout.tsx
|
#: src/components/routes/settings/layout.tsx
|
||||||
@@ -1233,3 +1233,4 @@ msgstr "YAML Configuratie"
|
|||||||
#: src/components/routes/settings/layout.tsx
|
#: src/components/routes/settings/layout.tsx
|
||||||
msgid "Your user settings have been updated."
|
msgid "Your user settings have been updated."
|
||||||
msgstr "Je gebruikersinstellingen zijn bijgewerkt."
|
msgstr "Je gebruikersinstellingen zijn bijgewerkt."
|
||||||
|
|
||||||
|
|||||||
@@ -8,15 +8,15 @@ msgstr ""
|
|||||||
"Language: no\n"
|
"Language: no\n"
|
||||||
"Project-Id-Version: beszel\n"
|
"Project-Id-Version: beszel\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"PO-Revision-Date: 2025-08-28 23:21\n"
|
"PO-Revision-Date: 2025-09-17 18:45\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Norwegian\n"
|
"Language-Team: Norwegian\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
"X-Crowdin-Project: beszel\n"
|
"X-Crowdin-Project: beszel\n"
|
||||||
"X-Crowdin-Project-ID: 733311\n"
|
"X-Crowdin-Project-ID: 733311\n"
|
||||||
"X-Crowdin-Language: no\n"
|
"X-Crowdin-Language: no\n"
|
||||||
"X-Crowdin-File: /main/beszel/site/src/locales/en/en.po\n"
|
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
|
||||||
"X-Crowdin-File-ID: 16\n"
|
"X-Crowdin-File-ID: 32\n"
|
||||||
|
|
||||||
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -112,11 +112,11 @@ msgstr "Juster visningsalternativer for diagrammer."
|
|||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
msgid "Admin"
|
msgid "Admin"
|
||||||
msgstr "Admin"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "Agent"
|
msgid "Agent"
|
||||||
msgstr "Agent"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||||
@@ -342,7 +342,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "CPU"
|
msgid "CPU"
|
||||||
msgstr "CPU"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -401,11 +401,11 @@ msgstr "Lader ut"
|
|||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "Disk"
|
msgid "Disk"
|
||||||
msgstr "Disk"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "Disk I/O"
|
msgid "Disk I/O"
|
||||||
msgstr "Disk I/O"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "Disk unit"
|
msgid "Disk unit"
|
||||||
@@ -545,7 +545,7 @@ msgstr "Kunne ikke oppdatere alarm"
|
|||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "Filter..."
|
msgid "Filter..."
|
||||||
msgstr "Filter..."
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||||
msgid "Fingerprint"
|
msgid "Fingerprint"
|
||||||
@@ -568,7 +568,7 @@ msgstr "FreeBSD kommando"
|
|||||||
#. Context: Battery state
|
#. Context: Battery state
|
||||||
#: src/lib/i18n.ts
|
#: src/lib/i18n.ts
|
||||||
msgid "Full"
|
msgid "Full"
|
||||||
msgstr "Full"
|
msgstr ""
|
||||||
|
|
||||||
#. Context: General settings
|
#. Context: General settings
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
@@ -618,7 +618,7 @@ msgstr "Språk"
|
|||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "Layout"
|
msgid "Layout"
|
||||||
msgstr "Layout"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "Load Average"
|
msgid "Load Average"
|
||||||
@@ -792,7 +792,7 @@ msgstr "Mottatt forespørsel om å nullstille passord"
|
|||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "Pause"
|
msgid "Pause"
|
||||||
msgstr "Pause"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "Paused"
|
msgid "Paused"
|
||||||
@@ -837,7 +837,7 @@ msgstr "Vennligst logg inn på kontoen din"
|
|||||||
|
|
||||||
#: src/components/add-system.tsx
|
#: src/components/add-system.tsx
|
||||||
msgid "Port"
|
msgid "Port"
|
||||||
msgstr "Port"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -957,7 +957,7 @@ msgstr ""
|
|||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
#: src/lib/alerts.ts
|
#: src/lib/alerts.ts
|
||||||
msgid "Status"
|
msgid "Status"
|
||||||
msgstr "Status"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "Swap space used by the system"
|
msgid "Swap space used by the system"
|
||||||
@@ -972,7 +972,7 @@ msgstr "Swap-bruk"
|
|||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
#: src/lib/alerts.ts
|
#: src/lib/alerts.ts
|
||||||
msgid "System"
|
msgid "System"
|
||||||
msgstr "System"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "System load averages over time"
|
msgid "System load averages over time"
|
||||||
@@ -993,7 +993,7 @@ msgstr "Tabell"
|
|||||||
#. Temperature label in systems table
|
#. Temperature label in systems table
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "Temp"
|
msgid "Temp"
|
||||||
msgstr "Temp"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
#: src/lib/alerts.ts
|
#: src/lib/alerts.ts
|
||||||
@@ -1010,7 +1010,7 @@ msgstr "Temperaturer på system-sensorer"
|
|||||||
|
|
||||||
#: src/components/routes/settings/notifications.tsx
|
#: src/components/routes/settings/notifications.tsx
|
||||||
msgid "Test <0>URL</0>"
|
msgid "Test <0>URL</0>"
|
||||||
msgstr "Test <0>URL</0>"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/notifications.tsx
|
#: src/components/routes/settings/notifications.tsx
|
||||||
msgid "Test notification sent"
|
msgid "Test notification sent"
|
||||||
@@ -1233,3 +1233,4 @@ msgstr "YAML Konfigurasjon"
|
|||||||
#: src/components/routes/settings/layout.tsx
|
#: src/components/routes/settings/layout.tsx
|
||||||
msgid "Your user settings have been updated."
|
msgid "Your user settings have been updated."
|
||||||
msgstr "Dine brukerinnstillinger har blitt oppdatert."
|
msgstr "Dine brukerinnstillinger har blitt oppdatert."
|
||||||
|
|
||||||
|
|||||||
@@ -8,15 +8,15 @@ msgstr ""
|
|||||||
"Language: pl\n"
|
"Language: pl\n"
|
||||||
"Project-Id-Version: beszel\n"
|
"Project-Id-Version: beszel\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"PO-Revision-Date: 2025-09-03 18:54\n"
|
"PO-Revision-Date: 2025-09-18 15:36\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Polish\n"
|
"Language-Team: Polish\n"
|
||||||
"Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n"
|
"Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n"
|
||||||
"X-Crowdin-Project: beszel\n"
|
"X-Crowdin-Project: beszel\n"
|
||||||
"X-Crowdin-Project-ID: 733311\n"
|
"X-Crowdin-Project-ID: 733311\n"
|
||||||
"X-Crowdin-Language: pl\n"
|
"X-Crowdin-Language: pl\n"
|
||||||
"X-Crowdin-File: /main/beszel/site/src/locales/en/en.po\n"
|
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
|
||||||
"X-Crowdin-File-ID: 16\n"
|
"X-Crowdin-File-ID: 32\n"
|
||||||
|
|
||||||
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -451,7 +451,7 @@ msgstr "Nie działa ({downSystemsLength})"
|
|||||||
|
|
||||||
#: src/components/routes/system/network-sheet.tsx
|
#: src/components/routes/system/network-sheet.tsx
|
||||||
msgid "Download"
|
msgid "Download"
|
||||||
msgstr "Pobierz"
|
msgstr "Pobieranie"
|
||||||
|
|
||||||
#: src/components/alerts-history-columns.tsx
|
#: src/components/alerts-history-columns.tsx
|
||||||
msgid "Duration"
|
msgid "Duration"
|
||||||
@@ -857,7 +857,7 @@ msgstr "Klucz publiczny"
|
|||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "Read"
|
msgid "Read"
|
||||||
msgstr "Czytaj"
|
msgstr "Odczyt"
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "Received"
|
msgid "Received"
|
||||||
@@ -1143,7 +1143,7 @@ msgstr "Działa ({upSystemsLength})"
|
|||||||
|
|
||||||
#: src/components/routes/system/network-sheet.tsx
|
#: src/components/routes/system/network-sheet.tsx
|
||||||
msgid "Upload"
|
msgid "Upload"
|
||||||
msgstr "Wyślij"
|
msgstr "Wysyłanie"
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "Uptime"
|
msgid "Uptime"
|
||||||
@@ -1233,3 +1233,4 @@ msgstr "Konfiguracja YAML"
|
|||||||
#: src/components/routes/settings/layout.tsx
|
#: src/components/routes/settings/layout.tsx
|
||||||
msgid "Your user settings have been updated."
|
msgid "Your user settings have been updated."
|
||||||
msgstr "Twoje ustawienia użytkownika zostały zaktualizowane."
|
msgstr "Twoje ustawienia użytkownika zostały zaktualizowane."
|
||||||
|
|
||||||
|
|||||||
@@ -8,15 +8,15 @@ msgstr ""
|
|||||||
"Language: pt\n"
|
"Language: pt\n"
|
||||||
"Project-Id-Version: beszel\n"
|
"Project-Id-Version: beszel\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"PO-Revision-Date: 2025-08-28 23:21\n"
|
"PO-Revision-Date: 2025-09-17 18:45\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Portuguese\n"
|
"Language-Team: Portuguese\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
"X-Crowdin-Project: beszel\n"
|
"X-Crowdin-Project: beszel\n"
|
||||||
"X-Crowdin-Project-ID: 733311\n"
|
"X-Crowdin-Project-ID: 733311\n"
|
||||||
"X-Crowdin-Language: pt-PT\n"
|
"X-Crowdin-Language: pt-PT\n"
|
||||||
"X-Crowdin-File: /main/beszel/site/src/locales/en/en.po\n"
|
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
|
||||||
"X-Crowdin-File-ID: 16\n"
|
"X-Crowdin-File-ID: 32\n"
|
||||||
|
|
||||||
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -46,7 +46,7 @@ msgstr "1 hora"
|
|||||||
#. Load average
|
#. Load average
|
||||||
#: src/components/charts/load-average-chart.tsx
|
#: src/components/charts/load-average-chart.tsx
|
||||||
msgid "1 min"
|
msgid "1 min"
|
||||||
msgstr "1 min"
|
msgstr ""
|
||||||
|
|
||||||
#: src/lib/utils.ts
|
#: src/lib/utils.ts
|
||||||
msgid "1 week"
|
msgid "1 week"
|
||||||
@@ -59,7 +59,7 @@ msgstr "12 horas"
|
|||||||
#. Load average
|
#. Load average
|
||||||
#: src/components/charts/load-average-chart.tsx
|
#: src/components/charts/load-average-chart.tsx
|
||||||
msgid "15 min"
|
msgid "15 min"
|
||||||
msgstr "15 min"
|
msgstr ""
|
||||||
|
|
||||||
#: src/lib/utils.ts
|
#: src/lib/utils.ts
|
||||||
msgid "24 hours"
|
msgid "24 hours"
|
||||||
@@ -72,7 +72,7 @@ msgstr "30 dias"
|
|||||||
#. Load average
|
#. Load average
|
||||||
#: src/components/charts/load-average-chart.tsx
|
#: src/components/charts/load-average-chart.tsx
|
||||||
msgid "5 min"
|
msgid "5 min"
|
||||||
msgstr "5 min"
|
msgstr ""
|
||||||
|
|
||||||
#. Table column
|
#. Table column
|
||||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||||
@@ -112,7 +112,7 @@ msgstr "Ajustar opções de exibição para gráficos."
|
|||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
msgid "Admin"
|
msgid "Admin"
|
||||||
msgstr "Admin"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "Agent"
|
msgid "Agent"
|
||||||
@@ -176,7 +176,7 @@ msgstr "Utilização média de {0}"
|
|||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/navbar.tsx
|
#: src/components/navbar.tsx
|
||||||
msgid "Backups"
|
msgid "Backups"
|
||||||
msgstr "Backups"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
#: src/lib/alerts.ts
|
#: src/lib/alerts.ts
|
||||||
@@ -202,16 +202,16 @@ msgstr "Binário"
|
|||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "Bits (Kbps, Mbps, Gbps)"
|
msgid "Bits (Kbps, Mbps, Gbps)"
|
||||||
msgstr "Bits (Kbps, Mbps, Gbps)"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "Bytes (KB/s, MB/s, GB/s)"
|
msgid "Bytes (KB/s, MB/s, GB/s)"
|
||||||
msgstr "Bytes (KB/s, MB/s, GB/s)"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/charts/mem-chart.tsx
|
#: src/components/charts/mem-chart.tsx
|
||||||
msgid "Cache / Buffers"
|
msgid "Cache / Buffers"
|
||||||
msgstr "Cache / Buffers"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
@@ -224,7 +224,7 @@ msgstr "Cuidado - possível perda de dados"
|
|||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "Celsius (°C)"
|
msgid "Celsius (°C)"
|
||||||
msgstr "Celsius (°C)"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "Change display units for metrics."
|
msgid "Change display units for metrics."
|
||||||
@@ -342,7 +342,7 @@ msgstr "Copiar YAML"
|
|||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "CPU"
|
msgid "CPU"
|
||||||
msgstr "CPU"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -466,7 +466,7 @@ msgstr "Editar"
|
|||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
#: src/components/login/otp-forms.tsx
|
#: src/components/login/otp-forms.tsx
|
||||||
msgid "Email"
|
msgid "Email"
|
||||||
msgstr "Email"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/notifications.tsx
|
#: src/components/routes/settings/notifications.tsx
|
||||||
msgid "Email notifications"
|
msgid "Email notifications"
|
||||||
@@ -522,7 +522,7 @@ msgstr "Exporte a configuração atual dos seus sistemas."
|
|||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "Fahrenheit (°F)"
|
msgid "Fahrenheit (°F)"
|
||||||
msgstr "Fahrenheit (°F)"
|
msgstr ""
|
||||||
|
|
||||||
#: src/lib/api.ts
|
#: src/lib/api.ts
|
||||||
msgid "Failed to authenticate"
|
msgid "Failed to authenticate"
|
||||||
@@ -592,7 +592,7 @@ msgstr "Comando Homebrew"
|
|||||||
|
|
||||||
#: src/components/add-system.tsx
|
#: src/components/add-system.tsx
|
||||||
msgid "Host / IP"
|
msgid "Host / IP"
|
||||||
msgstr "Host / IP"
|
msgstr ""
|
||||||
|
|
||||||
#. Context: Battery state
|
#. Context: Battery state
|
||||||
#: src/lib/i18n.ts
|
#: src/lib/i18n.ts
|
||||||
@@ -610,7 +610,7 @@ msgstr "Endereço de email inválido."
|
|||||||
#. Linux kernel
|
#. Linux kernel
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "Kernel"
|
msgid "Kernel"
|
||||||
msgstr "Kernel"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "Language"
|
msgid "Language"
|
||||||
@@ -657,7 +657,7 @@ msgstr "Tentativa de login falhou"
|
|||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/navbar.tsx
|
#: src/components/navbar.tsx
|
||||||
msgid "Logs"
|
msgid "Logs"
|
||||||
msgstr "Logs"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/notifications.tsx
|
#: src/components/routes/settings/notifications.tsx
|
||||||
msgid "Looking instead for where to create alerts? Click the bell <0/> icons in the systems table."
|
msgid "Looking instead for where to create alerts? Click the bell <0/> icons in the systems table."
|
||||||
@@ -957,7 +957,7 @@ msgstr "Estado"
|
|||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
#: src/lib/alerts.ts
|
#: src/lib/alerts.ts
|
||||||
msgid "Status"
|
msgid "Status"
|
||||||
msgstr "Status"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "Swap space used by the system"
|
msgid "Swap space used by the system"
|
||||||
@@ -993,7 +993,7 @@ msgstr "Tabela"
|
|||||||
#. Temperature label in systems table
|
#. Temperature label in systems table
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "Temp"
|
msgid "Temp"
|
||||||
msgstr "Temp"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
#: src/lib/alerts.ts
|
#: src/lib/alerts.ts
|
||||||
@@ -1056,7 +1056,7 @@ msgstr "Alternar tema"
|
|||||||
#: src/components/add-system.tsx
|
#: src/components/add-system.tsx
|
||||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||||
msgid "Token"
|
msgid "Token"
|
||||||
msgstr "Token"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/settings/layout.tsx
|
#: src/components/routes/settings/layout.tsx
|
||||||
@@ -1233,3 +1233,4 @@ msgstr "Configuração YAML"
|
|||||||
#: src/components/routes/settings/layout.tsx
|
#: src/components/routes/settings/layout.tsx
|
||||||
msgid "Your user settings have been updated."
|
msgid "Your user settings have been updated."
|
||||||
msgstr "As configurações do seu usuário foram atualizadas."
|
msgstr "As configurações do seu usuário foram atualizadas."
|
||||||
|
|
||||||
|
|||||||
@@ -8,15 +8,15 @@ msgstr ""
|
|||||||
"Language: ru\n"
|
"Language: ru\n"
|
||||||
"Project-Id-Version: beszel\n"
|
"Project-Id-Version: beszel\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"PO-Revision-Date: 2025-08-28 23:21\n"
|
"PO-Revision-Date: 2025-09-17 18:45\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Russian\n"
|
"Language-Team: Russian\n"
|
||||||
"Plural-Forms: nplurals=4; plural=((n%10==1 && n%100!=11) ? 0 : ((n%10 >= 2 && n%10 <=4 && (n%100 < 12 || n%100 > 14)) ? 1 : ((n%10 == 0 || (n%10 >= 5 && n%10 <=9)) || (n%100 >= 11 && n%100 <= 14)) ? 2 : 3));\n"
|
"Plural-Forms: nplurals=4; plural=((n%10==1 && n%100!=11) ? 0 : ((n%10 >= 2 && n%10 <=4 && (n%100 < 12 || n%100 > 14)) ? 1 : ((n%10 == 0 || (n%10 >= 5 && n%10 <=9)) || (n%100 >= 11 && n%100 <= 14)) ? 2 : 3));\n"
|
||||||
"X-Crowdin-Project: beszel\n"
|
"X-Crowdin-Project: beszel\n"
|
||||||
"X-Crowdin-Project-ID: 733311\n"
|
"X-Crowdin-Project-ID: 733311\n"
|
||||||
"X-Crowdin-Language: ru\n"
|
"X-Crowdin-Language: ru\n"
|
||||||
"X-Crowdin-File: /main/beszel/site/src/locales/en/en.po\n"
|
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
|
||||||
"X-Crowdin-File-ID: 16\n"
|
"X-Crowdin-File-ID: 32\n"
|
||||||
|
|
||||||
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -342,7 +342,7 @@ msgstr "Скопировать YAML"
|
|||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "CPU"
|
msgid "CPU"
|
||||||
msgstr "CPU"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -1233,3 +1233,4 @@ msgstr "YAML конфигурация"
|
|||||||
#: src/components/routes/settings/layout.tsx
|
#: src/components/routes/settings/layout.tsx
|
||||||
msgid "Your user settings have been updated."
|
msgid "Your user settings have been updated."
|
||||||
msgstr "Ваши настройки пользователя были обновлены."
|
msgstr "Ваши настройки пользователя были обновлены."
|
||||||
|
|
||||||
|
|||||||
@@ -8,15 +8,15 @@ msgstr ""
|
|||||||
"Language: sl\n"
|
"Language: sl\n"
|
||||||
"Project-Id-Version: beszel\n"
|
"Project-Id-Version: beszel\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"PO-Revision-Date: 2025-08-28 23:21\n"
|
"PO-Revision-Date: 2025-09-17 18:45\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Slovenian\n"
|
"Language-Team: Slovenian\n"
|
||||||
"Plural-Forms: nplurals=4; plural=n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3;\n"
|
"Plural-Forms: nplurals=4; plural=n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3;\n"
|
||||||
"X-Crowdin-Project: beszel\n"
|
"X-Crowdin-Project: beszel\n"
|
||||||
"X-Crowdin-Project-ID: 733311\n"
|
"X-Crowdin-Project-ID: 733311\n"
|
||||||
"X-Crowdin-Language: sl\n"
|
"X-Crowdin-Language: sl\n"
|
||||||
"X-Crowdin-File: /main/beszel/site/src/locales/en/en.po\n"
|
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
|
||||||
"X-Crowdin-File-ID: 16\n"
|
"X-Crowdin-File-ID: 32\n"
|
||||||
|
|
||||||
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -116,7 +116,7 @@ msgstr "Administrator"
|
|||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "Agent"
|
msgid "Agent"
|
||||||
msgstr "Agent"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||||
@@ -342,7 +342,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "CPU"
|
msgid "CPU"
|
||||||
msgstr "CPU"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -401,11 +401,11 @@ msgstr "Prazni se"
|
|||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "Disk"
|
msgid "Disk"
|
||||||
msgstr "Disk"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "Disk I/O"
|
msgid "Disk I/O"
|
||||||
msgstr "Disk I/O"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "Disk unit"
|
msgid "Disk unit"
|
||||||
@@ -545,7 +545,7 @@ msgstr "Opozorila ni bilo mogoče posodobiti"
|
|||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "Filter..."
|
msgid "Filter..."
|
||||||
msgstr "Filter..."
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||||
msgid "Fingerprint"
|
msgid "Fingerprint"
|
||||||
@@ -957,7 +957,7 @@ msgstr ""
|
|||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
#: src/lib/alerts.ts
|
#: src/lib/alerts.ts
|
||||||
msgid "Status"
|
msgid "Status"
|
||||||
msgstr "Status"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "Swap space used by the system"
|
msgid "Swap space used by the system"
|
||||||
@@ -1233,3 +1233,4 @@ msgstr "YAML nastavitev"
|
|||||||
#: src/components/routes/settings/layout.tsx
|
#: src/components/routes/settings/layout.tsx
|
||||||
msgid "Your user settings have been updated."
|
msgid "Your user settings have been updated."
|
||||||
msgstr "Vaše uporabniške nastavitve so posodobljene."
|
msgstr "Vaše uporabniške nastavitve so posodobljene."
|
||||||
|
|
||||||
|
|||||||
@@ -8,15 +8,15 @@ msgstr ""
|
|||||||
"Language: sv\n"
|
"Language: sv\n"
|
||||||
"Project-Id-Version: beszel\n"
|
"Project-Id-Version: beszel\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"PO-Revision-Date: 2025-08-28 23:21\n"
|
"PO-Revision-Date: 2025-09-17 18:45\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Swedish\n"
|
"Language-Team: Swedish\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
"X-Crowdin-Project: beszel\n"
|
"X-Crowdin-Project: beszel\n"
|
||||||
"X-Crowdin-Project-ID: 733311\n"
|
"X-Crowdin-Project-ID: 733311\n"
|
||||||
"X-Crowdin-Language: sv-SE\n"
|
"X-Crowdin-Language: sv-SE\n"
|
||||||
"X-Crowdin-File: /main/beszel/site/src/locales/en/en.po\n"
|
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
|
||||||
"X-Crowdin-File-ID: 16\n"
|
"X-Crowdin-File-ID: 32\n"
|
||||||
|
|
||||||
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -46,7 +46,7 @@ msgstr "1 timme"
|
|||||||
#. Load average
|
#. Load average
|
||||||
#: src/components/charts/load-average-chart.tsx
|
#: src/components/charts/load-average-chart.tsx
|
||||||
msgid "1 min"
|
msgid "1 min"
|
||||||
msgstr "1 min"
|
msgstr ""
|
||||||
|
|
||||||
#: src/lib/utils.ts
|
#: src/lib/utils.ts
|
||||||
msgid "1 week"
|
msgid "1 week"
|
||||||
@@ -59,7 +59,7 @@ msgstr "12 timmar"
|
|||||||
#. Load average
|
#. Load average
|
||||||
#: src/components/charts/load-average-chart.tsx
|
#: src/components/charts/load-average-chart.tsx
|
||||||
msgid "15 min"
|
msgid "15 min"
|
||||||
msgstr "15 min"
|
msgstr ""
|
||||||
|
|
||||||
#: src/lib/utils.ts
|
#: src/lib/utils.ts
|
||||||
msgid "24 hours"
|
msgid "24 hours"
|
||||||
@@ -72,7 +72,7 @@ msgstr "30 dagar"
|
|||||||
#. Load average
|
#. Load average
|
||||||
#: src/components/charts/load-average-chart.tsx
|
#: src/components/charts/load-average-chart.tsx
|
||||||
msgid "5 min"
|
msgid "5 min"
|
||||||
msgstr "5 min"
|
msgstr ""
|
||||||
|
|
||||||
#. Table column
|
#. Table column
|
||||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||||
@@ -112,11 +112,11 @@ msgstr "Justera visningsalternativ för diagram."
|
|||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
msgid "Admin"
|
msgid "Admin"
|
||||||
msgstr "Admin"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "Agent"
|
msgid "Agent"
|
||||||
msgstr "Agent"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||||
@@ -202,7 +202,7 @@ msgstr "Binär"
|
|||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "Bits (Kbps, Mbps, Gbps)"
|
msgid "Bits (Kbps, Mbps, Gbps)"
|
||||||
msgstr "Bits (Kbps, Mbps, Gbps)"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
@@ -224,7 +224,7 @@ msgstr "Varning - potentiell dataförlust"
|
|||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "Celsius (°C)"
|
msgid "Celsius (°C)"
|
||||||
msgstr "Celsius (°C)"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "Change display units for metrics."
|
msgid "Change display units for metrics."
|
||||||
@@ -342,7 +342,7 @@ msgstr "Kopiera YAML"
|
|||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "CPU"
|
msgid "CPU"
|
||||||
msgstr "CPU"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -379,7 +379,7 @@ msgstr "Aktuellt tillstånd"
|
|||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/home.tsx
|
#: src/components/routes/home.tsx
|
||||||
msgid "Dashboard"
|
msgid "Dashboard"
|
||||||
msgstr "Dashboard"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "Default time period"
|
msgid "Default time period"
|
||||||
@@ -401,11 +401,11 @@ msgstr "Urladdar"
|
|||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "Disk"
|
msgid "Disk"
|
||||||
msgstr "Disk"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "Disk I/O"
|
msgid "Disk I/O"
|
||||||
msgstr "Disk I/O"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "Disk unit"
|
msgid "Disk unit"
|
||||||
@@ -568,7 +568,7 @@ msgstr "FreeBSD kommando"
|
|||||||
#. Context: Battery state
|
#. Context: Battery state
|
||||||
#: src/lib/i18n.ts
|
#: src/lib/i18n.ts
|
||||||
msgid "Full"
|
msgid "Full"
|
||||||
msgstr "Full"
|
msgstr ""
|
||||||
|
|
||||||
#. Context: General settings
|
#. Context: General settings
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
@@ -618,7 +618,7 @@ msgstr "Språk"
|
|||||||
|
|
||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
msgid "Layout"
|
msgid "Layout"
|
||||||
msgstr "Layout"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "Load Average"
|
msgid "Load Average"
|
||||||
@@ -675,7 +675,7 @@ msgstr ""
|
|||||||
#. Chart select field. Please try to keep this short.
|
#. Chart select field. Please try to keep this short.
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "Max 1 min"
|
msgid "Max 1 min"
|
||||||
msgstr "Max 1 min"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "Memory"
|
msgid "Memory"
|
||||||
@@ -837,7 +837,7 @@ msgstr "Vänligen logga in på ditt konto"
|
|||||||
|
|
||||||
#: src/components/add-system.tsx
|
#: src/components/add-system.tsx
|
||||||
msgid "Port"
|
msgid "Port"
|
||||||
msgstr "Port"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -957,7 +957,7 @@ msgstr ""
|
|||||||
#: src/components/systems-table/systems-table.tsx
|
#: src/components/systems-table/systems-table.tsx
|
||||||
#: src/lib/alerts.ts
|
#: src/lib/alerts.ts
|
||||||
msgid "Status"
|
msgid "Status"
|
||||||
msgstr "Status"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "Swap space used by the system"
|
msgid "Swap space used by the system"
|
||||||
@@ -972,7 +972,7 @@ msgstr "Swap-användning"
|
|||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
#: src/lib/alerts.ts
|
#: src/lib/alerts.ts
|
||||||
msgid "System"
|
msgid "System"
|
||||||
msgstr "System"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "System load averages over time"
|
msgid "System load averages over time"
|
||||||
@@ -1233,3 +1233,4 @@ msgstr "YAML-konfiguration"
|
|||||||
#: src/components/routes/settings/layout.tsx
|
#: src/components/routes/settings/layout.tsx
|
||||||
msgid "Your user settings have been updated."
|
msgid "Your user settings have been updated."
|
||||||
msgstr "Dina användarinställningar har uppdaterats."
|
msgstr "Dina användarinställningar har uppdaterats."
|
||||||
|
|
||||||
|
|||||||
@@ -8,15 +8,15 @@ msgstr ""
|
|||||||
"Language: tr\n"
|
"Language: tr\n"
|
||||||
"Project-Id-Version: beszel\n"
|
"Project-Id-Version: beszel\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"PO-Revision-Date: 2025-08-28 23:21\n"
|
"PO-Revision-Date: 2025-09-17 18:45\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Turkish\n"
|
"Language-Team: Turkish\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
"X-Crowdin-Project: beszel\n"
|
"X-Crowdin-Project: beszel\n"
|
||||||
"X-Crowdin-Project-ID: 733311\n"
|
"X-Crowdin-Project-ID: 733311\n"
|
||||||
"X-Crowdin-Language: tr\n"
|
"X-Crowdin-Language: tr\n"
|
||||||
"X-Crowdin-File: /main/beszel/site/src/locales/en/en.po\n"
|
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
|
||||||
"X-Crowdin-File-ID: 16\n"
|
"X-Crowdin-File-ID: 32\n"
|
||||||
|
|
||||||
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -342,7 +342,7 @@ msgstr "YAML'ı kopyala"
|
|||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "CPU"
|
msgid "CPU"
|
||||||
msgstr "CPU"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -401,7 +401,7 @@ msgstr "Boşalıyor"
|
|||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "Disk"
|
msgid "Disk"
|
||||||
msgstr "Disk"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "Disk I/O"
|
msgid "Disk I/O"
|
||||||
@@ -592,7 +592,7 @@ msgstr "Homebrew komutu"
|
|||||||
|
|
||||||
#: src/components/add-system.tsx
|
#: src/components/add-system.tsx
|
||||||
msgid "Host / IP"
|
msgid "Host / IP"
|
||||||
msgstr "Host / IP"
|
msgstr ""
|
||||||
|
|
||||||
#. Context: Battery state
|
#. Context: Battery state
|
||||||
#: src/lib/i18n.ts
|
#: src/lib/i18n.ts
|
||||||
@@ -837,7 +837,7 @@ msgstr "Lütfen hesabınıza giriş yapın"
|
|||||||
|
|
||||||
#: src/components/add-system.tsx
|
#: src/components/add-system.tsx
|
||||||
msgid "Port"
|
msgid "Port"
|
||||||
msgstr "Port"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -1010,7 +1010,7 @@ msgstr "Sistem sensörlerinin sıcaklıkları"
|
|||||||
|
|
||||||
#: src/components/routes/settings/notifications.tsx
|
#: src/components/routes/settings/notifications.tsx
|
||||||
msgid "Test <0>URL</0>"
|
msgid "Test <0>URL</0>"
|
||||||
msgstr "Test <0>URL</0>"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/notifications.tsx
|
#: src/components/routes/settings/notifications.tsx
|
||||||
msgid "Test notification sent"
|
msgid "Test notification sent"
|
||||||
@@ -1056,7 +1056,7 @@ msgstr "Temayı değiştir"
|
|||||||
#: src/components/add-system.tsx
|
#: src/components/add-system.tsx
|
||||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||||
msgid "Token"
|
msgid "Token"
|
||||||
msgstr "Token"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/settings/layout.tsx
|
#: src/components/routes/settings/layout.tsx
|
||||||
@@ -1233,3 +1233,4 @@ msgstr "YAML Yapılandırması"
|
|||||||
#: src/components/routes/settings/layout.tsx
|
#: src/components/routes/settings/layout.tsx
|
||||||
msgid "Your user settings have been updated."
|
msgid "Your user settings have been updated."
|
||||||
msgstr "Kullanıcı ayarlarınız güncellendi."
|
msgstr "Kullanıcı ayarlarınız güncellendi."
|
||||||
|
|
||||||
|
|||||||
@@ -8,15 +8,15 @@ msgstr ""
|
|||||||
"Language: uk\n"
|
"Language: uk\n"
|
||||||
"Project-Id-Version: beszel\n"
|
"Project-Id-Version: beszel\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"PO-Revision-Date: 2025-08-30 16:20\n"
|
"PO-Revision-Date: 2025-09-17 18:45\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Ukrainian\n"
|
"Language-Team: Ukrainian\n"
|
||||||
"Plural-Forms: nplurals=4; plural=((n%10==1 && n%100!=11) ? 0 : ((n%10 >= 2 && n%10 <=4 && (n%100 < 12 || n%100 > 14)) ? 1 : ((n%10 == 0 || (n%10 >= 5 && n%10 <=9)) || (n%100 >= 11 && n%100 <= 14)) ? 2 : 3));\n"
|
"Plural-Forms: nplurals=4; plural=((n%10==1 && n%100!=11) ? 0 : ((n%10 >= 2 && n%10 <=4 && (n%100 < 12 || n%100 > 14)) ? 1 : ((n%10 == 0 || (n%10 >= 5 && n%10 <=9)) || (n%100 >= 11 && n%100 <= 14)) ? 2 : 3));\n"
|
||||||
"X-Crowdin-Project: beszel\n"
|
"X-Crowdin-Project: beszel\n"
|
||||||
"X-Crowdin-Project-ID: 733311\n"
|
"X-Crowdin-Project-ID: 733311\n"
|
||||||
"X-Crowdin-Language: uk\n"
|
"X-Crowdin-Language: uk\n"
|
||||||
"X-Crowdin-File: /main/beszel/site/src/locales/en/en.po\n"
|
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
|
||||||
"X-Crowdin-File-ID: 16\n"
|
"X-Crowdin-File-ID: 32\n"
|
||||||
|
|
||||||
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -1233,3 +1233,4 @@ msgstr "Конфігурація YAML"
|
|||||||
#: src/components/routes/settings/layout.tsx
|
#: src/components/routes/settings/layout.tsx
|
||||||
msgid "Your user settings have been updated."
|
msgid "Your user settings have been updated."
|
||||||
msgstr "Ваші налаштування користувача були оновлені."
|
msgstr "Ваші налаштування користувача були оновлені."
|
||||||
|
|
||||||
|
|||||||
@@ -8,15 +8,15 @@ msgstr ""
|
|||||||
"Language: vi\n"
|
"Language: vi\n"
|
||||||
"Project-Id-Version: beszel\n"
|
"Project-Id-Version: beszel\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"PO-Revision-Date: 2025-08-28 23:21\n"
|
"PO-Revision-Date: 2025-09-17 18:45\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Vietnamese\n"
|
"Language-Team: Vietnamese\n"
|
||||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||||
"X-Crowdin-Project: beszel\n"
|
"X-Crowdin-Project: beszel\n"
|
||||||
"X-Crowdin-Project-ID: 733311\n"
|
"X-Crowdin-Project-ID: 733311\n"
|
||||||
"X-Crowdin-Language: vi\n"
|
"X-Crowdin-Language: vi\n"
|
||||||
"X-Crowdin-File: /main/beszel/site/src/locales/en/en.po\n"
|
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
|
||||||
"X-Crowdin-File-ID: 16\n"
|
"X-Crowdin-File-ID: 32\n"
|
||||||
|
|
||||||
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -342,7 +342,7 @@ msgstr "Sao chép YAML"
|
|||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "CPU"
|
msgid "CPU"
|
||||||
msgstr "CPU"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -466,7 +466,7 @@ msgstr "Chỉnh sửa"
|
|||||||
#: src/components/login/forgot-pass-form.tsx
|
#: src/components/login/forgot-pass-form.tsx
|
||||||
#: src/components/login/otp-forms.tsx
|
#: src/components/login/otp-forms.tsx
|
||||||
msgid "Email"
|
msgid "Email"
|
||||||
msgstr "Email"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/notifications.tsx
|
#: src/components/routes/settings/notifications.tsx
|
||||||
msgid "Email notifications"
|
msgid "Email notifications"
|
||||||
@@ -1056,7 +1056,7 @@ msgstr "Chuyển đổi chủ đề"
|
|||||||
#: src/components/add-system.tsx
|
#: src/components/add-system.tsx
|
||||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||||
msgid "Token"
|
msgid "Token"
|
||||||
msgstr "Token"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/command-palette.tsx
|
#: src/components/command-palette.tsx
|
||||||
#: src/components/routes/settings/layout.tsx
|
#: src/components/routes/settings/layout.tsx
|
||||||
@@ -1233,3 +1233,4 @@ msgstr "Cấu hình YAML"
|
|||||||
#: src/components/routes/settings/layout.tsx
|
#: src/components/routes/settings/layout.tsx
|
||||||
msgid "Your user settings have been updated."
|
msgid "Your user settings have been updated."
|
||||||
msgstr "Cài đặt người dùng của bạn đã được cập nhật."
|
msgstr "Cài đặt người dùng của bạn đã được cập nhật."
|
||||||
|
|
||||||
|
|||||||
@@ -8,15 +8,15 @@ msgstr ""
|
|||||||
"Language: zh\n"
|
"Language: zh\n"
|
||||||
"Project-Id-Version: beszel\n"
|
"Project-Id-Version: beszel\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"PO-Revision-Date: 2025-09-05 08:15\n"
|
"PO-Revision-Date: 2025-09-17 18:45\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Chinese Simplified\n"
|
"Language-Team: Chinese Simplified\n"
|
||||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||||
"X-Crowdin-Project: beszel\n"
|
"X-Crowdin-Project: beszel\n"
|
||||||
"X-Crowdin-Project-ID: 733311\n"
|
"X-Crowdin-Project-ID: 733311\n"
|
||||||
"X-Crowdin-Language: zh-CN\n"
|
"X-Crowdin-Language: zh-CN\n"
|
||||||
"X-Crowdin-File: /main/beszel/site/src/locales/en/en.po\n"
|
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
|
||||||
"X-Crowdin-File-ID: 16\n"
|
"X-Crowdin-File-ID: 32\n"
|
||||||
|
|
||||||
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -1233,3 +1233,4 @@ msgstr "YAML 配置"
|
|||||||
#: src/components/routes/settings/layout.tsx
|
#: src/components/routes/settings/layout.tsx
|
||||||
msgid "Your user settings have been updated."
|
msgid "Your user settings have been updated."
|
||||||
msgstr "您的用户设置已更新。"
|
msgstr "您的用户设置已更新。"
|
||||||
|
|
||||||
|
|||||||
@@ -8,15 +8,15 @@ msgstr ""
|
|||||||
"Language: zh\n"
|
"Language: zh\n"
|
||||||
"Project-Id-Version: beszel\n"
|
"Project-Id-Version: beszel\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"PO-Revision-Date: 2025-08-28 23:21\n"
|
"PO-Revision-Date: 2025-09-17 18:45\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Chinese Traditional, Hong Kong\n"
|
"Language-Team: Chinese Traditional, Hong Kong\n"
|
||||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||||
"X-Crowdin-Project: beszel\n"
|
"X-Crowdin-Project: beszel\n"
|
||||||
"X-Crowdin-Project-ID: 733311\n"
|
"X-Crowdin-Project-ID: 733311\n"
|
||||||
"X-Crowdin-Language: zh-HK\n"
|
"X-Crowdin-Language: zh-HK\n"
|
||||||
"X-Crowdin-File: /main/beszel/site/src/locales/en/en.po\n"
|
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
|
||||||
"X-Crowdin-File-ID: 16\n"
|
"X-Crowdin-File-ID: 32\n"
|
||||||
|
|
||||||
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -342,7 +342,7 @@ msgstr "複製YAML"
|
|||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "CPU"
|
msgid "CPU"
|
||||||
msgstr "CPU"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -610,7 +610,7 @@ msgstr "無效的電子郵件地址。"
|
|||||||
#. Linux kernel
|
#. Linux kernel
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "Kernel"
|
msgid "Kernel"
|
||||||
msgstr "Kernel"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "Language"
|
msgid "Language"
|
||||||
@@ -1233,3 +1233,4 @@ msgstr "YAML配置"
|
|||||||
#: src/components/routes/settings/layout.tsx
|
#: src/components/routes/settings/layout.tsx
|
||||||
msgid "Your user settings have been updated."
|
msgid "Your user settings have been updated."
|
||||||
msgstr "您的用戶設置已更新。"
|
msgstr "您的用戶設置已更新。"
|
||||||
|
|
||||||
|
|||||||
@@ -8,15 +8,15 @@ msgstr ""
|
|||||||
"Language: zh\n"
|
"Language: zh\n"
|
||||||
"Project-Id-Version: beszel\n"
|
"Project-Id-Version: beszel\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"PO-Revision-Date: 2025-08-28 23:21\n"
|
"PO-Revision-Date: 2025-09-17 18:45\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Chinese Traditional\n"
|
"Language-Team: Chinese Traditional\n"
|
||||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||||
"X-Crowdin-Project: beszel\n"
|
"X-Crowdin-Project: beszel\n"
|
||||||
"X-Crowdin-Project-ID: 733311\n"
|
"X-Crowdin-Project-ID: 733311\n"
|
||||||
"X-Crowdin-Language: zh-TW\n"
|
"X-Crowdin-Language: zh-TW\n"
|
||||||
"X-Crowdin-File: /main/beszel/site/src/locales/en/en.po\n"
|
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
|
||||||
"X-Crowdin-File-ID: 16\n"
|
"X-Crowdin-File-ID: 32\n"
|
||||||
|
|
||||||
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -342,7 +342,7 @@ msgstr "複製YAML"
|
|||||||
|
|
||||||
#: src/components/systems-table/systems-table-columns.tsx
|
#: src/components/systems-table/systems-table-columns.tsx
|
||||||
msgid "CPU"
|
msgid "CPU"
|
||||||
msgstr "CPU"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -592,7 +592,7 @@ msgstr "Homebrew 指令"
|
|||||||
|
|
||||||
#: src/components/add-system.tsx
|
#: src/components/add-system.tsx
|
||||||
msgid "Host / IP"
|
msgid "Host / IP"
|
||||||
msgstr "Host / IP"
|
msgstr ""
|
||||||
|
|
||||||
#. Context: Battery state
|
#. Context: Battery state
|
||||||
#: src/lib/i18n.ts
|
#: src/lib/i18n.ts
|
||||||
@@ -610,7 +610,7 @@ msgstr "無效的電子郵件地址。"
|
|||||||
#. Linux kernel
|
#. Linux kernel
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
msgid "Kernel"
|
msgid "Kernel"
|
||||||
msgstr "Kernel"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/settings/general.tsx
|
#: src/components/routes/settings/general.tsx
|
||||||
msgid "Language"
|
msgid "Language"
|
||||||
@@ -837,7 +837,7 @@ msgstr "請登入您的帳號"
|
|||||||
|
|
||||||
#: src/components/add-system.tsx
|
#: src/components/add-system.tsx
|
||||||
msgid "Port"
|
msgid "Port"
|
||||||
msgstr "Port"
|
msgstr ""
|
||||||
|
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
#: src/components/routes/system.tsx
|
#: src/components/routes/system.tsx
|
||||||
@@ -1233,3 +1233,4 @@ msgstr "YAML 設定檔"
|
|||||||
#: src/components/routes/settings/layout.tsx
|
#: src/components/routes/settings/layout.tsx
|
||||||
msgid "Your user settings have been updated."
|
msgid "Your user settings have been updated."
|
||||||
msgstr "已更新您的使用者設定"
|
msgstr "已更新您的使用者設定"
|
||||||
|
|
||||||
|
|||||||
8
internal/site/src/types.d.ts
vendored
8
internal/site/src/types.d.ts
vendored
@@ -1,5 +1,5 @@
|
|||||||
import type { RecordModel } from "pocketbase"
|
import type { RecordModel } from "pocketbase"
|
||||||
import type { Unit, Os, BatteryState, HourFormat, ConnectionType } from "@/lib/enums"
|
import type { Unit, Os, BatteryState, HourFormat } from "./lib/enums"
|
||||||
|
|
||||||
// global window properties
|
// global window properties
|
||||||
declare global {
|
declare global {
|
||||||
@@ -75,8 +75,6 @@ export interface SystemInfo {
|
|||||||
dt?: number
|
dt?: number
|
||||||
/** operating system */
|
/** operating system */
|
||||||
os?: Os
|
os?: Os
|
||||||
/** connection type */
|
|
||||||
ct?: ConnectionType
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SystemStats {
|
export interface SystemStats {
|
||||||
@@ -143,8 +141,6 @@ export interface SystemStats {
|
|||||||
g?: Record<string, GPUData>
|
g?: Record<string, GPUData>
|
||||||
/** battery percent and state */
|
/** battery percent and state */
|
||||||
bat?: [number, BatteryState]
|
bat?: [number, BatteryState]
|
||||||
/** network interfaces [upload bytes, download bytes, total upload bytes, total download bytes] */
|
|
||||||
ni?: Record<string, [number, number, number, number]>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GPUData {
|
export interface GPUData {
|
||||||
@@ -158,8 +154,6 @@ export interface GPUData {
|
|||||||
u: number
|
u: number
|
||||||
/** power (w) */
|
/** power (w) */
|
||||||
p?: number
|
p?: number
|
||||||
/** engines */
|
|
||||||
e?: Record<string, number>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ExtraFsStats {
|
export interface ExtraFsStats {
|
||||||
|
|||||||
@@ -125,28 +125,3 @@ func CreateSystems(app core.App, count int, userId string, status string) ([]*co
|
|||||||
}
|
}
|
||||||
return systems, nil
|
return systems, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetHubWithUser creates a test hub with a test user and user settings
|
|
||||||
func GetHubWithUser(t *testing.T) (*TestHub, *core.Record) {
|
|
||||||
hub, err := NewTestHub(t.TempDir())
|
|
||||||
assert.NoError(t, err)
|
|
||||||
hub.StartHub()
|
|
||||||
|
|
||||||
// Manually initialize the system manager to bind event hooks
|
|
||||||
err = hub.GetSystemManager().Initialize()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
// Create a test user
|
|
||||||
user, err := CreateUser(hub, "test@example.com", "password")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
// Create user settings for the test user (required for alert notifications)
|
|
||||||
userSettingsData := map[string]any{
|
|
||||||
"user": user.Id,
|
|
||||||
"settings": `{"emails":[test@example.com],"webhooks":[]}`,
|
|
||||||
}
|
|
||||||
_, err = CreateRecord(hub, "user_settings", userSettingsData)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
return hub, user
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,32 +1,7 @@
|
|||||||
## 0.12.10
|
|
||||||
|
|
||||||
- Show connection type (WebSocket / SSH) in hub UI.
|
|
||||||
|
|
||||||
- Fix temperature unit and bytes / bits settings. (#1180)
|
|
||||||
|
|
||||||
## 0.12.9
|
|
||||||
|
|
||||||
- Fix divide by zero error introduced in 0.12.8 :) (#1175)
|
|
||||||
|
|
||||||
|
|
||||||
## 0.12.8
|
## 0.12.8
|
||||||
|
|
||||||
- Add per-interface network traffic charts. (#926)
|
|
||||||
|
|
||||||
- Add cumulative network traffic charts. (#926)
|
|
||||||
|
|
||||||
- Add setting for time format (12h / 24h). (#424)
|
|
||||||
|
|
||||||
- Add experimental one-time password (OTP) support.
|
|
||||||
|
|
||||||
- Add `TRUSTED_AUTH_HEADER` environment variable for authentication forwarding. (#399)
|
|
||||||
|
|
||||||
- Add `AUTO_LOGIN` environment variable for automatic login. (#399)
|
|
||||||
|
|
||||||
- Add FreeBSD support for agent install script and update command.
|
- Add FreeBSD support for agent install script and update command.
|
||||||
|
|
||||||
- Fix status alerts not being resolved when system comes up. (#1052)
|
|
||||||
|
|
||||||
## 0.12.7
|
## 0.12.7
|
||||||
|
|
||||||
- Make LibreHardwareMonitor opt-in with `LHM=true` environment variable. (#1130)
|
- Make LibreHardwareMonitor opt-in with `LHM=true` environment variable. (#1130)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: Service
|
kind: Service
|
||||||
metadata:
|
metadata:
|
||||||
name: {{ include "beszel.fullname" . }}
|
name: {{ include "beszel.fullname" . }}-web
|
||||||
labels:
|
labels:
|
||||||
{{- include "beszel.labels" . | nindent 4 }}
|
{{- include "beszel.labels" . | nindent 4 }}
|
||||||
{{- if .Values.service.annotations }}
|
{{- if .Values.service.annotations }}
|
||||||
|
|||||||
@@ -30,10 +30,14 @@ securityContext: {}
|
|||||||
|
|
||||||
service:
|
service:
|
||||||
enabled: true
|
enabled: true
|
||||||
annotations: {}
|
type: LoadBalancer
|
||||||
type: ClusterIP
|
loadBalancerIP: "10.0.10.251"
|
||||||
loadBalancerIP: ""
|
|
||||||
port: 8090
|
port: 8090
|
||||||
|
# -- Annotations for the DHCP service
|
||||||
|
annotations:
|
||||||
|
metallb.universe.tf/address-pool: pool
|
||||||
|
metallb.universe.tf/allow-shared-ip: beszel-hub-web
|
||||||
|
# -- Labels for the DHCP service
|
||||||
|
|
||||||
ingress:
|
ingress:
|
||||||
enabled: false
|
enabled: false
|
||||||
@@ -92,7 +96,7 @@ persistentVolumeClaim:
|
|||||||
accessModes:
|
accessModes:
|
||||||
- ReadWriteOnce
|
- ReadWriteOnce
|
||||||
|
|
||||||
storageClass: ""
|
storageClass: "retain-local-path"
|
||||||
|
|
||||||
# -- volume claim size
|
# -- volume claim size
|
||||||
size: "500Mi"
|
size: "500Mi"
|
||||||
|
|||||||
@@ -161,53 +161,6 @@ run_rc_command "$1"
|
|||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
# Detect system architecture
|
|
||||||
detect_architecture() {
|
|
||||||
local arch=$(uname -m)
|
|
||||||
|
|
||||||
if [ "$arch" = "mips" ]; then
|
|
||||||
detect_mips_endianness
|
|
||||||
return $?
|
|
||||||
fi
|
|
||||||
|
|
||||||
case "$arch" in
|
|
||||||
x86_64)
|
|
||||||
arch="amd64"
|
|
||||||
;;
|
|
||||||
armv6l|armv7l)
|
|
||||||
arch="arm"
|
|
||||||
;;
|
|
||||||
aarch64)
|
|
||||||
arch="arm64"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
echo "$arch"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Detect MIPS endianness using ELF header
|
|
||||||
detect_mips_endianness() {
|
|
||||||
local bins="/bin/sh /bin/ls /usr/bin/env"
|
|
||||||
local bin_to_check endian
|
|
||||||
|
|
||||||
for bin_to_check in $bins; do
|
|
||||||
if [ -f "$bin_to_check" ]; then
|
|
||||||
# The 6th byte in ELF header: 01 = little, 02 = big
|
|
||||||
endian=$(hexdump -n 1 -s 5 -e '1/1 "%02x"' "$bin_to_check" 2>/dev/null)
|
|
||||||
if [ "$endian" = "01" ]; then
|
|
||||||
echo "mipsle"
|
|
||||||
return
|
|
||||||
elif [ "$endian" = "02" ]; then
|
|
||||||
echo "mips"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# Final fallback
|
|
||||||
echo "mips"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Default values
|
# Default values
|
||||||
PORT=45876
|
PORT=45876
|
||||||
UNINSTALL=false
|
UNINSTALL=false
|
||||||
@@ -603,7 +556,7 @@ fi
|
|||||||
echo "Downloading and installing the agent..."
|
echo "Downloading and installing the agent..."
|
||||||
|
|
||||||
OS=$(uname -s | sed -e 'y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/')
|
OS=$(uname -s | sed -e 'y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/')
|
||||||
ARCH=$(detect_architecture)
|
ARCH=$(uname -m | sed -e 's/x86_64/amd64/' -e 's/armv6l/arm/' -e 's/armv7l/arm/' -e 's/aarch64/arm64/')
|
||||||
FILE_NAME="beszel-agent_${OS}_${ARCH}.tar.gz"
|
FILE_NAME="beszel-agent_${OS}_${ARCH}.tar.gz"
|
||||||
|
|
||||||
# Determine version to install
|
# Determine version to install
|
||||||
@@ -785,7 +738,9 @@ EXTRA_HELP=" update Update the Beszel agent
|
|||||||
restart Restart the Beszel agent"
|
restart Restart the Beszel agent"
|
||||||
|
|
||||||
update() {
|
update() {
|
||||||
$BIN_PATH update
|
if $BIN_PATH update | grep -q "Update completed successfully"; then
|
||||||
|
/etc/init.d/beszel-agent restart
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
EOF
|
EOF
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ fi
|
|||||||
version=0.0.1
|
version=0.0.1
|
||||||
PORT=8090 # Default port
|
PORT=8090 # Default port
|
||||||
GITHUB_PROXY_URL="https://ghfast.top/" # Default proxy URL
|
GITHUB_PROXY_URL="https://ghfast.top/" # Default proxy URL
|
||||||
AUTO_UPDATE_FLAG="false" # default to no auto-updates, "true" means enable
|
|
||||||
|
|
||||||
# Function to ensure the proxy URL ends with a /
|
# Function to ensure the proxy URL ends with a /
|
||||||
ensure_trailing_slash() {
|
ensure_trailing_slash() {
|
||||||
@@ -33,42 +32,26 @@ ensure_trailing_slash() {
|
|||||||
# Ensure the proxy URL ends with a /
|
# Ensure the proxy URL ends with a /
|
||||||
GITHUB_PROXY_URL=$(ensure_trailing_slash "$GITHUB_PROXY_URL")
|
GITHUB_PROXY_URL=$(ensure_trailing_slash "$GITHUB_PROXY_URL")
|
||||||
|
|
||||||
# Parse command line arguments
|
# Read command line options
|
||||||
while [ $# -gt 0 ]; do
|
while getopts ":uhp:c:" opt; do
|
||||||
case "$1" in
|
case $opt in
|
||||||
-u)
|
u) UNINSTALL="true" ;;
|
||||||
UNINSTALL="true"
|
h)
|
||||||
shift
|
printf "Beszel Hub installation script\n\n"
|
||||||
;;
|
printf "Usage: ./install-hub.sh [options]\n\n"
|
||||||
-h|--help)
|
printf "Options: \n"
|
||||||
printf "Beszel Hub installation script\n\n"
|
printf " -u : Uninstall the Beszel Hub\n"
|
||||||
printf "Usage: ./install-hub.sh [options]\n\n"
|
printf " -p <port> : Specify a port number (default: 8090)\n"
|
||||||
printf "Options: \n"
|
printf " -c <url> : Use a custom GitHub mirror URL (e.g., https://ghfast.top/)\n"
|
||||||
printf " -u : Uninstall the Beszel Hub\n"
|
echo " -h : Display this help message"
|
||||||
printf " -p <port> : Specify a port number (default: 8090)\n"
|
exit 0
|
||||||
printf " -c <url> : Use a custom GitHub mirror URL (e.g., https://ghfast.top/)\n"
|
;;
|
||||||
printf " --auto-update : Enable automatic daily updates (disabled by default)\n"
|
p) PORT=$OPTARG ;;
|
||||||
printf " -h, --help : Display this help message\n"
|
c) GITHUB_PROXY_URL=$(ensure_trailing_slash "$OPTARG") ;;
|
||||||
exit 0
|
\?)
|
||||||
;;
|
echo "Invalid option: -$OPTARG"
|
||||||
-p)
|
exit 1
|
||||||
shift
|
;;
|
||||||
PORT="$1"
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
-c)
|
|
||||||
shift
|
|
||||||
GITHUB_PROXY_URL=$(ensure_trailing_slash "$1")
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
--auto-update)
|
|
||||||
AUTO_UPDATE_FLAG="true"
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "Invalid option: $1" >&2
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
@@ -80,14 +63,7 @@ if [ "$UNINSTALL" = "true" ]; then
|
|||||||
|
|
||||||
# Remove the systemd service file
|
# Remove the systemd service file
|
||||||
echo "Removing the systemd service file..."
|
echo "Removing the systemd service file..."
|
||||||
rm -f /etc/systemd/system/beszel-hub.service
|
rm /etc/systemd/system/beszel-hub.service
|
||||||
|
|
||||||
# Remove the update timer and service if they exist
|
|
||||||
echo "Removing the daily update service and timer..."
|
|
||||||
systemctl stop beszel-hub-update.timer 2>/dev/null
|
|
||||||
systemctl disable beszel-hub-update.timer 2>/dev/null
|
|
||||||
rm -f /etc/systemd/system/beszel-hub-update.service
|
|
||||||
rm -f /etc/systemd/system/beszel-hub-update.timer
|
|
||||||
|
|
||||||
# Reload the systemd daemon
|
# Reload the systemd daemon
|
||||||
echo "Reloading the systemd daemon..."
|
echo "Reloading the systemd daemon..."
|
||||||
@@ -99,7 +75,7 @@ if [ "$UNINSTALL" = "true" ]; then
|
|||||||
|
|
||||||
# Remove the dedicated user
|
# Remove the dedicated user
|
||||||
echo "Removing the dedicated user..."
|
echo "Removing the dedicated user..."
|
||||||
userdel beszel 2>/dev/null
|
userdel beszel
|
||||||
|
|
||||||
echo "The Beszel Hub has been uninstalled successfully!"
|
echo "The Beszel Hub has been uninstalled successfully!"
|
||||||
exit 0
|
exit 0
|
||||||
@@ -175,39 +151,4 @@ if [ "$(systemctl is-active beszel-hub.service)" != "active" ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Enable auto-update if flag is set to true
|
|
||||||
if [ "$AUTO_UPDATE_FLAG" = "true" ]; then
|
|
||||||
echo "Setting up daily automatic updates for beszel-hub..."
|
|
||||||
|
|
||||||
# Create systemd service for the daily update
|
|
||||||
cat >/etc/systemd/system/beszel-hub-update.service <<EOF
|
|
||||||
[Unit]
|
|
||||||
Description=Update beszel-hub if needed
|
|
||||||
Wants=beszel-hub.service
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=oneshot
|
|
||||||
ExecStart=/opt/beszel/beszel update
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# Create systemd timer for the daily update
|
|
||||||
cat >/etc/systemd/system/beszel-hub-update.timer <<EOF
|
|
||||||
[Unit]
|
|
||||||
Description=Run beszel-hub update daily
|
|
||||||
|
|
||||||
[Timer]
|
|
||||||
OnCalendar=daily
|
|
||||||
Persistent=true
|
|
||||||
RandomizedDelaySec=4h
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=timers.target
|
|
||||||
EOF
|
|
||||||
|
|
||||||
systemctl daemon-reload
|
|
||||||
systemctl enable --now beszel-hub-update.timer
|
|
||||||
|
|
||||||
printf "\nDaily updates have been enabled.\n"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "The Beszel Hub has been installed and configured successfully! It is now accessible on port $PORT."
|
echo "The Beszel Hub has been installed and configured successfully! It is now accessible on port $PORT."
|
||||||
|
|||||||
Reference in New Issue
Block a user