mirror of
https://github.com/henrygd/beszel.git
synced 2026-03-23 14:06:18 +01:00
Compare commits
48 Commits
53a7e06dcf
...
v0.18.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
618e5b4cc1 | ||
|
|
42c3ca5db5 | ||
|
|
534791776b | ||
|
|
0c6c53fc7d | ||
|
|
0dfd5ce07d | ||
|
|
2cd6d46f7c | ||
|
|
c333a9fadd | ||
|
|
ba3d1c66f0 | ||
|
|
7276e533ce | ||
|
|
8b84231042 | ||
|
|
77da744008 | ||
|
|
5da7a21119 | ||
|
|
78d742c712 | ||
|
|
1c97ea3e2c | ||
|
|
3d970defe9 | ||
|
|
6282794004 | ||
|
|
475c53a55d | ||
|
|
4547ff7b5d | ||
|
|
e7b4be3dc5 | ||
|
|
2bd85e04fc | ||
|
|
f6ab5f2af1 | ||
|
|
7d943633a3 | ||
|
|
7fff3c999a | ||
|
|
a9068a11a9 | ||
|
|
d3d102516c | ||
|
|
32131439f9 | ||
|
|
d17685c540 | ||
|
|
e59f8eee36 | ||
|
|
35329abcbd | ||
|
|
ee7741c3ab | ||
|
|
ab0803b2da | ||
|
|
96196a353c | ||
|
|
2a8796c38d | ||
|
|
c8d4f7427d | ||
|
|
8d41a797d3 | ||
|
|
570e1cbf40 | ||
|
|
4c9b00a066 | ||
|
|
7d1f8bb180 | ||
|
|
3a6caeb06e | ||
|
|
9e67245e60 | ||
|
|
b7a95d5d76 | ||
|
|
fe550c5901 | ||
|
|
8aac0a571a | ||
|
|
c506b8b0ad | ||
|
|
a6e84c207e | ||
|
|
249402eaed | ||
|
|
394c476f2a | ||
|
|
86e8a141ea |
@@ -16,10 +16,21 @@ builds:
|
||||
goos:
|
||||
- linux
|
||||
- darwin
|
||||
- windows
|
||||
- freebsd
|
||||
goarch:
|
||||
- amd64
|
||||
- arm64
|
||||
- arm
|
||||
ignore:
|
||||
- goos: windows
|
||||
goarch: arm64
|
||||
- goos: windows
|
||||
goarch: arm
|
||||
- goos: freebsd
|
||||
goarch: arm64
|
||||
- goos: freebsd
|
||||
goarch: arm
|
||||
|
||||
- id: beszel-agent
|
||||
binary: beszel-agent
|
||||
@@ -86,6 +97,9 @@ archives:
|
||||
{{ .Binary }}_
|
||||
{{- .Os }}_
|
||||
{{- .Arch }}
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
formats: [zip]
|
||||
|
||||
nfpms:
|
||||
- id: beszel-agent
|
||||
|
||||
@@ -12,10 +12,12 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gliderlabs/ssh"
|
||||
"github.com/henrygd/beszel"
|
||||
"github.com/henrygd/beszel/agent/deltatracker"
|
||||
"github.com/henrygd/beszel/internal/common"
|
||||
"github.com/henrygd/beszel/internal/entities/system"
|
||||
"github.com/shirou/gopsutil/v4/host"
|
||||
gossh "golang.org/x/crypto/ssh"
|
||||
@@ -29,12 +31,15 @@ type Agent struct {
|
||||
fsNames []string // List of filesystem device names being monitored
|
||||
fsStats map[string]*system.FsStats // Keeps track of disk stats for each filesystem
|
||||
diskPrev map[uint16]map[string]prevDisk // Previous disk I/O counters per cache interval
|
||||
diskUsageCacheDuration time.Duration // How long to cache disk usage (to avoid waking sleeping disks)
|
||||
lastDiskUsageUpdate time.Time // Last time disk usage was collected
|
||||
netInterfaces map[string]struct{} // Stores all valid network interfaces
|
||||
netIoStats map[uint16]system.NetIoStats // Keeps track of bandwidth usage per cache interval
|
||||
netInterfaceDeltaTrackers map[uint16]*deltatracker.DeltaTracker[string, uint64] // Per-cache-time NIC delta trackers
|
||||
dockerManager *dockerManager // Manages Docker API requests
|
||||
sensorConfig *SensorConfig // Sensors config
|
||||
systemInfo system.Info // Host system info
|
||||
systemInfo system.Info // Host system info (dynamic)
|
||||
systemDetails system.Details // Host system details (static, once-per-connection)
|
||||
gpuManager *GPUManager // Manages GPU data
|
||||
cache *systemDataCache // Cache for system stats based on cache time
|
||||
connectionManager *ConnectionManager // Channel to signal connection events
|
||||
@@ -69,6 +74,17 @@ func NewAgent(dataDir ...string) (agent *Agent, err error) {
|
||||
|
||||
agent.memCalc, _ = GetEnv("MEM_CALC")
|
||||
agent.sensorConfig = agent.newSensorConfig()
|
||||
|
||||
// Parse disk usage cache duration (e.g., "15m", "1h") to avoid waking sleeping disks
|
||||
if diskUsageCache, exists := GetEnv("DISK_USAGE_CACHE"); exists {
|
||||
if duration, err := time.ParseDuration(diskUsageCache); err == nil {
|
||||
agent.diskUsageCacheDuration = duration
|
||||
slog.Info("DISK_USAGE_CACHE", "duration", duration)
|
||||
} else {
|
||||
slog.Warn("Invalid DISK_USAGE_CACHE", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Set up slog with a log level determined by the LOG_LEVEL env var
|
||||
if logLevelStr, exists := GetEnv("LOG_LEVEL"); exists {
|
||||
switch strings.ToLower(logLevelStr) {
|
||||
@@ -84,8 +100,21 @@ func NewAgent(dataDir ...string) (agent *Agent, err error) {
|
||||
|
||||
slog.Debug(beszel.Version)
|
||||
|
||||
// initialize docker manager
|
||||
agent.dockerManager = newDockerManager()
|
||||
|
||||
// initialize system info
|
||||
agent.initializeSystemInfo()
|
||||
agent.refreshSystemDetails()
|
||||
|
||||
// SMART_INTERVAL env var to update smart data at this interval
|
||||
if smartIntervalEnv, exists := GetEnv("SMART_INTERVAL"); exists {
|
||||
if duration, err := time.ParseDuration(smartIntervalEnv); err == nil && duration > 0 {
|
||||
agent.systemDetails.SmartInterval = duration
|
||||
slog.Info("SMART_INTERVAL", "duration", duration)
|
||||
} else {
|
||||
slog.Warn("Invalid SMART_INTERVAL", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// initialize connection manager
|
||||
agent.connectionManager = newConnectionManager(agent)
|
||||
@@ -99,9 +128,6 @@ func NewAgent(dataDir ...string) (agent *Agent, err error) {
|
||||
// initialize net io stats
|
||||
agent.initializeNetIoStats()
|
||||
|
||||
// initialize docker manager
|
||||
agent.dockerManager = newDockerManager(agent)
|
||||
|
||||
agent.systemdManager, err = newSystemdManager()
|
||||
if err != nil {
|
||||
slog.Debug("Systemd", "err", err)
|
||||
@@ -120,7 +146,7 @@ func NewAgent(dataDir ...string) (agent *Agent, err error) {
|
||||
|
||||
// if debugging, print stats
|
||||
if agent.debug {
|
||||
slog.Debug("Stats", "data", agent.gatherStats(0))
|
||||
slog.Debug("Stats", "data", agent.gatherStats(common.DataRequestOptions{CacheTimeMs: 60_000, IncludeDetails: true}))
|
||||
}
|
||||
|
||||
return agent, nil
|
||||
@@ -135,10 +161,11 @@ func GetEnv(key string) (value string, exists bool) {
|
||||
return os.LookupEnv(key)
|
||||
}
|
||||
|
||||
func (a *Agent) gatherStats(cacheTimeMs uint16) *system.CombinedData {
|
||||
func (a *Agent) gatherStats(options common.DataRequestOptions) *system.CombinedData {
|
||||
a.Lock()
|
||||
defer a.Unlock()
|
||||
|
||||
cacheTimeMs := options.CacheTimeMs
|
||||
data, isCached := a.cache.Get(cacheTimeMs)
|
||||
if isCached {
|
||||
slog.Debug("Cached data", "cacheTimeMs", cacheTimeMs)
|
||||
@@ -149,6 +176,12 @@ func (a *Agent) gatherStats(cacheTimeMs uint16) *system.CombinedData {
|
||||
Stats: a.getSystemStats(cacheTimeMs),
|
||||
Info: a.systemInfo,
|
||||
}
|
||||
|
||||
// Include static system details only when requested
|
||||
if options.IncludeDetails {
|
||||
data.Details = &a.systemDetails
|
||||
}
|
||||
|
||||
// slog.Info("System data", "data", data, "cacheTimeMs", cacheTimeMs)
|
||||
|
||||
if a.dockerManager != nil {
|
||||
@@ -211,8 +244,9 @@ func (a *Agent) getFingerprint() string {
|
||||
|
||||
// if no fingerprint is found, generate one
|
||||
fingerprint, err := host.HostID()
|
||||
if err != nil || fingerprint == "" {
|
||||
fingerprint = a.systemInfo.Hostname + a.systemInfo.CpuModel
|
||||
// we ignore a commonly known "product_uuid" known not to be unique
|
||||
if err != nil || fingerprint == "" || fingerprint == "03000200-0400-0500-0006-000700080009" {
|
||||
fingerprint = a.systemDetails.Hostname + a.systemDetails.CpuModel
|
||||
}
|
||||
|
||||
// hash fingerprint
|
||||
|
||||
@@ -22,7 +22,7 @@ func createTestCacheData() *system.CombinedData {
|
||||
DiskTotal: 100000,
|
||||
},
|
||||
Info: system.Info{
|
||||
Hostname: "test-host",
|
||||
AgentVersion: "0.12.0",
|
||||
},
|
||||
Containers: []*container.Stats{
|
||||
{
|
||||
@@ -128,7 +128,7 @@ func TestCacheMultipleIntervals(t *testing.T) {
|
||||
Mem: 16384,
|
||||
},
|
||||
Info: system.Info{
|
||||
Hostname: "test-host-2",
|
||||
AgentVersion: "0.12.0",
|
||||
},
|
||||
Containers: []*container.Stats{},
|
||||
}
|
||||
@@ -171,7 +171,7 @@ func TestCacheOverwrite(t *testing.T) {
|
||||
Mem: 32768,
|
||||
},
|
||||
Info: system.Info{
|
||||
Hostname: "updated-host",
|
||||
AgentVersion: "0.12.0",
|
||||
},
|
||||
Containers: []*container.Stats{},
|
||||
}
|
||||
|
||||
@@ -15,9 +15,6 @@ import (
|
||||
|
||||
"github.com/henrygd/beszel"
|
||||
"github.com/henrygd/beszel/internal/common"
|
||||
"github.com/henrygd/beszel/internal/entities/smart"
|
||||
"github.com/henrygd/beszel/internal/entities/system"
|
||||
"github.com/henrygd/beszel/internal/entities/systemd"
|
||||
|
||||
"github.com/fxamacker/cbor/v2"
|
||||
"github.com/lxzan/gws"
|
||||
@@ -201,7 +198,7 @@ func (client *WebSocketClient) handleAuthChallenge(msg *common.HubRequest[cbor.R
|
||||
|
||||
if authRequest.NeedSysInfo {
|
||||
response.Name, _ = GetEnv("SYSTEM_NAME")
|
||||
response.Hostname = client.agent.systemInfo.Hostname
|
||||
response.Hostname = client.agent.systemDetails.Hostname
|
||||
serverAddr := client.agent.connectionManager.serverOptions.Addr
|
||||
_, response.Port, _ = net.SplitHostPort(serverAddr)
|
||||
}
|
||||
@@ -259,40 +256,16 @@ func (client *WebSocketClient) sendMessage(data any) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// sendResponse sends a response with optional request ID for the new protocol
|
||||
// sendResponse sends a response with optional request ID.
|
||||
// For ID-based requests, we must populate legacy typed fields for backward
|
||||
// compatibility with older hubs (<= 0.17) that don't read the generic Data field.
|
||||
func (client *WebSocketClient) sendResponse(data any, requestID *uint32) error {
|
||||
if requestID != nil {
|
||||
// New format with ID - use typed fields
|
||||
response := common.AgentResponse{
|
||||
Id: requestID,
|
||||
}
|
||||
|
||||
// Set the appropriate typed field based on data type
|
||||
switch v := data.(type) {
|
||||
case *system.CombinedData:
|
||||
response.SystemData = v
|
||||
case *common.FingerprintResponse:
|
||||
response.Fingerprint = v
|
||||
case string:
|
||||
response.String = &v
|
||||
case map[string]smart.SmartData:
|
||||
response.SmartData = v
|
||||
case systemd.ServiceDetails:
|
||||
response.ServiceInfo = v
|
||||
// case []byte:
|
||||
// response.RawBytes = v
|
||||
// case string:
|
||||
// response.RawBytes = []byte(v)
|
||||
default:
|
||||
// For any other type, convert to error
|
||||
response.Error = fmt.Sprintf("unsupported response type: %T", data)
|
||||
}
|
||||
|
||||
response := newAgentResponse(data, requestID)
|
||||
return client.sendMessage(response)
|
||||
} else {
|
||||
// Legacy format - send data directly
|
||||
return client.sendMessage(data)
|
||||
}
|
||||
// Legacy format - send data directly
|
||||
return client.sendMessage(data)
|
||||
}
|
||||
|
||||
// getUserAgent returns one of two User-Agent strings based on current time.
|
||||
|
||||
@@ -225,8 +225,19 @@ func (a *Agent) initializeDiskIoStats(diskIoCounters map[string]disk.IOCountersS
|
||||
|
||||
// Updates disk usage statistics for all monitored filesystems
|
||||
func (a *Agent) updateDiskUsage(systemStats *system.Stats) {
|
||||
// Check if we should skip extra filesystem collection to avoid waking sleeping disks.
|
||||
// Root filesystem is always updated since it can't be sleeping while the agent runs.
|
||||
// Always collect on first call (lastDiskUsageUpdate is zero) or if caching is disabled.
|
||||
cacheExtraFs := a.diskUsageCacheDuration > 0 &&
|
||||
!a.lastDiskUsageUpdate.IsZero() &&
|
||||
time.Since(a.lastDiskUsageUpdate) < a.diskUsageCacheDuration
|
||||
|
||||
// disk usage
|
||||
for _, stats := range a.fsStats {
|
||||
// Skip non-root filesystems if caching is active
|
||||
if cacheExtraFs && !stats.Root {
|
||||
continue
|
||||
}
|
||||
if d, err := disk.Usage(stats.Mountpoint); err == nil {
|
||||
stats.DiskTotal = bytesToGigabytes(d.Total)
|
||||
stats.DiskUsed = bytesToGigabytes(d.Used)
|
||||
@@ -244,6 +255,11 @@ func (a *Agent) updateDiskUsage(systemStats *system.Stats) {
|
||||
stats.TotalWrite = 0
|
||||
}
|
||||
}
|
||||
|
||||
// Update the last disk usage update time when we've collected extra filesystems
|
||||
if !cacheExtraFs {
|
||||
a.lastDiskUsageUpdate = time.Now()
|
||||
}
|
||||
}
|
||||
|
||||
// Updates disk I/O statistics for all monitored filesystems
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/henrygd/beszel/internal/entities/system"
|
||||
"github.com/shirou/gopsutil/v4/disk"
|
||||
@@ -233,3 +234,86 @@ func TestExtraFsKeyGeneration(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDiskUsageCaching(t *testing.T) {
|
||||
t.Run("caching disabled updates all filesystems", func(t *testing.T) {
|
||||
agent := &Agent{
|
||||
fsStats: map[string]*system.FsStats{
|
||||
"sda": {Root: true, Mountpoint: "/"},
|
||||
"sdb": {Root: false, Mountpoint: "/mnt/storage"},
|
||||
},
|
||||
diskUsageCacheDuration: 0, // caching disabled
|
||||
}
|
||||
|
||||
var stats system.Stats
|
||||
agent.updateDiskUsage(&stats)
|
||||
|
||||
// Both should be updated (non-zero values from disk.Usage)
|
||||
// Root stats should be populated in systemStats
|
||||
assert.True(t, agent.lastDiskUsageUpdate.IsZero() || !agent.lastDiskUsageUpdate.IsZero(),
|
||||
"lastDiskUsageUpdate should be set when caching is disabled")
|
||||
})
|
||||
|
||||
t.Run("caching enabled always updates root filesystem", func(t *testing.T) {
|
||||
agent := &Agent{
|
||||
fsStats: map[string]*system.FsStats{
|
||||
"sda": {Root: true, Mountpoint: "/", DiskTotal: 100, DiskUsed: 50},
|
||||
"sdb": {Root: false, Mountpoint: "/mnt/storage", DiskTotal: 200, DiskUsed: 100},
|
||||
},
|
||||
diskUsageCacheDuration: 1 * time.Hour,
|
||||
lastDiskUsageUpdate: time.Now(), // cache is fresh
|
||||
}
|
||||
|
||||
// Store original extra fs values
|
||||
originalExtraTotal := agent.fsStats["sdb"].DiskTotal
|
||||
originalExtraUsed := agent.fsStats["sdb"].DiskUsed
|
||||
|
||||
var stats system.Stats
|
||||
agent.updateDiskUsage(&stats)
|
||||
|
||||
// Root should be updated (systemStats populated from disk.Usage call)
|
||||
// We can't easily check if disk.Usage was called, but we verify the flow works
|
||||
|
||||
// Extra filesystem should retain cached values (not reset)
|
||||
assert.Equal(t, originalExtraTotal, agent.fsStats["sdb"].DiskTotal,
|
||||
"extra filesystem DiskTotal should be unchanged when cached")
|
||||
assert.Equal(t, originalExtraUsed, agent.fsStats["sdb"].DiskUsed,
|
||||
"extra filesystem DiskUsed should be unchanged when cached")
|
||||
})
|
||||
|
||||
t.Run("first call always updates all filesystems", func(t *testing.T) {
|
||||
agent := &Agent{
|
||||
fsStats: map[string]*system.FsStats{
|
||||
"sda": {Root: true, Mountpoint: "/"},
|
||||
"sdb": {Root: false, Mountpoint: "/mnt/storage"},
|
||||
},
|
||||
diskUsageCacheDuration: 1 * time.Hour,
|
||||
// lastDiskUsageUpdate is zero (first call)
|
||||
}
|
||||
|
||||
var stats system.Stats
|
||||
agent.updateDiskUsage(&stats)
|
||||
|
||||
// After first call, lastDiskUsageUpdate should be set
|
||||
assert.False(t, agent.lastDiskUsageUpdate.IsZero(),
|
||||
"lastDiskUsageUpdate should be set after first call")
|
||||
})
|
||||
|
||||
t.Run("expired cache updates extra filesystems", func(t *testing.T) {
|
||||
agent := &Agent{
|
||||
fsStats: map[string]*system.FsStats{
|
||||
"sda": {Root: true, Mountpoint: "/"},
|
||||
"sdb": {Root: false, Mountpoint: "/mnt/storage"},
|
||||
},
|
||||
diskUsageCacheDuration: 1 * time.Millisecond,
|
||||
lastDiskUsageUpdate: time.Now().Add(-1 * time.Second), // cache expired
|
||||
}
|
||||
|
||||
var stats system.Stats
|
||||
agent.updateDiskUsage(&stats)
|
||||
|
||||
// lastDiskUsageUpdate should be refreshed since cache expired
|
||||
assert.True(t, time.Since(agent.lastDiskUsageUpdate) < time.Second,
|
||||
"lastDiskUsageUpdate should be refreshed when cache expires")
|
||||
})
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -24,6 +25,10 @@ import (
|
||||
"github.com/blang/semver"
|
||||
)
|
||||
|
||||
// ansiEscapePattern matches ANSI escape sequences (colors, cursor movement, etc.)
|
||||
// This includes CSI sequences like \x1b[...m and simple escapes like \x1b[K
|
||||
var ansiEscapePattern = regexp.MustCompile(`\x1b\[[0-9;]*[a-zA-Z]|\x1b\][^\x07]*\x07|\x1b[@-Z\\-_]`)
|
||||
|
||||
const (
|
||||
// Docker API timeout in milliseconds
|
||||
dockerTimeoutMs = 2100
|
||||
@@ -55,6 +60,7 @@ type dockerManager struct {
|
||||
decoder *json.Decoder // Reusable JSON decoder that reads from buf
|
||||
apiStats *container.ApiStats // Reusable API stats object
|
||||
excludeContainers []string // Patterns to exclude containers by name
|
||||
usingPodman bool // Whether the Docker Engine API is running on Podman
|
||||
|
||||
// Cache-time-aware tracking for CPU stats (similar to cpu.go)
|
||||
// Maps cache time intervals to container-specific CPU usage tracking
|
||||
@@ -473,7 +479,7 @@ func (dm *dockerManager) deleteContainerStatsSync(id string) {
|
||||
}
|
||||
|
||||
// Creates a new http client for Docker or Podman API
|
||||
func newDockerManager(a *Agent) *dockerManager {
|
||||
func newDockerManager() *dockerManager {
|
||||
dockerHost, exists := GetEnv("DOCKER_HOST")
|
||||
if exists {
|
||||
// return nil if set to empty string
|
||||
@@ -559,7 +565,7 @@ func newDockerManager(a *Agent) *dockerManager {
|
||||
|
||||
// If using podman, return client
|
||||
if strings.Contains(dockerHost, "podman") {
|
||||
a.systemInfo.Podman = true
|
||||
manager.usingPodman = true
|
||||
manager.goodDockerVersion = true
|
||||
return manager
|
||||
}
|
||||
@@ -688,17 +694,26 @@ func (dm *dockerManager) getLogs(ctx context.Context, containerID string) (strin
|
||||
}
|
||||
|
||||
var builder strings.Builder
|
||||
if err := decodeDockerLogStream(resp.Body, &builder); err != nil {
|
||||
multiplexed := resp.Header.Get("Content-Type") == "application/vnd.docker.multiplexed-stream"
|
||||
if err := decodeDockerLogStream(resp.Body, &builder, multiplexed); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return builder.String(), nil
|
||||
// Strip ANSI escape sequences from logs for clean display in web UI
|
||||
logs := builder.String()
|
||||
if strings.Contains(logs, "\x1b") {
|
||||
logs = ansiEscapePattern.ReplaceAllString(logs, "")
|
||||
}
|
||||
return logs, nil
|
||||
}
|
||||
|
||||
func decodeDockerLogStream(reader io.Reader, builder *strings.Builder) error {
|
||||
func decodeDockerLogStream(reader io.Reader, builder *strings.Builder, multiplexed bool) error {
|
||||
if !multiplexed {
|
||||
_, err := io.Copy(builder, io.LimitReader(reader, maxTotalLogSize))
|
||||
return err
|
||||
}
|
||||
const headerSize = 8
|
||||
var header [headerSize]byte
|
||||
buf := make([]byte, 0, dockerLogsTail*200)
|
||||
totalBytesRead := 0
|
||||
|
||||
for {
|
||||
@@ -722,36 +737,37 @@ func decodeDockerLogStream(reader io.Reader, builder *strings.Builder) error {
|
||||
// Check if reading this frame would exceed total log size limit
|
||||
if totalBytesRead+int(frameLen) > maxTotalLogSize {
|
||||
// Read and discard remaining data to avoid blocking
|
||||
_, _ = io.Copy(io.Discard, io.LimitReader(reader, int64(frameLen)))
|
||||
_, _ = io.CopyN(io.Discard, reader, int64(frameLen))
|
||||
slog.Debug("Truncating logs: limit reached", "read", totalBytesRead, "limit", maxTotalLogSize)
|
||||
return nil
|
||||
}
|
||||
|
||||
buf = allocateBuffer(buf, int(frameLen))
|
||||
if _, err := io.ReadFull(reader, buf[:frameLen]); err != nil {
|
||||
n, err := io.CopyN(builder, reader, int64(frameLen))
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) {
|
||||
if len(buf) > 0 {
|
||||
builder.Write(buf[:min(int(frameLen), len(buf))])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
builder.Write(buf[:frameLen])
|
||||
totalBytesRead += int(frameLen)
|
||||
totalBytesRead += int(n)
|
||||
}
|
||||
}
|
||||
|
||||
func allocateBuffer(current []byte, needed int) []byte {
|
||||
if cap(current) >= needed {
|
||||
return current[:needed]
|
||||
// GetHostInfo fetches the system info from Docker
|
||||
func (dm *dockerManager) GetHostInfo() (info container.HostInfo, err error) {
|
||||
resp, err := dm.client.Get("http://localhost/info")
|
||||
if err != nil {
|
||||
return info, err
|
||||
}
|
||||
return make([]byte, needed)
|
||||
defer resp.Body.Close()
|
||||
|
||||
if err := json.NewDecoder(resp.Body).Decode(&info); err != nil {
|
||||
return info, err
|
||||
}
|
||||
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func min(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
func (dm *dockerManager) IsPodman() bool {
|
||||
return dm.usingPodman
|
||||
}
|
||||
|
||||
@@ -802,6 +802,24 @@ func TestNetworkRateCalculationFormula(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetHostInfo(t *testing.T) {
|
||||
data, err := os.ReadFile("test-data/system_info.json")
|
||||
require.NoError(t, err)
|
||||
|
||||
var info container.HostInfo
|
||||
err = json.Unmarshal(data, &info)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "6.8.0-31-generic", info.KernelVersion)
|
||||
assert.Equal(t, "Ubuntu 24.04 LTS", info.OperatingSystem)
|
||||
// assert.Equal(t, "24.04", info.OSVersion)
|
||||
// assert.Equal(t, "linux", info.OSType)
|
||||
// assert.Equal(t, "x86_64", info.Architecture)
|
||||
assert.EqualValues(t, 4, info.NCPU)
|
||||
assert.EqualValues(t, 2095882240, info.MemTotal)
|
||||
// assert.Equal(t, "27.0.1", info.ServerVersion)
|
||||
}
|
||||
|
||||
func TestDeltaTrackerCacheTimeIsolation(t *testing.T) {
|
||||
// Test that different cache times have separate DeltaTracker instances
|
||||
dm := &dockerManager{
|
||||
@@ -932,6 +950,7 @@ func TestDecodeDockerLogStream(t *testing.T) {
|
||||
input []byte
|
||||
expected string
|
||||
expectError bool
|
||||
multiplexed bool
|
||||
}{
|
||||
{
|
||||
name: "simple log entry",
|
||||
@@ -942,6 +961,7 @@ func TestDecodeDockerLogStream(t *testing.T) {
|
||||
},
|
||||
expected: "Hello World",
|
||||
expectError: false,
|
||||
multiplexed: true,
|
||||
},
|
||||
{
|
||||
name: "multiple frames",
|
||||
@@ -955,6 +975,7 @@ func TestDecodeDockerLogStream(t *testing.T) {
|
||||
},
|
||||
expected: "HelloWorld",
|
||||
expectError: false,
|
||||
multiplexed: true,
|
||||
},
|
||||
{
|
||||
name: "zero length frame",
|
||||
@@ -967,12 +988,20 @@ func TestDecodeDockerLogStream(t *testing.T) {
|
||||
},
|
||||
expected: "Hello",
|
||||
expectError: false,
|
||||
multiplexed: true,
|
||||
},
|
||||
{
|
||||
name: "empty input",
|
||||
input: []byte{},
|
||||
expected: "",
|
||||
expectError: false,
|
||||
multiplexed: true,
|
||||
},
|
||||
{
|
||||
name: "raw stream (not multiplexed)",
|
||||
input: []byte("raw log content"),
|
||||
expected: "raw log content",
|
||||
multiplexed: false,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -980,7 +1009,7 @@ func TestDecodeDockerLogStream(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
reader := bytes.NewReader(tt.input)
|
||||
var builder strings.Builder
|
||||
err := decodeDockerLogStream(reader, &builder)
|
||||
err := decodeDockerLogStream(reader, &builder, tt.multiplexed)
|
||||
|
||||
if tt.expectError {
|
||||
assert.Error(t, err)
|
||||
@@ -1004,7 +1033,7 @@ func TestDecodeDockerLogStreamMemoryProtection(t *testing.T) {
|
||||
|
||||
reader := bytes.NewReader(input)
|
||||
var builder strings.Builder
|
||||
err := decodeDockerLogStream(reader, &builder)
|
||||
err := decodeDockerLogStream(reader, &builder, true)
|
||||
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "log frame size")
|
||||
@@ -1038,7 +1067,7 @@ func TestDecodeDockerLogStreamMemoryProtection(t *testing.T) {
|
||||
|
||||
reader := bytes.NewReader(input)
|
||||
var builder strings.Builder
|
||||
err := decodeDockerLogStream(reader, &builder)
|
||||
err := decodeDockerLogStream(reader, &builder, true)
|
||||
|
||||
// Should complete without error (graceful truncation)
|
||||
assert.NoError(t, err)
|
||||
@@ -1053,53 +1082,6 @@ func TestDecodeDockerLogStreamMemoryProtection(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestAllocateBuffer(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
currentCap int
|
||||
needed int
|
||||
expectedCap int
|
||||
shouldRealloc bool
|
||||
}{
|
||||
{
|
||||
name: "buffer has enough capacity",
|
||||
currentCap: 1024,
|
||||
needed: 512,
|
||||
expectedCap: 1024,
|
||||
shouldRealloc: false,
|
||||
},
|
||||
{
|
||||
name: "buffer needs reallocation",
|
||||
currentCap: 512,
|
||||
needed: 1024,
|
||||
expectedCap: 1024,
|
||||
shouldRealloc: true,
|
||||
},
|
||||
{
|
||||
name: "buffer needs exact size",
|
||||
currentCap: 1024,
|
||||
needed: 1024,
|
||||
expectedCap: 1024,
|
||||
shouldRealloc: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
current := make([]byte, 0, tt.currentCap)
|
||||
result := allocateBuffer(current, tt.needed)
|
||||
|
||||
assert.Equal(t, tt.needed, len(result))
|
||||
assert.GreaterOrEqual(t, cap(result), tt.expectedCap)
|
||||
|
||||
if tt.shouldRealloc {
|
||||
// If reallocation was needed, capacity should be at least the needed size
|
||||
assert.GreaterOrEqual(t, cap(result), tt.needed)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestShouldExcludeContainer(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -1203,3 +1185,59 @@ func TestShouldExcludeContainer(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnsiEscapePattern(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "no ANSI codes",
|
||||
input: "Hello, World!",
|
||||
expected: "Hello, World!",
|
||||
},
|
||||
{
|
||||
name: "simple color code",
|
||||
input: "\x1b[34mINFO\x1b[0m client mode",
|
||||
expected: "INFO client mode",
|
||||
},
|
||||
{
|
||||
name: "multiple color codes",
|
||||
input: "\x1b[31mERROR\x1b[0m: \x1b[33mWarning\x1b[0m message",
|
||||
expected: "ERROR: Warning message",
|
||||
},
|
||||
{
|
||||
name: "bold and color",
|
||||
input: "\x1b[1;32mSUCCESS\x1b[0m",
|
||||
expected: "SUCCESS",
|
||||
},
|
||||
{
|
||||
name: "cursor movement codes",
|
||||
input: "Line 1\x1b[KLine 2",
|
||||
expected: "Line 1Line 2",
|
||||
},
|
||||
{
|
||||
name: "256 color code",
|
||||
input: "\x1b[38;5;196mRed text\x1b[0m",
|
||||
expected: "Red text",
|
||||
},
|
||||
{
|
||||
name: "RGB/truecolor code",
|
||||
input: "\x1b[38;2;255;0;0mRed text\x1b[0m",
|
||||
expected: "Red text",
|
||||
},
|
||||
{
|
||||
name: "mixed content with newlines",
|
||||
input: "\x1b[34m2024-01-01 12:00:00\x1b[0m INFO Starting\n\x1b[31m2024-01-01 12:00:01\x1b[0m ERROR Failed",
|
||||
expected: "2024-01-01 12:00:00 INFO Starting\n2024-01-01 12:00:01 ERROR Failed",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := ansiEscapePattern.ReplaceAllString(tt.input, "")
|
||||
assert.Equal(t, tt.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
27
agent/gpu.go
27
agent/gpu.go
@@ -15,7 +15,7 @@ import (
|
||||
|
||||
"github.com/henrygd/beszel/internal/entities/system"
|
||||
|
||||
"golang.org/x/exp/slog"
|
||||
"log/slog"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -44,6 +44,7 @@ type GPUManager struct {
|
||||
rocmSmi bool
|
||||
tegrastats bool
|
||||
intelGpuStats bool
|
||||
nvml bool
|
||||
GpuDataMap map[string]*system.GPUData
|
||||
// lastAvgData stores the last calculated averages for each GPU
|
||||
// Used when a collection happens before new data arrives (Count == 0)
|
||||
@@ -297,8 +298,13 @@ func (gm *GPUManager) calculateGPUAverage(id string, gpu *system.GPUData, cacheK
|
||||
currentCount := uint32(gpu.Count)
|
||||
deltaCount := gm.calculateDeltaCount(currentCount, lastSnapshot)
|
||||
|
||||
// If no new data arrived, use last known average
|
||||
// If no new data arrived
|
||||
if deltaCount == 0 {
|
||||
// If GPU appears suspended (instantaneous values are 0), return zero values
|
||||
// Otherwise return last known average for temporary collection gaps
|
||||
if gpu.Temperature == 0 && gpu.MemoryUsed == 0 {
|
||||
return system.GPUData{Name: gpu.Name}
|
||||
}
|
||||
return gm.lastAvgData[id] // zero value if not found
|
||||
}
|
||||
|
||||
@@ -396,7 +402,7 @@ func (gm *GPUManager) detectGPUs() error {
|
||||
if _, err := exec.LookPath(intelGpuStatsCmd); err == nil {
|
||||
gm.intelGpuStats = true
|
||||
}
|
||||
if gm.nvidiaSmi || gm.rocmSmi || gm.tegrastats || gm.intelGpuStats {
|
||||
if gm.nvidiaSmi || gm.rocmSmi || gm.tegrastats || gm.intelGpuStats || gm.nvml {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("no GPU found - install nvidia-smi, rocm-smi, tegrastats, or intel_gpu_top")
|
||||
@@ -467,7 +473,20 @@ func NewGPUManager() (*GPUManager, error) {
|
||||
gm.GpuDataMap = make(map[string]*system.GPUData)
|
||||
|
||||
if gm.nvidiaSmi {
|
||||
gm.startCollector(nvidiaSmiCmd)
|
||||
if nvml, _ := GetEnv("NVML"); nvml == "true" {
|
||||
gm.nvml = true
|
||||
gm.nvidiaSmi = false
|
||||
collector := &nvmlCollector{gm: &gm}
|
||||
if err := collector.init(); err == nil {
|
||||
go collector.start()
|
||||
} else {
|
||||
slog.Warn("Failed to initialize NVML, falling back to nvidia-smi", "err", err)
|
||||
gm.nvidiaSmi = true
|
||||
gm.startCollector(nvidiaSmiCmd)
|
||||
}
|
||||
} else {
|
||||
gm.startCollector(nvidiaSmiCmd)
|
||||
}
|
||||
}
|
||||
if gm.rocmSmi {
|
||||
gm.startCollector(rocmSmiCmd)
|
||||
|
||||
224
agent/gpu_nvml.go
Normal file
224
agent/gpu_nvml.go
Normal file
@@ -0,0 +1,224 @@
|
||||
//go:build (linux || windows) && (amd64 || arm64)
|
||||
|
||||
package agent
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strings"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/ebitengine/purego"
|
||||
"github.com/henrygd/beszel/internal/entities/system"
|
||||
)
|
||||
|
||||
// NVML constants and types
|
||||
const (
|
||||
nvmlSuccess int = 0
|
||||
)
|
||||
|
||||
type nvmlDevice uintptr
|
||||
|
||||
type nvmlReturn int
|
||||
|
||||
type nvmlMemoryV1 struct {
|
||||
Total uint64
|
||||
Free uint64
|
||||
Used uint64
|
||||
}
|
||||
|
||||
type nvmlMemoryV2 struct {
|
||||
Version uint32
|
||||
Total uint64
|
||||
Reserved uint64
|
||||
Free uint64
|
||||
Used uint64
|
||||
}
|
||||
|
||||
type nvmlUtilization struct {
|
||||
Gpu uint32
|
||||
Memory uint32
|
||||
}
|
||||
|
||||
type nvmlPciInfo struct {
|
||||
BusId [16]byte
|
||||
Domain uint32
|
||||
Bus uint32
|
||||
Device uint32
|
||||
PciDeviceId uint32
|
||||
PciSubSystemId uint32
|
||||
}
|
||||
|
||||
// NVML function signatures
|
||||
var (
|
||||
nvmlInit func() nvmlReturn
|
||||
nvmlShutdown func() nvmlReturn
|
||||
nvmlDeviceGetCount func(count *uint32) nvmlReturn
|
||||
nvmlDeviceGetHandleByIndex func(index uint32, device *nvmlDevice) nvmlReturn
|
||||
nvmlDeviceGetName func(device nvmlDevice, name *byte, length uint32) nvmlReturn
|
||||
nvmlDeviceGetMemoryInfo func(device nvmlDevice, memory uintptr) nvmlReturn
|
||||
nvmlDeviceGetUtilizationRates func(device nvmlDevice, utilization *nvmlUtilization) nvmlReturn
|
||||
nvmlDeviceGetTemperature func(device nvmlDevice, sensorType int, temp *uint32) nvmlReturn
|
||||
nvmlDeviceGetPowerUsage func(device nvmlDevice, power *uint32) nvmlReturn
|
||||
nvmlDeviceGetPciInfo func(device nvmlDevice, pci *nvmlPciInfo) nvmlReturn
|
||||
nvmlErrorString func(result nvmlReturn) string
|
||||
)
|
||||
|
||||
type nvmlCollector struct {
|
||||
gm *GPUManager
|
||||
lib uintptr
|
||||
devices []nvmlDevice
|
||||
bdfs []string
|
||||
isV2 bool
|
||||
}
|
||||
|
||||
func (c *nvmlCollector) init() error {
|
||||
slog.Debug("NVML: Initializing")
|
||||
libPath := getNVMLPath()
|
||||
|
||||
lib, err := openLibrary(libPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load %s: %w", libPath, err)
|
||||
}
|
||||
c.lib = lib
|
||||
|
||||
purego.RegisterLibFunc(&nvmlInit, lib, "nvmlInit")
|
||||
purego.RegisterLibFunc(&nvmlShutdown, lib, "nvmlShutdown")
|
||||
purego.RegisterLibFunc(&nvmlDeviceGetCount, lib, "nvmlDeviceGetCount")
|
||||
purego.RegisterLibFunc(&nvmlDeviceGetHandleByIndex, lib, "nvmlDeviceGetHandleByIndex")
|
||||
purego.RegisterLibFunc(&nvmlDeviceGetName, lib, "nvmlDeviceGetName")
|
||||
// Try to get v2 memory info, fallback to v1 if not available
|
||||
if hasSymbol(lib, "nvmlDeviceGetMemoryInfo_v2") {
|
||||
c.isV2 = true
|
||||
purego.RegisterLibFunc(&nvmlDeviceGetMemoryInfo, lib, "nvmlDeviceGetMemoryInfo_v2")
|
||||
} else {
|
||||
purego.RegisterLibFunc(&nvmlDeviceGetMemoryInfo, lib, "nvmlDeviceGetMemoryInfo")
|
||||
}
|
||||
purego.RegisterLibFunc(&nvmlDeviceGetUtilizationRates, lib, "nvmlDeviceGetUtilizationRates")
|
||||
purego.RegisterLibFunc(&nvmlDeviceGetTemperature, lib, "nvmlDeviceGetTemperature")
|
||||
purego.RegisterLibFunc(&nvmlDeviceGetPowerUsage, lib, "nvmlDeviceGetPowerUsage")
|
||||
purego.RegisterLibFunc(&nvmlDeviceGetPciInfo, lib, "nvmlDeviceGetPciInfo")
|
||||
purego.RegisterLibFunc(&nvmlErrorString, lib, "nvmlErrorString")
|
||||
|
||||
if ret := nvmlInit(); ret != nvmlReturn(nvmlSuccess) {
|
||||
return fmt.Errorf("nvmlInit failed: %v", ret)
|
||||
}
|
||||
|
||||
var count uint32
|
||||
if ret := nvmlDeviceGetCount(&count); ret != nvmlReturn(nvmlSuccess) {
|
||||
return fmt.Errorf("nvmlDeviceGetCount failed: %v", ret)
|
||||
}
|
||||
|
||||
for i := uint32(0); i < count; i++ {
|
||||
var device nvmlDevice
|
||||
if ret := nvmlDeviceGetHandleByIndex(i, &device); ret == nvmlReturn(nvmlSuccess) {
|
||||
c.devices = append(c.devices, device)
|
||||
// Get BDF for power state check
|
||||
var pci nvmlPciInfo
|
||||
if ret := nvmlDeviceGetPciInfo(device, &pci); ret == nvmlReturn(nvmlSuccess) {
|
||||
busID := string(pci.BusId[:])
|
||||
if idx := strings.Index(busID, "\x00"); idx != -1 {
|
||||
busID = busID[:idx]
|
||||
}
|
||||
c.bdfs = append(c.bdfs, strings.ToLower(busID))
|
||||
} else {
|
||||
c.bdfs = append(c.bdfs, "")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *nvmlCollector) start() {
|
||||
defer nvmlShutdown()
|
||||
ticker := time.Tick(3 * time.Second)
|
||||
|
||||
for range ticker {
|
||||
c.collect()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *nvmlCollector) collect() {
|
||||
c.gm.Lock()
|
||||
defer c.gm.Unlock()
|
||||
|
||||
for i, device := range c.devices {
|
||||
id := fmt.Sprintf("%d", i)
|
||||
bdf := c.bdfs[i]
|
||||
|
||||
// Update GPUDataMap
|
||||
if _, ok := c.gm.GpuDataMap[id]; !ok {
|
||||
var nameBuf [64]byte
|
||||
if ret := nvmlDeviceGetName(device, &nameBuf[0], 64); ret != nvmlReturn(nvmlSuccess) {
|
||||
continue
|
||||
}
|
||||
name := string(nameBuf[:strings.Index(string(nameBuf[:]), "\x00")])
|
||||
name = strings.TrimPrefix(name, "NVIDIA ")
|
||||
c.gm.GpuDataMap[id] = &system.GPUData{Name: strings.TrimSuffix(name, " Laptop GPU")}
|
||||
}
|
||||
gpu := c.gm.GpuDataMap[id]
|
||||
|
||||
if bdf != "" && !c.isGPUActive(bdf) {
|
||||
slog.Debug("NVML: GPU is suspended, skipping", "bdf", bdf)
|
||||
gpu.Temperature = 0
|
||||
gpu.MemoryUsed = 0
|
||||
continue
|
||||
}
|
||||
|
||||
// Utilization
|
||||
var utilization nvmlUtilization
|
||||
if ret := nvmlDeviceGetUtilizationRates(device, &utilization); ret != nvmlReturn(nvmlSuccess) {
|
||||
slog.Debug("NVML: Utilization failed (GPU likely suspended)", "bdf", bdf, "ret", ret)
|
||||
gpu.Temperature = 0
|
||||
gpu.MemoryUsed = 0
|
||||
continue
|
||||
}
|
||||
|
||||
slog.Debug("NVML: Collecting data for GPU", "bdf", bdf)
|
||||
|
||||
// Temperature
|
||||
var temp uint32
|
||||
nvmlDeviceGetTemperature(device, 0, &temp) // 0 is NVML_TEMPERATURE_GPU
|
||||
|
||||
// Memory: only poll if GPU is active to avoid leaving D3cold state (#1522)
|
||||
if utilization.Gpu > 0 {
|
||||
var usedMem, totalMem uint64
|
||||
if c.isV2 {
|
||||
var memory nvmlMemoryV2
|
||||
memory.Version = 0x02000028 // (2 << 24) | 40 bytes
|
||||
if ret := nvmlDeviceGetMemoryInfo(device, uintptr(unsafe.Pointer(&memory))); ret != nvmlReturn(nvmlSuccess) {
|
||||
slog.Debug("NVML: MemoryInfo_v2 failed", "bdf", bdf, "ret", ret)
|
||||
} else {
|
||||
usedMem = memory.Used
|
||||
totalMem = memory.Total
|
||||
}
|
||||
} else {
|
||||
var memory nvmlMemoryV1
|
||||
if ret := nvmlDeviceGetMemoryInfo(device, uintptr(unsafe.Pointer(&memory))); ret != nvmlReturn(nvmlSuccess) {
|
||||
slog.Debug("NVML: MemoryInfo failed", "bdf", bdf, "ret", ret)
|
||||
} else {
|
||||
usedMem = memory.Used
|
||||
totalMem = memory.Total
|
||||
}
|
||||
}
|
||||
if totalMem > 0 {
|
||||
gpu.MemoryUsed = float64(usedMem) / 1024 / 1024 / mebibytesInAMegabyte
|
||||
gpu.MemoryTotal = float64(totalMem) / 1024 / 1024 / mebibytesInAMegabyte
|
||||
}
|
||||
} else {
|
||||
slog.Debug("NVML: Skipping memory info (utilization=0)", "bdf", bdf)
|
||||
}
|
||||
|
||||
// Power
|
||||
var power uint32
|
||||
nvmlDeviceGetPowerUsage(device, &power)
|
||||
|
||||
gpu.Temperature = float64(temp)
|
||||
gpu.Usage += float64(utilization.Gpu)
|
||||
gpu.Power += float64(power) / 1000.0
|
||||
gpu.Count++
|
||||
slog.Debug("NVML: Collected data", "gpu", gpu)
|
||||
}
|
||||
}
|
||||
57
agent/gpu_nvml_linux.go
Normal file
57
agent/gpu_nvml_linux.go
Normal file
@@ -0,0 +1,57 @@
|
||||
//go:build linux && (amd64 || arm64)
|
||||
|
||||
package agent
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/ebitengine/purego"
|
||||
)
|
||||
|
||||
func openLibrary(name string) (uintptr, error) {
|
||||
return purego.Dlopen(name, purego.RTLD_NOW|purego.RTLD_GLOBAL)
|
||||
}
|
||||
|
||||
func getNVMLPath() string {
|
||||
return "libnvidia-ml.so.1"
|
||||
}
|
||||
|
||||
func hasSymbol(lib uintptr, symbol string) bool {
|
||||
_, err := purego.Dlsym(lib, symbol)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func (c *nvmlCollector) isGPUActive(bdf string) bool {
|
||||
// runtime_status
|
||||
statusPath := filepath.Join("/sys/bus/pci/devices", bdf, "power/runtime_status")
|
||||
status, err := os.ReadFile(statusPath)
|
||||
if err != nil {
|
||||
slog.Debug("NVML: Can't read runtime_status", "bdf", bdf, "err", err)
|
||||
return true // Assume active if we can't read status
|
||||
}
|
||||
statusStr := strings.TrimSpace(string(status))
|
||||
if statusStr != "active" && statusStr != "resuming" {
|
||||
slog.Debug("NVML: GPU not active", "bdf", bdf, "status", statusStr)
|
||||
return false
|
||||
}
|
||||
|
||||
// power_state (D0 check)
|
||||
// Find any drm card device power_state
|
||||
pstatePathPattern := filepath.Join("/sys/bus/pci/devices", bdf, "drm/card*/device/power_state")
|
||||
matches, _ := filepath.Glob(pstatePathPattern)
|
||||
if len(matches) > 0 {
|
||||
pstate, err := os.ReadFile(matches[0])
|
||||
if err == nil {
|
||||
pstateStr := strings.TrimSpace(string(pstate))
|
||||
if pstateStr != "D0" {
|
||||
slog.Debug("NVML: GPU not in D0 state", "bdf", bdf, "pstate", pstateStr)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
33
agent/gpu_nvml_unsupported.go
Normal file
33
agent/gpu_nvml_unsupported.go
Normal file
@@ -0,0 +1,33 @@
|
||||
//go:build (!linux && !windows) || (!amd64 && !arm64)
|
||||
|
||||
package agent
|
||||
|
||||
import "fmt"
|
||||
|
||||
type nvmlCollector struct {
|
||||
gm *GPUManager
|
||||
}
|
||||
|
||||
func (c *nvmlCollector) init() error {
|
||||
return fmt.Errorf("nvml not supported on this platform")
|
||||
}
|
||||
|
||||
func (c *nvmlCollector) start() {}
|
||||
|
||||
func (c *nvmlCollector) collect() {}
|
||||
|
||||
func openLibrary(name string) (uintptr, error) {
|
||||
return 0, fmt.Errorf("nvml not supported on this platform")
|
||||
}
|
||||
|
||||
func getNVMLPath() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func hasSymbol(lib uintptr, symbol string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *nvmlCollector) isGPUActive(bdf string) bool {
|
||||
return true
|
||||
}
|
||||
25
agent/gpu_nvml_windows.go
Normal file
25
agent/gpu_nvml_windows.go
Normal file
@@ -0,0 +1,25 @@
|
||||
//go:build windows && (amd64 || arm64)
|
||||
|
||||
package agent
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
func openLibrary(name string) (uintptr, error) {
|
||||
handle, err := windows.LoadLibrary(name)
|
||||
return uintptr(handle), err
|
||||
}
|
||||
|
||||
func getNVMLPath() string {
|
||||
return "nvml.dll"
|
||||
}
|
||||
|
||||
func hasSymbol(lib uintptr, symbol string) bool {
|
||||
_, err := windows.GetProcAddress(windows.Handle(lib), symbol)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func (c *nvmlCollector) isGPUActive(bdf string) bool {
|
||||
return true
|
||||
}
|
||||
@@ -825,7 +825,7 @@ func TestInitializeSnapshots(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCalculateGPUAverage(t *testing.T) {
|
||||
t.Run("returns zero value when deltaCount is zero", func(t *testing.T) {
|
||||
t.Run("returns cached average when deltaCount is zero", func(t *testing.T) {
|
||||
gm := &GPUManager{
|
||||
lastSnapshots: map[uint16]map[string]*gpuSnapshot{
|
||||
5000: {
|
||||
@@ -838,9 +838,10 @@ func TestCalculateGPUAverage(t *testing.T) {
|
||||
}
|
||||
|
||||
gpu := &system.GPUData{
|
||||
Count: 10.0, // Same as snapshot, so delta = 0
|
||||
Usage: 100.0,
|
||||
Power: 200.0,
|
||||
Count: 10.0, // Same as snapshot, so delta = 0
|
||||
Usage: 100.0,
|
||||
Power: 200.0,
|
||||
Temperature: 50.0, // Non-zero to avoid "suspended" check
|
||||
}
|
||||
|
||||
result := gm.calculateGPUAverage("0", gpu, 5000)
|
||||
@@ -849,6 +850,31 @@ func TestCalculateGPUAverage(t *testing.T) {
|
||||
assert.Equal(t, 100.0, result.Power, "Should return cached average")
|
||||
})
|
||||
|
||||
t.Run("returns zero value when GPU is suspended", func(t *testing.T) {
|
||||
gm := &GPUManager{
|
||||
lastSnapshots: map[uint16]map[string]*gpuSnapshot{
|
||||
5000: {
|
||||
"0": {count: 10, usage: 100, power: 200},
|
||||
},
|
||||
},
|
||||
lastAvgData: map[string]system.GPUData{
|
||||
"0": {Usage: 50.0, Power: 100.0},
|
||||
},
|
||||
}
|
||||
|
||||
gpu := &system.GPUData{
|
||||
Name: "Test GPU",
|
||||
Count: 10.0,
|
||||
Temperature: 0,
|
||||
MemoryUsed: 0,
|
||||
}
|
||||
|
||||
result := gm.calculateGPUAverage("0", gpu, 5000)
|
||||
|
||||
assert.Equal(t, 0.0, result.Usage, "Should return zero usage")
|
||||
assert.Equal(t, 0.0, result.Power, "Should return zero power")
|
||||
})
|
||||
|
||||
t.Run("calculates average for standard GPU", func(t *testing.T) {
|
||||
gm := &GPUManager{
|
||||
lastSnapshots: map[uint16]map[string]*gpuSnapshot{
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/henrygd/beszel/internal/common"
|
||||
"github.com/henrygd/beszel/internal/entities/smart"
|
||||
|
||||
"golang.org/x/exp/slog"
|
||||
"log/slog"
|
||||
)
|
||||
|
||||
// HandlerContext provides context for request handlers
|
||||
@@ -94,7 +94,7 @@ func (h *GetDataHandler) Handle(hctx *HandlerContext) error {
|
||||
var options common.DataRequestOptions
|
||||
_ = cbor.Unmarshal(hctx.Request.Data, &options)
|
||||
|
||||
sysStats := hctx.Agent.gatherStats(options.CacheTimeMs)
|
||||
sysStats := hctx.Agent.gatherStats(options)
|
||||
return hctx.SendResponse(sysStats, hctx.RequestID)
|
||||
}
|
||||
|
||||
|
||||
31
agent/response.go
Normal file
31
agent/response.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"github.com/fxamacker/cbor/v2"
|
||||
"github.com/henrygd/beszel/internal/common"
|
||||
"github.com/henrygd/beszel/internal/entities/smart"
|
||||
"github.com/henrygd/beszel/internal/entities/system"
|
||||
"github.com/henrygd/beszel/internal/entities/systemd"
|
||||
)
|
||||
|
||||
// newAgentResponse creates an AgentResponse using legacy typed fields.
|
||||
// This maintains backward compatibility with <= 0.17 hubs that expect specific fields.
|
||||
func newAgentResponse(data any, requestID *uint32) common.AgentResponse {
|
||||
response := common.AgentResponse{Id: requestID}
|
||||
switch v := data.(type) {
|
||||
case *system.CombinedData:
|
||||
response.SystemData = v
|
||||
case *common.FingerprintResponse:
|
||||
response.Fingerprint = v
|
||||
case string:
|
||||
response.String = &v
|
||||
case map[string]smart.SmartData:
|
||||
response.SmartData = v
|
||||
case systemd.ServiceDetails:
|
||||
response.ServiceInfo = v
|
||||
default:
|
||||
// For unknown types, use the generic Data field
|
||||
response.Data, _ = cbor.Marshal(data)
|
||||
}
|
||||
return response
|
||||
}
|
||||
@@ -13,9 +13,7 @@ import (
|
||||
|
||||
"github.com/henrygd/beszel"
|
||||
"github.com/henrygd/beszel/internal/common"
|
||||
"github.com/henrygd/beszel/internal/entities/smart"
|
||||
"github.com/henrygd/beszel/internal/entities/system"
|
||||
"github.com/henrygd/beszel/internal/entities/systemd"
|
||||
|
||||
"github.com/blang/semver"
|
||||
"github.com/fxamacker/cbor/v2"
|
||||
@@ -165,20 +163,9 @@ func (a *Agent) handleSSHRequest(w io.Writer, req *common.HubRequest[cbor.RawMes
|
||||
}
|
||||
|
||||
// responder that writes AgentResponse to stdout
|
||||
// Uses legacy typed fields for backward compatibility with <= 0.17
|
||||
sshResponder := func(data any, requestID *uint32) error {
|
||||
response := common.AgentResponse{Id: requestID}
|
||||
switch v := data.(type) {
|
||||
case *system.CombinedData:
|
||||
response.SystemData = v
|
||||
case string:
|
||||
response.String = &v
|
||||
case map[string]smart.SmartData:
|
||||
response.SmartData = v
|
||||
case systemd.ServiceDetails:
|
||||
response.ServiceInfo = v
|
||||
default:
|
||||
response.Error = fmt.Sprintf("unsupported response type: %T", data)
|
||||
}
|
||||
response := newAgentResponse(data, requestID)
|
||||
return cbor.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
@@ -202,7 +189,7 @@ func (a *Agent) handleSSHRequest(w io.Writer, req *common.HubRequest[cbor.RawMes
|
||||
|
||||
// handleLegacyStats serves the legacy one-shot stats payload for older hubs
|
||||
func (a *Agent) handleLegacyStats(w io.Writer, hubVersion semver.Version) error {
|
||||
stats := a.gatherStats(60_000)
|
||||
stats := a.gatherStats(common.DataRequestOptions{CacheTimeMs: 60_000})
|
||||
return a.writeToSession(w, stats, hubVersion)
|
||||
}
|
||||
|
||||
|
||||
@@ -513,7 +513,7 @@ func TestWriteToSessionEncoding(t *testing.T) {
|
||||
err = json.Unmarshal([]byte(encodedData), &decodedJson)
|
||||
assert.Error(t, err, "Should not be valid JSON data")
|
||||
|
||||
assert.Equal(t, testData.Info.Hostname, decodedCbor.Info.Hostname)
|
||||
assert.Equal(t, testData.Details.Hostname, decodedCbor.Details.Hostname)
|
||||
assert.Equal(t, testData.Stats.Cpu, decodedCbor.Stats.Cpu)
|
||||
} else {
|
||||
// Should be JSON - try to decode as JSON
|
||||
@@ -526,7 +526,7 @@ func TestWriteToSessionEncoding(t *testing.T) {
|
||||
assert.Error(t, err, "Should not be valid CBOR data")
|
||||
|
||||
// Verify the decoded JSON data matches our test data
|
||||
assert.Equal(t, testData.Info.Hostname, decodedJson.Info.Hostname)
|
||||
assert.Equal(t, testData.Details.Hostname, decodedJson.Details.Hostname)
|
||||
assert.Equal(t, testData.Stats.Cpu, decodedJson.Stats.Cpu)
|
||||
|
||||
// Verify it looks like JSON (starts with '{' and contains readable field names)
|
||||
@@ -550,13 +550,12 @@ func createTestCombinedData() *system.CombinedData {
|
||||
DiskUsed: 549755813888, // 512GB
|
||||
DiskPct: 50.0,
|
||||
},
|
||||
Details: &system.Details{
|
||||
Hostname: "test-host",
|
||||
},
|
||||
Info: system.Info{
|
||||
Hostname: "test-host",
|
||||
Cores: 8,
|
||||
CpuModel: "Test CPU Model",
|
||||
Uptime: 3600,
|
||||
AgentVersion: "0.12.0",
|
||||
Os: system.Linux,
|
||||
},
|
||||
Containers: []*container.Stats{
|
||||
{
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -18,7 +19,7 @@ import (
|
||||
|
||||
"github.com/henrygd/beszel/internal/entities/smart"
|
||||
|
||||
"golang.org/x/exp/slog"
|
||||
"log/slog"
|
||||
)
|
||||
|
||||
// SmartManager manages data collection for SMART devices
|
||||
@@ -430,7 +431,7 @@ func (sm *SmartManager) CollectSmart(deviceInfo *DeviceInfo) error {
|
||||
// Check if we have any existing data for this device
|
||||
hasExistingData := sm.hasDataForDevice(deviceInfo.Name)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// Try with -n standby first if we have existing data
|
||||
@@ -445,7 +446,7 @@ func (sm *SmartManager) CollectSmart(deviceInfo *DeviceInfo) error {
|
||||
return nil
|
||||
}
|
||||
// No cached data, need to collect initial data by bypassing standby
|
||||
ctx2, cancel2 := context.WithTimeout(context.Background(), 2*time.Second)
|
||||
ctx2, cancel2 := context.WithTimeout(context.Background(), 15*time.Second)
|
||||
defer cancel2()
|
||||
args = sm.smartctlArgs(deviceInfo, false)
|
||||
cmd = exec.CommandContext(ctx2, sm.binPath, args...)
|
||||
@@ -454,6 +455,34 @@ func (sm *SmartManager) CollectSmart(deviceInfo *DeviceInfo) error {
|
||||
|
||||
hasValidData := sm.parseSmartOutput(deviceInfo, output)
|
||||
|
||||
// If NVMe controller path failed, try namespace path as fallback.
|
||||
// NVMe controllers (/dev/nvme0) don't always support SMART queries. See github.com/henrygd/beszel/issues/1504
|
||||
if !hasValidData && err != nil && isNvmeControllerPath(deviceInfo.Name) {
|
||||
controllerPath := deviceInfo.Name
|
||||
namespacePath := controllerPath + "n1"
|
||||
if !sm.isExcludedDevice(namespacePath) {
|
||||
deviceInfo.Name = namespacePath
|
||||
|
||||
ctx3, cancel3 := context.WithTimeout(context.Background(), 15*time.Second)
|
||||
defer cancel3()
|
||||
args = sm.smartctlArgs(deviceInfo, false)
|
||||
cmd = exec.CommandContext(ctx3, sm.binPath, args...)
|
||||
output, err = cmd.CombinedOutput()
|
||||
hasValidData = sm.parseSmartOutput(deviceInfo, output)
|
||||
|
||||
// Auto-exclude the controller path so future scans don't re-add it
|
||||
if hasValidData {
|
||||
sm.Lock()
|
||||
if sm.excludedDevices == nil {
|
||||
sm.excludedDevices = make(map[string]struct{})
|
||||
}
|
||||
sm.excludedDevices[controllerPath] = struct{}{}
|
||||
sm.Unlock()
|
||||
slog.Debug("auto-excluded NVMe controller path", "path", controllerPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !hasValidData {
|
||||
if err != nil {
|
||||
slog.Info("smartctl failed", "device", deviceInfo.Name, "err", err)
|
||||
@@ -957,6 +986,27 @@ func (sm *SmartManager) detectSmartctl() (string, error) {
|
||||
return "", errors.New("smartctl not found")
|
||||
}
|
||||
|
||||
// isNvmeControllerPath checks if the path matches an NVMe controller pattern
|
||||
// like /dev/nvme0, /dev/nvme1, etc. (without namespace suffix like n1)
|
||||
func isNvmeControllerPath(path string) bool {
|
||||
base := filepath.Base(path)
|
||||
if !strings.HasPrefix(base, "nvme") {
|
||||
return false
|
||||
}
|
||||
suffix := strings.TrimPrefix(base, "nvme")
|
||||
if suffix == "" {
|
||||
return false
|
||||
}
|
||||
// Controller paths are just "nvme" + digits (e.g., nvme0, nvme1)
|
||||
// Namespace paths have "n" after the controller number (e.g., nvme0n1)
|
||||
for _, c := range suffix {
|
||||
if c < '0' || c > '9' {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// NewSmartManager creates and initializes a new SmartManager
|
||||
func NewSmartManager() (*SmartManager, error) {
|
||||
sm := &SmartManager{
|
||||
|
||||
@@ -780,3 +780,36 @@ func TestFilterExcludedDevices(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsNvmeControllerPath(t *testing.T) {
|
||||
tests := []struct {
|
||||
path string
|
||||
expected bool
|
||||
}{
|
||||
// Controller paths (should return true)
|
||||
{"/dev/nvme0", true},
|
||||
{"/dev/nvme1", true},
|
||||
{"/dev/nvme10", true},
|
||||
{"nvme0", true},
|
||||
|
||||
// Namespace paths (should return false)
|
||||
{"/dev/nvme0n1", false},
|
||||
{"/dev/nvme1n1", false},
|
||||
{"/dev/nvme0n1p1", false},
|
||||
{"nvme0n1", false},
|
||||
|
||||
// Non-NVMe paths (should return false)
|
||||
{"/dev/sda", false},
|
||||
{"/dev/sda1", false},
|
||||
{"/dev/hda", false},
|
||||
{"", false},
|
||||
{"/dev/nvme", false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.path, func(t *testing.T) {
|
||||
result := isNvmeControllerPath(tt.path)
|
||||
assert.Equal(t, tt.expected, result, "path: %s", tt.path)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
115
agent/system.go
115
agent/system.go
@@ -2,15 +2,18 @@ package agent
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/henrygd/beszel"
|
||||
"github.com/henrygd/beszel/agent/battery"
|
||||
"github.com/henrygd/beszel/internal/entities/container"
|
||||
"github.com/henrygd/beszel/internal/entities/system"
|
||||
|
||||
"github.com/shirou/gopsutil/v4/cpu"
|
||||
@@ -27,41 +30,79 @@ type prevDisk struct {
|
||||
}
|
||||
|
||||
// Sets initial / non-changing values about the host system
|
||||
func (a *Agent) initializeSystemInfo() {
|
||||
func (a *Agent) refreshSystemDetails() {
|
||||
a.systemInfo.AgentVersion = beszel.Version
|
||||
a.systemInfo.Hostname, _ = os.Hostname()
|
||||
|
||||
// get host info from Docker if available
|
||||
var hostInfo container.HostInfo
|
||||
|
||||
if a.dockerManager != nil {
|
||||
a.systemDetails.Podman = a.dockerManager.IsPodman()
|
||||
hostInfo, _ = a.dockerManager.GetHostInfo()
|
||||
}
|
||||
|
||||
a.systemDetails.Hostname, _ = os.Hostname()
|
||||
if arch, err := host.KernelArch(); err == nil {
|
||||
a.systemDetails.Arch = arch
|
||||
} else {
|
||||
a.systemDetails.Arch = runtime.GOARCH
|
||||
}
|
||||
|
||||
platform, _, version, _ := host.PlatformInformation()
|
||||
|
||||
if platform == "darwin" {
|
||||
a.systemInfo.KernelVersion = version
|
||||
a.systemInfo.Os = system.Darwin
|
||||
a.systemDetails.Os = system.Darwin
|
||||
a.systemDetails.OsName = fmt.Sprintf("macOS %s", version)
|
||||
} else if strings.Contains(platform, "indows") {
|
||||
a.systemInfo.KernelVersion = fmt.Sprintf("%s %s", strings.Replace(platform, "Microsoft ", "", 1), version)
|
||||
a.systemInfo.Os = system.Windows
|
||||
a.systemDetails.Os = system.Windows
|
||||
a.systemDetails.OsName = strings.Replace(platform, "Microsoft ", "", 1)
|
||||
a.systemDetails.Kernel = version
|
||||
} else if platform == "freebsd" {
|
||||
a.systemInfo.Os = system.Freebsd
|
||||
a.systemInfo.KernelVersion = version
|
||||
a.systemDetails.Os = system.Freebsd
|
||||
a.systemDetails.Kernel, _ = host.KernelVersion()
|
||||
if prettyName, err := getOsPrettyName(); err == nil {
|
||||
a.systemDetails.OsName = prettyName
|
||||
} else {
|
||||
a.systemDetails.OsName = "FreeBSD"
|
||||
}
|
||||
} else {
|
||||
a.systemInfo.Os = system.Linux
|
||||
}
|
||||
|
||||
if a.systemInfo.KernelVersion == "" {
|
||||
a.systemInfo.KernelVersion, _ = host.KernelVersion()
|
||||
a.systemDetails.Os = system.Linux
|
||||
a.systemDetails.OsName = hostInfo.OperatingSystem
|
||||
if a.systemDetails.OsName == "" {
|
||||
if prettyName, err := getOsPrettyName(); err == nil {
|
||||
a.systemDetails.OsName = prettyName
|
||||
} else {
|
||||
a.systemDetails.OsName = platform
|
||||
}
|
||||
}
|
||||
a.systemDetails.Kernel = hostInfo.KernelVersion
|
||||
if a.systemDetails.Kernel == "" {
|
||||
a.systemDetails.Kernel, _ = host.KernelVersion()
|
||||
}
|
||||
}
|
||||
|
||||
// cpu model
|
||||
if info, err := cpu.Info(); err == nil && len(info) > 0 {
|
||||
a.systemInfo.CpuModel = info[0].ModelName
|
||||
a.systemDetails.CpuModel = info[0].ModelName
|
||||
}
|
||||
// cores / threads
|
||||
a.systemInfo.Cores, _ = cpu.Counts(false)
|
||||
if threads, err := cpu.Counts(true); err == nil {
|
||||
if threads > 0 && threads < a.systemInfo.Cores {
|
||||
// in lxc logical cores reflects container limits, so use that as cores if lower
|
||||
a.systemInfo.Cores = threads
|
||||
} else {
|
||||
a.systemInfo.Threads = threads
|
||||
cores, _ := cpu.Counts(false)
|
||||
threads := hostInfo.NCPU
|
||||
if threads == 0 {
|
||||
threads, _ = cpu.Counts(true)
|
||||
}
|
||||
// in lxc, logical cores reflects container limits, so use that as cores if lower
|
||||
if threads > 0 && threads < cores {
|
||||
cores = threads
|
||||
}
|
||||
a.systemDetails.Cores = cores
|
||||
a.systemDetails.Threads = threads
|
||||
|
||||
// total memory
|
||||
a.systemDetails.MemoryTotal = hostInfo.MemTotal
|
||||
if a.systemDetails.MemoryTotal == 0 {
|
||||
if v, err := mem.VirtualMemory(); err == nil {
|
||||
a.systemDetails.MemoryTotal = v.Total
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,21 +236,16 @@ func (a *Agent) getSystemStats(cacheTimeMs uint16) system.Stats {
|
||||
}
|
||||
}
|
||||
|
||||
// update base system info
|
||||
// update system info
|
||||
a.systemInfo.ConnectionType = a.connectionManager.ConnectionType
|
||||
a.systemInfo.Cpu = systemStats.Cpu
|
||||
a.systemInfo.LoadAvg = systemStats.LoadAvg
|
||||
// TODO: remove these in future release in favor of load avg array
|
||||
a.systemInfo.LoadAvg1 = systemStats.LoadAvg[0]
|
||||
a.systemInfo.LoadAvg5 = systemStats.LoadAvg[1]
|
||||
a.systemInfo.LoadAvg15 = systemStats.LoadAvg[2]
|
||||
a.systemInfo.MemPct = systemStats.MemPct
|
||||
a.systemInfo.DiskPct = systemStats.DiskPct
|
||||
a.systemInfo.Battery = systemStats.Battery
|
||||
a.systemInfo.Uptime, _ = host.Uptime()
|
||||
// TODO: in future release, remove MB bandwidth values in favor of bytes
|
||||
a.systemInfo.Bandwidth = twoDecimals(systemStats.NetworkSent + systemStats.NetworkRecv)
|
||||
a.systemInfo.BandwidthBytes = systemStats.Bandwidth[0] + systemStats.Bandwidth[1]
|
||||
slog.Debug("sysinfo", "data", a.systemInfo)
|
||||
a.systemInfo.Threads = a.systemDetails.Threads
|
||||
|
||||
return systemStats
|
||||
}
|
||||
@@ -239,3 +275,24 @@ func getARCSize() (uint64, error) {
|
||||
|
||||
return 0, fmt.Errorf("failed to parse size field")
|
||||
}
|
||||
|
||||
// getOsPrettyName attempts to get the pretty OS name from /etc/os-release on Linux systems
|
||||
func getOsPrettyName() (string, error) {
|
||||
file, err := os.Open("/etc/os-release")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if after, ok := strings.CutPrefix(line, "PRETTY_NAME="); ok {
|
||||
value := after
|
||||
value = strings.Trim(value, `"`)
|
||||
return value, nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", errors.New("pretty name not found")
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"log/slog"
|
||||
"maps"
|
||||
"math"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -28,11 +29,36 @@ type systemdManager struct {
|
||||
patterns []string
|
||||
}
|
||||
|
||||
// isSystemdAvailable checks if systemd is used on the system to avoid unnecessary connection attempts (#1548)
|
||||
func isSystemdAvailable() bool {
|
||||
paths := []string{
|
||||
"/run/systemd/system",
|
||||
"/run/dbus/system_bus_socket",
|
||||
"/var/run/dbus/system_bus_socket",
|
||||
}
|
||||
for _, path := range paths {
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if data, err := os.ReadFile("/proc/1/comm"); err == nil {
|
||||
return strings.TrimSpace(string(data)) == "systemd"
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// newSystemdManager creates a new systemdManager.
|
||||
func newSystemdManager() (*systemdManager, error) {
|
||||
if skipSystemd, _ := GetEnv("SKIP_SYSTEMD"); skipSystemd == "true" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Check if systemd is available on the system before attempting connection
|
||||
if !isSystemdAvailable() {
|
||||
slog.Debug("Systemd not available")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
conn, err := dbus.NewSystemConnectionContext(context.Background())
|
||||
if err != nil {
|
||||
slog.Debug("Error connecting to systemd", "err", err, "ref", "https://beszel.dev/guide/systemd")
|
||||
|
||||
@@ -4,6 +4,7 @@ package agent
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -48,6 +49,35 @@ func TestUnescapeServiceNameInvalid(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsSystemdAvailable(t *testing.T) {
|
||||
// Note: This test's result will vary based on the actual system running the tests
|
||||
// On systems with systemd, it should return true
|
||||
// On systems without systemd, it should return false
|
||||
result := isSystemdAvailable()
|
||||
|
||||
// Check if either the /run/systemd/system directory exists or PID 1 is systemd
|
||||
runSystemdExists := false
|
||||
if _, err := os.Stat("/run/systemd/system"); err == nil {
|
||||
runSystemdExists = true
|
||||
}
|
||||
|
||||
pid1IsSystemd := false
|
||||
if data, err := os.ReadFile("/proc/1/comm"); err == nil {
|
||||
pid1IsSystemd = strings.TrimSpace(string(data)) == "systemd"
|
||||
}
|
||||
|
||||
expected := runSystemdExists || pid1IsSystemd
|
||||
|
||||
assert.Equal(t, expected, result, "isSystemdAvailable should correctly detect systemd presence")
|
||||
|
||||
// Log the result for informational purposes
|
||||
if result {
|
||||
t.Log("Systemd is available on this system")
|
||||
} else {
|
||||
t.Log("Systemd is not available on this system")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetServicePatterns(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
|
||||
17
agent/test-data/system_info.json
Normal file
17
agent/test-data/system_info.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"ID": "7TRN:IPZB:QYBB:VPBQ:UMPP:KARE:6ZNR:XE6T:7EWV:PKF4:ZOJD:TPYS",
|
||||
"Containers": 14,
|
||||
"ContainersRunning": 3,
|
||||
"ContainersPaused": 1,
|
||||
"ContainersStopped": 10,
|
||||
"Images": 508,
|
||||
"Driver": "overlay2",
|
||||
"KernelVersion": "6.8.0-31-generic",
|
||||
"OperatingSystem": "Ubuntu 24.04 LTS",
|
||||
"OSVersion": "24.04",
|
||||
"OSType": "linux",
|
||||
"Architecture": "x86_64",
|
||||
"NCPU": 4,
|
||||
"MemTotal": 2095882240,
|
||||
"ServerVersion": "27.0.1"
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import "github.com/blang/semver"
|
||||
|
||||
const (
|
||||
// Version is the current version of the application.
|
||||
Version = "0.16.1"
|
||||
Version = "0.18.0"
|
||||
// AppName is the name of the application.
|
||||
AppName = "beszel"
|
||||
)
|
||||
|
||||
38
go.mod
38
go.mod
@@ -1,25 +1,27 @@
|
||||
module github.com/henrygd/beszel
|
||||
|
||||
go 1.25.3
|
||||
go 1.25.5
|
||||
|
||||
require (
|
||||
github.com/blang/semver v3.5.1+incompatible
|
||||
github.com/coreos/go-systemd/v22 v22.6.0
|
||||
github.com/distatus/battery v0.11.0
|
||||
github.com/ebitengine/purego v0.9.1
|
||||
github.com/fxamacker/cbor/v2 v2.9.0
|
||||
github.com/gliderlabs/ssh v0.3.8
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/lxzan/gws v1.8.9
|
||||
github.com/nicholas-fedor/shoutrrr v0.12.1
|
||||
github.com/nicholas-fedor/shoutrrr v0.13.1
|
||||
github.com/pocketbase/dbx v1.11.0
|
||||
github.com/pocketbase/pocketbase v0.34.0
|
||||
github.com/shirou/gopsutil/v4 v4.25.10
|
||||
github.com/pocketbase/pocketbase v0.35.1
|
||||
github.com/shirou/gopsutil/v4 v4.25.12
|
||||
github.com/spf13/cast v1.10.0
|
||||
github.com/spf13/cobra v1.10.1
|
||||
github.com/spf13/cobra v1.10.2
|
||||
github.com/spf13/pflag v1.0.10
|
||||
github.com/stretchr/testify v1.11.1
|
||||
golang.org/x/crypto v0.45.0
|
||||
golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39
|
||||
golang.org/x/crypto v0.46.0
|
||||
golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93
|
||||
golang.org/x/sys v0.40.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
@@ -31,17 +33,16 @@ require (
|
||||
github.com/dolthub/maphash v0.1.0 // indirect
|
||||
github.com/domodwyer/mailyak/v3 v3.6.2 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/ebitengine/purego v0.9.1 // indirect
|
||||
github.com/fatih/color v1.18.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.11 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.12 // indirect
|
||||
github.com/ganigeorgiev/fexpr v0.5.0 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/go-ozzo/ozzo-validation/v4 v4.3.0 // indirect
|
||||
github.com/go-sql-driver/mysql v1.9.1 // indirect
|
||||
github.com/godbus/dbus/v5 v5.2.0 // indirect
|
||||
github.com/godbus/dbus/v5 v5.2.2 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/klauspost/compress v1.18.1 // indirect
|
||||
github.com/klauspost/compress v1.18.2 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
@@ -53,16 +54,15 @@ require (
|
||||
github.com/tklauser/numcpus v0.11.0 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
golang.org/x/image v0.33.0 // indirect
|
||||
golang.org/x/net v0.47.0 // indirect
|
||||
golang.org/x/oauth2 v0.33.0 // indirect
|
||||
golang.org/x/sync v0.18.0 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
golang.org/x/term v0.37.0 // indirect
|
||||
golang.org/x/text v0.31.0 // indirect
|
||||
golang.org/x/image v0.34.0 // indirect
|
||||
golang.org/x/net v0.48.0 // indirect
|
||||
golang.org/x/oauth2 v0.34.0 // indirect
|
||||
golang.org/x/sync v0.19.0 // indirect
|
||||
golang.org/x/term v0.39.0 // indirect
|
||||
golang.org/x/text v0.33.0 // indirect
|
||||
howett.net/plist v1.0.1 // indirect
|
||||
modernc.org/libc v1.66.10 // indirect
|
||||
modernc.org/mathutil v1.7.1 // indirect
|
||||
modernc.org/memory v1.11.0 // indirect
|
||||
modernc.org/sqlite v1.40.1 // indirect
|
||||
modernc.org/sqlite v1.43.0 // indirect
|
||||
)
|
||||
|
||||
88
go.sum
88
go.sum
@@ -33,8 +33,8 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
|
||||
github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
|
||||
github.com/gabriel-vasile/mimetype v1.4.11 h1:AQvxbp830wPhHTqc1u7nzoLT+ZFxGY7emj5DR5DYFik=
|
||||
github.com/gabriel-vasile/mimetype v1.4.11/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
||||
github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw=
|
||||
github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
||||
github.com/ganigeorgiev/fexpr v0.5.0 h1:XA9JxtTE/Xm+g/JFI6RfZEHSiQlk+1glLvRK1Lpv/Tk=
|
||||
github.com/ganigeorgiev/fexpr v0.5.0/go.mod h1:RyGiGqmeXhEQ6+mlGdnUleLHgtzzu/VGO2WtJkF5drE=
|
||||
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
|
||||
@@ -51,8 +51,8 @@ github.com/go-sql-driver/mysql v1.9.1 h1:FrjNGn/BsJQjVRuSa8CBrM5BWA9BWoXXat3KrtS
|
||||
github.com/go-sql-driver/mysql v1.9.1/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/godbus/dbus/v5 v5.2.0 h1:3WexO+U+yg9T70v9FdHr9kCxYlazaAXUhx2VMkbfax8=
|
||||
github.com/godbus/dbus/v5 v5.2.0/go.mod h1:3AAv2+hPq5rdnr5txxxRwiGjPXamgoIHgz9FPBfOp3c=
|
||||
github.com/godbus/dbus/v5 v5.2.2 h1:TUR3TgtSVDmjiXOgAAyaZbYmIeP3DPkld3jgKGV8mXQ=
|
||||
github.com/godbus/dbus/v5 v5.2.2/go.mod h1:3AAv2+hPq5rdnr5txxxRwiGjPXamgoIHgz9FPBfOp3c=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
@@ -69,8 +69,8 @@ github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLf
|
||||
github.com/jarcoal/httpmock v1.4.1 h1:0Ju+VCFuARfFlhVXFc2HxlcQkfB+Xq12/EotHko+x2A=
|
||||
github.com/jarcoal/httpmock v1.4.1/go.mod h1:ftW1xULwo+j0R0JJkJIIi7UKigZUXCLLanykgjwBXL0=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/klauspost/compress v1.18.1 h1:bcSGx7UbpBqMChDtsF28Lw6v/G94LPrrbMbdC3JH2co=
|
||||
github.com/klauspost/compress v1.18.1/go.mod h1:ZQFFVG+MdnR0P+l6wpXgIL4NTtwiKIdBnrBd8Nrxr+0=
|
||||
github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=
|
||||
github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
@@ -85,19 +85,19 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
|
||||
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/nicholas-fedor/shoutrrr v0.12.1 h1:8NjY+I3K7cGHy89ncnaPGUA0ex44XbYK3SAFJX9YMI8=
|
||||
github.com/nicholas-fedor/shoutrrr v0.12.1/go.mod h1:64qWuPpvTUv9ZppEoR6OdroiFmgf9w11YSaR0h9KZGg=
|
||||
github.com/onsi/ginkgo/v2 v2.27.2 h1:LzwLj0b89qtIy6SSASkzlNvX6WktqurSHwkk2ipF/Ns=
|
||||
github.com/onsi/ginkgo/v2 v2.27.2/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo=
|
||||
github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A=
|
||||
github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k=
|
||||
github.com/nicholas-fedor/shoutrrr v0.13.1 h1:llEoHNbnMM4GfQ9+2Ns3n6ssvNfi3NPWluM0AQiicoY=
|
||||
github.com/nicholas-fedor/shoutrrr v0.13.1/go.mod h1:kU4cFJpEAtTzl3iV0l+XUXmM90OlC5T01b7roM4/pYM=
|
||||
github.com/onsi/ginkgo/v2 v2.27.3 h1:ICsZJ8JoYafeXFFlFAG75a7CxMsJHwgKwtO+82SE9L8=
|
||||
github.com/onsi/ginkgo/v2 v2.27.3/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo=
|
||||
github.com/onsi/gomega v1.38.3 h1:eTX+W6dobAYfFeGC2PV6RwXRu/MyT+cQguijutvkpSM=
|
||||
github.com/onsi/gomega v1.38.3/go.mod h1:ZCU1pkQcXDO5Sl9/VVEGlDyp+zm0m1cmeG5TOzLgdh4=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pocketbase/dbx v1.11.0 h1:LpZezioMfT3K4tLrqA55wWFw1EtH1pM4tzSVa7kgszU=
|
||||
github.com/pocketbase/dbx v1.11.0/go.mod h1:xXRCIAKTHMgUCyCKZm55pUOdvFziJjQfXaWKhu2vhMs=
|
||||
github.com/pocketbase/pocketbase v0.34.0 h1:5W80PrGvkRYIMAIK90F7w031/hXgZVz1KSuCJqSpgJo=
|
||||
github.com/pocketbase/pocketbase v0.34.0/go.mod h1:K/9z/Zb9PR9yW2Qyoc73jHV/EKT8cMTk9bQWyrzYlvI=
|
||||
github.com/pocketbase/pocketbase v0.35.1 h1:Cd5ivUThTw29myY/tYa2cb0elkScBMseG6fExZsIQB8=
|
||||
github.com/pocketbase/pocketbase v0.35.1/go.mod h1:yQnh1o1Aq6wVuqcmZbRbDmIhc31AME/F5pnPR0Bdtmg=
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
@@ -105,12 +105,12 @@ github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qq
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/shirou/gopsutil/v4 v4.25.10 h1:at8lk/5T1OgtuCp+AwrDofFRjnvosn0nkN2OLQ6g8tA=
|
||||
github.com/shirou/gopsutil/v4 v4.25.10/go.mod h1:+kSwyC8DRUD9XXEHCAFjK+0nuArFJM0lva+StQAcskM=
|
||||
github.com/shirou/gopsutil/v4 v4.25.12 h1:e7PvW/0RmJ8p8vPGJH4jvNkOyLmbkXgXW4m6ZPic6CY=
|
||||
github.com/shirou/gopsutil/v4 v4.25.12/go.mod h1:EivAfP5x2EhLp2ovdpKSozecVXn1TmuG7SMzs/Wh4PU=
|
||||
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
|
||||
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
|
||||
github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=
|
||||
github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=
|
||||
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
|
||||
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
|
||||
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
@@ -129,38 +129,38 @@ github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQ
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
||||
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
||||
golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39 h1:DHNhtq3sNNzrvduZZIiFyXWOL9IWaDPHqTnLJp+rCBY=
|
||||
golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39/go.mod h1:46edojNIoXTNOhySWIWdix628clX9ODXwPsQuG6hsK0=
|
||||
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
|
||||
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
|
||||
golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0=
|
||||
golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU=
|
||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.33.0 h1:LXRZRnv1+zGd5XBUVRFmYEphyyKJjQjCRiOuAP3sZfQ=
|
||||
golang.org/x/image v0.33.0/go.mod h1:DD3OsTYT9chzuzTQt+zMcOlBHgfoKQb1gry8p76Y1sc=
|
||||
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
|
||||
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
|
||||
golang.org/x/image v0.34.0 h1:33gCkyw9hmwbZJeZkct8XyR11yH889EQt/QH4VmXMn8=
|
||||
golang.org/x/image v0.34.0/go.mod h1:2RNFBZRB+vnwwFil8GkMdRvrJOFd1AzdZI6vOY+eJVU=
|
||||
golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=
|
||||
golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||
golang.org/x/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo=
|
||||
golang.org/x/oauth2 v0.33.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
||||
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
|
||||
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
||||
golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
|
||||
golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
|
||||
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
|
||||
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
||||
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY=
|
||||
golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
|
||||
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
|
||||
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
|
||||
golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA=
|
||||
golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
@@ -187,8 +187,8 @@ modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
|
||||
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
|
||||
modernc.org/libc v1.66.10 h1:yZkb3YeLx4oynyR+iUsXsybsX4Ubx7MQlSYEw4yj59A=
|
||||
modernc.org/libc v1.66.10/go.mod h1:8vGSEwvoUoltr4dlywvHqjtAqHBaw0j1jI7iFBTAr2I=
|
||||
modernc.org/libc v1.67.1 h1:bFaqOaa5/zbWYJo8aW0tXPX21hXsngG2M7mckCnFSVk=
|
||||
modernc.org/libc v1.67.1/go.mod h1:QvvnnJ5P7aitu0ReNpVIEyesuhmDLQ8kaEoyMjIFZJA=
|
||||
modernc.org/libc v1.67.4 h1:zZGmCMUVPORtKv95c2ReQN5VDjvkoRm9GWPTEPuvlWg=
|
||||
modernc.org/libc v1.67.4/go.mod h1:QvvnnJ5P7aitu0ReNpVIEyesuhmDLQ8kaEoyMjIFZJA=
|
||||
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
|
||||
@@ -197,8 +197,8 @@ modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
|
||||
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
||||
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
|
||||
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
|
||||
modernc.org/sqlite v1.40.1 h1:VfuXcxcUWWKRBuP8+BR9L7VnmusMgBNNnBYGEe9w/iY=
|
||||
modernc.org/sqlite v1.40.1/go.mod h1:9fjQZ0mB1LLP0GYrp39oOJXx/I2sxEnZtzCmEQIKvGE=
|
||||
modernc.org/sqlite v1.43.0 h1:8YqiFx3G1VhHTXO2Q00bl1Wz9KhS9Q5okwfp9Y97VnA=
|
||||
modernc.org/sqlite v1.43.0/go.mod h1:+VkC6v3pLOAE0A0uVucQEcbVW0I5nHCeDaBf+DpsQT8=
|
||||
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
|
||||
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
|
||||
@@ -49,6 +49,7 @@ type SystemAlertStats struct {
|
||||
GPU map[string]SystemAlertGPUData `json:"g"`
|
||||
Temperatures map[string]float32 `json:"t"`
|
||||
LoadAvg [3]float64 `json:"la"`
|
||||
Battery [2]uint8 `json:"bat"`
|
||||
}
|
||||
|
||||
type SystemAlertGPUData struct {
|
||||
|
||||
387
internal/alerts/alerts_battery_test.go
Normal file
387
internal/alerts/alerts_battery_test.go
Normal file
@@ -0,0 +1,387 @@
|
||||
//go:build testing
|
||||
// +build testing
|
||||
|
||||
package alerts_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/henrygd/beszel/internal/entities/system"
|
||||
beszelTests "github.com/henrygd/beszel/internal/tests"
|
||||
|
||||
"github.com/pocketbase/dbx"
|
||||
"github.com/pocketbase/pocketbase/tools/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// TestBatteryAlertLogic tests that battery alerts trigger when value drops BELOW threshold
|
||||
// (opposite of other alerts like CPU, Memory, etc. which trigger when exceeding threshold)
|
||||
func TestBatteryAlertLogic(t *testing.T) {
|
||||
hub, user := beszelTests.GetHubWithUser(t)
|
||||
defer hub.Cleanup()
|
||||
|
||||
// Create a system
|
||||
systems, err := beszelTests.CreateSystems(hub, 1, user.Id, "up")
|
||||
require.NoError(t, err)
|
||||
systemRecord := systems[0]
|
||||
|
||||
// Create a battery alert with threshold of 20% and min of 1 minute (immediate trigger)
|
||||
batteryAlert, err := beszelTests.CreateRecord(hub, "alerts", map[string]any{
|
||||
"name": "Battery",
|
||||
"system": systemRecord.Id,
|
||||
"user": user.Id,
|
||||
"value": 20, // threshold: 20%
|
||||
"min": 1, // 1 minute (immediate trigger for testing)
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify alert is not triggered initially
|
||||
assert.False(t, batteryAlert.GetBool("triggered"), "Alert should not be triggered initially")
|
||||
|
||||
// Create system stats with battery at 50% (above threshold - should NOT trigger)
|
||||
statsHigh := system.Stats{
|
||||
Cpu: 10,
|
||||
MemPct: 30,
|
||||
DiskPct: 40,
|
||||
Battery: [2]uint8{50, 1}, // 50% battery, discharging
|
||||
}
|
||||
statsHighJSON, _ := json.Marshal(statsHigh)
|
||||
_, err = beszelTests.CreateRecord(hub, "system_stats", map[string]any{
|
||||
"system": systemRecord.Id,
|
||||
"type": "1m",
|
||||
"stats": string(statsHighJSON),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create CombinedData for the alert handler
|
||||
combinedDataHigh := &system.CombinedData{
|
||||
Stats: statsHigh,
|
||||
Info: system.Info{
|
||||
AgentVersion: "0.12.0",
|
||||
Cpu: 10,
|
||||
MemPct: 30,
|
||||
DiskPct: 40,
|
||||
},
|
||||
}
|
||||
|
||||
// Simulate system update time
|
||||
systemRecord.Set("updated", time.Now().UTC())
|
||||
err = hub.SaveNoValidate(systemRecord)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Handle system alerts with high battery
|
||||
am := hub.GetAlertManager()
|
||||
err = am.HandleSystemAlerts(systemRecord, combinedDataHigh)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify alert is still NOT triggered (battery 50% is above threshold 20%)
|
||||
batteryAlert, err = hub.FindFirstRecordByFilter("alerts", "id={:id}", dbx.Params{"id": batteryAlert.Id})
|
||||
require.NoError(t, err)
|
||||
assert.False(t, batteryAlert.GetBool("triggered"), "Alert should NOT be triggered when battery (50%%) is above threshold (20%%)")
|
||||
|
||||
// Now create stats with battery at 15% (below threshold - should trigger)
|
||||
statsLow := system.Stats{
|
||||
Cpu: 10,
|
||||
MemPct: 30,
|
||||
DiskPct: 40,
|
||||
Battery: [2]uint8{15, 1}, // 15% battery, discharging
|
||||
}
|
||||
statsLowJSON, _ := json.Marshal(statsLow)
|
||||
_, err = beszelTests.CreateRecord(hub, "system_stats", map[string]any{
|
||||
"system": systemRecord.Id,
|
||||
"type": "1m",
|
||||
"stats": string(statsLowJSON),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
combinedDataLow := &system.CombinedData{
|
||||
Stats: statsLow,
|
||||
Info: system.Info{
|
||||
AgentVersion: "0.12.0",
|
||||
Cpu: 10,
|
||||
MemPct: 30,
|
||||
DiskPct: 40,
|
||||
},
|
||||
}
|
||||
|
||||
// Update system timestamp
|
||||
systemRecord.Set("updated", time.Now().UTC())
|
||||
err = hub.SaveNoValidate(systemRecord)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Handle system alerts with low battery
|
||||
err = am.HandleSystemAlerts(systemRecord, combinedDataLow)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Wait for the alert to be processed
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
|
||||
// Verify alert IS triggered (battery 15% is below threshold 20%)
|
||||
batteryAlert, err = hub.FindFirstRecordByFilter("alerts", "id={:id}", dbx.Params{"id": batteryAlert.Id})
|
||||
require.NoError(t, err)
|
||||
assert.True(t, batteryAlert.GetBool("triggered"), "Alert SHOULD be triggered when battery (15%%) drops below threshold (20%%)")
|
||||
|
||||
// Now test resolution: battery goes back above threshold
|
||||
statsRecovered := system.Stats{
|
||||
Cpu: 10,
|
||||
MemPct: 30,
|
||||
DiskPct: 40,
|
||||
Battery: [2]uint8{25, 1}, // 25% battery, discharging
|
||||
}
|
||||
statsRecoveredJSON, _ := json.Marshal(statsRecovered)
|
||||
_, err = beszelTests.CreateRecord(hub, "system_stats", map[string]any{
|
||||
"system": systemRecord.Id,
|
||||
"type": "1m",
|
||||
"stats": string(statsRecoveredJSON),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
combinedDataRecovered := &system.CombinedData{
|
||||
Stats: statsRecovered,
|
||||
Info: system.Info{
|
||||
AgentVersion: "0.12.0",
|
||||
Cpu: 10,
|
||||
MemPct: 30,
|
||||
DiskPct: 40,
|
||||
},
|
||||
}
|
||||
|
||||
// Update system timestamp
|
||||
systemRecord.Set("updated", time.Now().UTC())
|
||||
err = hub.SaveNoValidate(systemRecord)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Handle system alerts with recovered battery
|
||||
err = am.HandleSystemAlerts(systemRecord, combinedDataRecovered)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Wait for the alert to be processed
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
|
||||
// Verify alert is now resolved (battery 25% is above threshold 20%)
|
||||
batteryAlert, err = hub.FindFirstRecordByFilter("alerts", "id={:id}", dbx.Params{"id": batteryAlert.Id})
|
||||
require.NoError(t, err)
|
||||
assert.False(t, batteryAlert.GetBool("triggered"), "Alert should be resolved when battery (25%%) goes above threshold (20%%)")
|
||||
}
|
||||
|
||||
// TestBatteryAlertNoBattery verifies that systems without battery data don't trigger alerts
|
||||
func TestBatteryAlertNoBattery(t *testing.T) {
|
||||
hub, user := beszelTests.GetHubWithUser(t)
|
||||
defer hub.Cleanup()
|
||||
|
||||
// Create a system
|
||||
systems, err := beszelTests.CreateSystems(hub, 1, user.Id, "up")
|
||||
require.NoError(t, err)
|
||||
systemRecord := systems[0]
|
||||
|
||||
// Create a battery alert
|
||||
batteryAlert, err := beszelTests.CreateRecord(hub, "alerts", map[string]any{
|
||||
"name": "Battery",
|
||||
"system": systemRecord.Id,
|
||||
"user": user.Id,
|
||||
"value": 20,
|
||||
"min": 1,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create stats with NO battery data (Battery[0] = 0)
|
||||
statsNoBattery := system.Stats{
|
||||
Cpu: 10,
|
||||
MemPct: 30,
|
||||
DiskPct: 40,
|
||||
Battery: [2]uint8{0, 0}, // No battery
|
||||
}
|
||||
|
||||
combinedData := &system.CombinedData{
|
||||
Stats: statsNoBattery,
|
||||
Info: system.Info{
|
||||
AgentVersion: "0.12.0",
|
||||
Cpu: 10,
|
||||
MemPct: 30,
|
||||
DiskPct: 40,
|
||||
},
|
||||
}
|
||||
|
||||
// Simulate system update time
|
||||
systemRecord.Set("updated", time.Now().UTC())
|
||||
err = hub.SaveNoValidate(systemRecord)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Handle system alerts
|
||||
am := hub.GetAlertManager()
|
||||
err = am.HandleSystemAlerts(systemRecord, combinedData)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Wait a moment for processing
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
|
||||
// Verify alert is NOT triggered (no battery data should skip the alert)
|
||||
batteryAlert, err = hub.FindFirstRecordByFilter("alerts", "id={:id}", dbx.Params{"id": batteryAlert.Id})
|
||||
require.NoError(t, err)
|
||||
assert.False(t, batteryAlert.GetBool("triggered"), "Alert should NOT be triggered when system has no battery")
|
||||
}
|
||||
|
||||
// TestBatteryAlertAveragedSamples tests battery alerts with min > 1 (averaging multiple samples)
|
||||
// This ensures the inverted threshold logic works correctly across averaged time windows
|
||||
func TestBatteryAlertAveragedSamples(t *testing.T) {
|
||||
hub, user := beszelTests.GetHubWithUser(t)
|
||||
defer hub.Cleanup()
|
||||
|
||||
// Create a system
|
||||
systems, err := beszelTests.CreateSystems(hub, 1, user.Id, "up")
|
||||
require.NoError(t, err)
|
||||
systemRecord := systems[0]
|
||||
|
||||
// Create a battery alert with threshold of 25% and min of 2 minutes (requires averaging)
|
||||
batteryAlert, err := beszelTests.CreateRecord(hub, "alerts", map[string]any{
|
||||
"name": "Battery",
|
||||
"system": systemRecord.Id,
|
||||
"user": user.Id,
|
||||
"value": 25, // threshold: 25%
|
||||
"min": 2, // 2 minutes - requires averaging
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify alert is not triggered initially
|
||||
assert.False(t, batteryAlert.GetBool("triggered"), "Alert should not be triggered initially")
|
||||
|
||||
am := hub.GetAlertManager()
|
||||
now := time.Now().UTC()
|
||||
|
||||
// Create system_stats records with low battery (below threshold)
|
||||
// The alert has min=2 minutes, so alert.time = now - 2 minutes
|
||||
// For the alert to be valid, alert.time must be AFTER the oldest record's created time
|
||||
// So we need records older than (now - 2 min), plus records within the window
|
||||
// Records at: now-3min (oldest, before window), now-90s, now-60s, now-30s
|
||||
recordTimes := []time.Duration{
|
||||
-180 * time.Second, // 3 min ago - this makes the oldest record before alert.time
|
||||
-90 * time.Second,
|
||||
-60 * time.Second,
|
||||
-30 * time.Second,
|
||||
}
|
||||
|
||||
for _, offset := range recordTimes {
|
||||
statsLow := system.Stats{
|
||||
Cpu: 10,
|
||||
MemPct: 30,
|
||||
DiskPct: 40,
|
||||
Battery: [2]uint8{15, 1}, // 15% battery (below 25% threshold)
|
||||
}
|
||||
statsLowJSON, _ := json.Marshal(statsLow)
|
||||
|
||||
recordTime := now.Add(offset)
|
||||
record, err := beszelTests.CreateRecord(hub, "system_stats", map[string]any{
|
||||
"system": systemRecord.Id,
|
||||
"type": "1m",
|
||||
"stats": string(statsLowJSON),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
// Update created time to simulate historical records - use SetRaw with formatted string
|
||||
record.SetRaw("created", recordTime.Format(types.DefaultDateLayout))
|
||||
err = hub.SaveNoValidate(record)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// Create combined data with low battery
|
||||
combinedDataLow := &system.CombinedData{
|
||||
Stats: system.Stats{
|
||||
Cpu: 10,
|
||||
MemPct: 30,
|
||||
DiskPct: 40,
|
||||
Battery: [2]uint8{15, 1},
|
||||
},
|
||||
Info: system.Info{
|
||||
AgentVersion: "0.12.0",
|
||||
Cpu: 10,
|
||||
MemPct: 30,
|
||||
DiskPct: 40,
|
||||
},
|
||||
}
|
||||
|
||||
// Update system timestamp
|
||||
systemRecord.Set("updated", now)
|
||||
err = hub.SaveNoValidate(systemRecord)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Handle system alerts - should trigger because average battery is below threshold
|
||||
err = am.HandleSystemAlerts(systemRecord, combinedDataLow)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Wait for alert processing
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
|
||||
// Verify alert IS triggered (average battery 15% is below threshold 25%)
|
||||
batteryAlert, err = hub.FindFirstRecordByFilter("alerts", "id={:id}", dbx.Params{"id": batteryAlert.Id})
|
||||
require.NoError(t, err)
|
||||
assert.True(t, batteryAlert.GetBool("triggered"),
|
||||
"Alert SHOULD be triggered when average battery (15%%) is below threshold (25%%) over min period")
|
||||
|
||||
// Now add records with high battery to test resolution
|
||||
// Use a new time window 2 minutes later
|
||||
newNow := now.Add(2 * time.Minute)
|
||||
// Records need to span before the alert time window (newNow - 2 min)
|
||||
recordTimesHigh := []time.Duration{
|
||||
-180 * time.Second, // 3 min before newNow - makes oldest record before alert.time
|
||||
-90 * time.Second,
|
||||
-60 * time.Second,
|
||||
-30 * time.Second,
|
||||
}
|
||||
|
||||
for _, offset := range recordTimesHigh {
|
||||
statsHigh := system.Stats{
|
||||
Cpu: 10,
|
||||
MemPct: 30,
|
||||
DiskPct: 40,
|
||||
Battery: [2]uint8{50, 1}, // 50% battery (above 25% threshold)
|
||||
}
|
||||
statsHighJSON, _ := json.Marshal(statsHigh)
|
||||
|
||||
recordTime := newNow.Add(offset)
|
||||
record, err := beszelTests.CreateRecord(hub, "system_stats", map[string]any{
|
||||
"system": systemRecord.Id,
|
||||
"type": "1m",
|
||||
"stats": string(statsHighJSON),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
record.SetRaw("created", recordTime.Format(types.DefaultDateLayout))
|
||||
err = hub.SaveNoValidate(record)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// Create combined data with high battery
|
||||
combinedDataHigh := &system.CombinedData{
|
||||
Stats: system.Stats{
|
||||
Cpu: 10,
|
||||
MemPct: 30,
|
||||
DiskPct: 40,
|
||||
Battery: [2]uint8{50, 1},
|
||||
},
|
||||
Info: system.Info{
|
||||
AgentVersion: "0.12.0",
|
||||
Cpu: 10,
|
||||
MemPct: 30,
|
||||
DiskPct: 40,
|
||||
},
|
||||
}
|
||||
|
||||
// Update system timestamp to the new time window
|
||||
systemRecord.Set("updated", newNow)
|
||||
err = hub.SaveNoValidate(systemRecord)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Handle system alerts - should resolve because average battery is now above threshold
|
||||
err = am.HandleSystemAlerts(systemRecord, combinedDataHigh)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Wait for alert processing
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
|
||||
// Verify alert is resolved (average battery 50% is above threshold 25%)
|
||||
batteryAlert, err = hub.FindFirstRecordByFilter("alerts", "id={:id}", dbx.Params{"id": batteryAlert.Id})
|
||||
require.NoError(t, err)
|
||||
assert.False(t, batteryAlert.GetBool("triggered"),
|
||||
"Alert should be resolved when average battery (50%%) is above threshold (25%%) over min period")
|
||||
}
|
||||
@@ -66,17 +66,30 @@ func (am *AlertManager) HandleSystemAlerts(systemRecord *core.Record, data *syst
|
||||
unit = ""
|
||||
case "GPU":
|
||||
val = data.Info.GpuPct
|
||||
case "Battery":
|
||||
if data.Stats.Battery[0] == 0 {
|
||||
continue
|
||||
}
|
||||
val = float64(data.Stats.Battery[0])
|
||||
}
|
||||
|
||||
triggered := alertRecord.GetBool("triggered")
|
||||
threshold := alertRecord.GetFloat("value")
|
||||
|
||||
// Battery alert has inverted logic: trigger when value is BELOW threshold
|
||||
lowAlert := isLowAlert(name)
|
||||
|
||||
// CONTINUE
|
||||
// IF alert is not triggered and curValue is less than threshold
|
||||
// OR alert is triggered and curValue is greater than threshold
|
||||
if (!triggered && val <= threshold) || (triggered && val > threshold) {
|
||||
// log.Printf("Skipping alert %s: val %f | threshold %f | triggered %v\n", name, val, threshold, triggered)
|
||||
continue
|
||||
// For normal alerts: IF not triggered and curValue <= threshold, OR triggered and curValue > threshold
|
||||
// For low alerts (Battery): IF not triggered and curValue >= threshold, OR triggered and curValue < threshold
|
||||
if lowAlert {
|
||||
if (!triggered && val >= threshold) || (triggered && val < threshold) {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
if (!triggered && val <= threshold) || (triggered && val > threshold) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
min := max(1, cast.ToUint8(alertRecord.Get("min")))
|
||||
@@ -94,7 +107,11 @@ func (am *AlertManager) HandleSystemAlerts(systemRecord *core.Record, data *syst
|
||||
|
||||
// send alert immediately if min is 1 - no need to sum up values.
|
||||
if min == 1 {
|
||||
alert.triggered = val > threshold
|
||||
if lowAlert {
|
||||
alert.triggered = val < threshold
|
||||
} else {
|
||||
alert.triggered = val > threshold
|
||||
}
|
||||
go am.sendSystemAlert(alert)
|
||||
continue
|
||||
}
|
||||
@@ -219,6 +236,8 @@ func (am *AlertManager) HandleSystemAlerts(systemRecord *core.Record, data *syst
|
||||
}
|
||||
}
|
||||
alert.val += maxUsage
|
||||
case "Battery":
|
||||
alert.val += float64(stats.Battery[0])
|
||||
default:
|
||||
continue
|
||||
}
|
||||
@@ -256,12 +275,24 @@ func (am *AlertManager) HandleSystemAlerts(systemRecord *core.Record, data *syst
|
||||
// log.Printf("%s: val %f | count %d | min-count %f | threshold %f\n", alert.name, alert.val, alert.count, minCount, alert.threshold)
|
||||
// pass through alert if count is greater than or equal to minCount
|
||||
if float32(alert.count) >= minCount {
|
||||
if !alert.triggered && alert.val > alert.threshold {
|
||||
alert.triggered = true
|
||||
go am.sendSystemAlert(alert)
|
||||
} else if alert.triggered && alert.val <= alert.threshold {
|
||||
alert.triggered = false
|
||||
go am.sendSystemAlert(alert)
|
||||
// Battery alert has inverted logic: trigger when value is BELOW threshold
|
||||
lowAlert := isLowAlert(alert.name)
|
||||
if lowAlert {
|
||||
if !alert.triggered && alert.val < alert.threshold {
|
||||
alert.triggered = true
|
||||
go am.sendSystemAlert(alert)
|
||||
} else if alert.triggered && alert.val >= alert.threshold {
|
||||
alert.triggered = false
|
||||
go am.sendSystemAlert(alert)
|
||||
}
|
||||
} else {
|
||||
if !alert.triggered && alert.val > alert.threshold {
|
||||
alert.triggered = true
|
||||
go am.sendSystemAlert(alert)
|
||||
} else if alert.triggered && alert.val <= alert.threshold {
|
||||
alert.triggered = false
|
||||
go am.sendSystemAlert(alert)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -288,10 +319,19 @@ func (am *AlertManager) sendSystemAlert(alert SystemAlertData) {
|
||||
}
|
||||
|
||||
var subject string
|
||||
lowAlert := isLowAlert(alert.name)
|
||||
if alert.triggered {
|
||||
subject = fmt.Sprintf("%s %s above threshold", systemName, titleAlertName)
|
||||
if lowAlert {
|
||||
subject = fmt.Sprintf("%s %s below threshold", systemName, titleAlertName)
|
||||
} else {
|
||||
subject = fmt.Sprintf("%s %s above threshold", systemName, titleAlertName)
|
||||
}
|
||||
} else {
|
||||
subject = fmt.Sprintf("%s %s below threshold", systemName, titleAlertName)
|
||||
if lowAlert {
|
||||
subject = fmt.Sprintf("%s %s above threshold", systemName, titleAlertName)
|
||||
} else {
|
||||
subject = fmt.Sprintf("%s %s below threshold", systemName, titleAlertName)
|
||||
}
|
||||
}
|
||||
minutesLabel := "minute"
|
||||
if alert.min > 1 {
|
||||
@@ -316,3 +356,7 @@ func (am *AlertManager) sendSystemAlert(alert SystemAlertData) {
|
||||
LinkText: "View " + systemName,
|
||||
})
|
||||
}
|
||||
|
||||
func isLowAlert(name string) bool {
|
||||
return name == "Battery"
|
||||
}
|
||||
|
||||
@@ -17,9 +17,8 @@ import (
|
||||
type cmdOptions struct {
|
||||
key string // key is the public key(s) for SSH authentication.
|
||||
listen string // listen is the address or port to listen on.
|
||||
// TODO: add hubURL and token
|
||||
// hubURL string // hubURL is the URL of the hub to use.
|
||||
// token string // token is the token to use for authentication.
|
||||
hubURL string // hubURL is the URL of the Beszel hub.
|
||||
token string // token is the token to use for authentication.
|
||||
}
|
||||
|
||||
// parse parses the command line flags and populates the config struct.
|
||||
@@ -47,13 +46,13 @@ func (opts *cmdOptions) parse() bool {
|
||||
// pflag.CommandLine.ParseErrorsWhitelist.UnknownFlags = true
|
||||
pflag.StringVarP(&opts.key, "key", "k", "", "Public key(s) for SSH authentication")
|
||||
pflag.StringVarP(&opts.listen, "listen", "l", "", "Address or port to listen on")
|
||||
// pflag.StringVarP(&opts.hubURL, "hub-url", "u", "", "URL of the hub to use")
|
||||
// pflag.StringVarP(&opts.token, "token", "t", "", "Token to use for authentication")
|
||||
pflag.StringVarP(&opts.hubURL, "url", "u", "", "URL of the Beszel hub")
|
||||
pflag.StringVarP(&opts.token, "token", "t", "", "Token to use for authentication")
|
||||
chinaMirrors := pflag.BoolP("china-mirrors", "c", false, "Use mirror for update (gh.beszel.dev) instead of GitHub")
|
||||
help := pflag.BoolP("help", "h", false, "Show this help message")
|
||||
|
||||
// Convert old single-dash long flags to double-dash for backward compatibility
|
||||
flagsToConvert := []string{"key", "listen"}
|
||||
flagsToConvert := []string{"key", "listen", "url", "token"}
|
||||
for i, arg := range os.Args {
|
||||
for _, flag := range flagsToConvert {
|
||||
singleDash := "-" + flag
|
||||
@@ -95,6 +94,13 @@ func (opts *cmdOptions) parse() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Set environment variables from CLI flags (if provided)
|
||||
if opts.hubURL != "" {
|
||||
os.Setenv("HUB_URL", opts.hubURL)
|
||||
}
|
||||
if opts.token != "" {
|
||||
os.Setenv("TOKEN", opts.token)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"github.com/fxamacker/cbor/v2"
|
||||
"github.com/henrygd/beszel/internal/entities/smart"
|
||||
"github.com/henrygd/beszel/internal/entities/system"
|
||||
"github.com/henrygd/beszel/internal/entities/systemd"
|
||||
@@ -34,14 +35,14 @@ type HubRequest[T any] struct {
|
||||
// AgentResponse defines the structure for responses sent from agent to hub.
|
||||
type AgentResponse struct {
|
||||
Id *uint32 `cbor:"0,keyasint,omitempty"`
|
||||
SystemData *system.CombinedData `cbor:"1,keyasint,omitempty,omitzero"`
|
||||
Fingerprint *FingerprintResponse `cbor:"2,keyasint,omitempty,omitzero"`
|
||||
SystemData *system.CombinedData `cbor:"1,keyasint,omitempty,omitzero"` // Legacy (<= 0.17)
|
||||
Fingerprint *FingerprintResponse `cbor:"2,keyasint,omitempty,omitzero"` // Legacy (<= 0.17)
|
||||
Error string `cbor:"3,keyasint,omitempty,omitzero"`
|
||||
String *string `cbor:"4,keyasint,omitempty,omitzero"`
|
||||
SmartData map[string]smart.SmartData `cbor:"5,keyasint,omitempty,omitzero"`
|
||||
ServiceInfo systemd.ServiceDetails `cbor:"6,keyasint,omitempty,omitzero"`
|
||||
// Logs *LogsPayload `cbor:"4,keyasint,omitempty,omitzero"`
|
||||
// RawBytes []byte `cbor:"4,keyasint,omitempty,omitzero"`
|
||||
String *string `cbor:"4,keyasint,omitempty,omitzero"` // Legacy (<= 0.17)
|
||||
SmartData map[string]smart.SmartData `cbor:"5,keyasint,omitempty,omitzero"` // Legacy (<= 0.17)
|
||||
ServiceInfo systemd.ServiceDetails `cbor:"6,keyasint,omitempty,omitzero"` // Legacy (<= 0.17)
|
||||
// Data is the generic response payload for new endpoints (0.18+)
|
||||
Data cbor.RawMessage `cbor:"7,keyasint,omitempty,omitzero"`
|
||||
}
|
||||
|
||||
type FingerprintRequest struct {
|
||||
@@ -58,8 +59,8 @@ type FingerprintResponse struct {
|
||||
}
|
||||
|
||||
type DataRequestOptions struct {
|
||||
CacheTimeMs uint16 `cbor:"0,keyasint"`
|
||||
// ResourceType uint8 `cbor:"1,keyasint,omitempty,omitzero"`
|
||||
CacheTimeMs uint16 `cbor:"0,keyasint"`
|
||||
IncludeDetails bool `cbor:"1,keyasint"`
|
||||
}
|
||||
|
||||
type ContainerLogsRequest struct {
|
||||
|
||||
@@ -17,7 +17,7 @@ RUN rm -rf /tmp/*
|
||||
# --------------------------
|
||||
# Final image: default scratch-based agent
|
||||
# --------------------------
|
||||
FROM alpine:latest
|
||||
FROM alpine:3.23
|
||||
COPY --from=builder /agent /agent
|
||||
|
||||
RUN apk add --no-cache smartmontools
|
||||
|
||||
@@ -16,7 +16,7 @@ RUN CGO_ENABLED=0 GOGC=75 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -ldflags "-
|
||||
# Final image
|
||||
# Note: must cap_add: [CAP_PERFMON] and mount /dev/dri/ as volume
|
||||
# --------------------------
|
||||
FROM alpine:edge
|
||||
FROM alpine:3.23
|
||||
|
||||
COPY --from=builder /agent /agent
|
||||
|
||||
|
||||
@@ -34,6 +34,14 @@ type ApiStats struct {
|
||||
MemoryStats MemoryStats `json:"memory_stats"`
|
||||
}
|
||||
|
||||
// Docker system info from /info API endpoint
|
||||
type HostInfo struct {
|
||||
OperatingSystem string `json:"OperatingSystem"`
|
||||
KernelVersion string `json:"KernelVersion"`
|
||||
NCPU int `json:"NCPU"`
|
||||
MemTotal uint64 `json:"MemTotal"`
|
||||
}
|
||||
|
||||
func (s *ApiStats) CalculateCpuPercentLinux(prevCpuContainer uint64, prevCpuSystem uint64) float64 {
|
||||
cpuDelta := s.CPUStats.CPUUsage.TotalUsage - prevCpuContainer
|
||||
systemDelta := s.CPUStats.SystemUsage - prevCpuSystem
|
||||
|
||||
@@ -123,31 +123,49 @@ const (
|
||||
ConnectionTypeWebSocket
|
||||
)
|
||||
|
||||
// Core system data that is needed in All Systems table
|
||||
type Info struct {
|
||||
Hostname string `json:"h" cbor:"0,keyasint"`
|
||||
KernelVersion string `json:"k,omitempty" cbor:"1,keyasint,omitempty"`
|
||||
Cores int `json:"c" cbor:"2,keyasint"`
|
||||
Hostname string `json:"h,omitempty" cbor:"0,keyasint,omitempty"` // deprecated - moved to Details struct
|
||||
KernelVersion string `json:"k,omitempty" cbor:"1,keyasint,omitempty"` // deprecated - moved to Details struct
|
||||
Cores int `json:"c,omitzero" cbor:"2,keyasint,omitzero"` // deprecated - moved to Details struct
|
||||
// Threads is needed in Info struct to calculate load average thresholds
|
||||
Threads int `json:"t,omitempty" cbor:"3,keyasint,omitempty"`
|
||||
CpuModel string `json:"m" cbor:"4,keyasint"`
|
||||
CpuModel string `json:"m,omitempty" cbor:"4,keyasint,omitempty"` // deprecated - moved to Details struct
|
||||
Uptime uint64 `json:"u" cbor:"5,keyasint"`
|
||||
Cpu float64 `json:"cpu" cbor:"6,keyasint"`
|
||||
MemPct float64 `json:"mp" cbor:"7,keyasint"`
|
||||
DiskPct float64 `json:"dp" cbor:"8,keyasint"`
|
||||
Bandwidth float64 `json:"b" cbor:"9,keyasint"`
|
||||
AgentVersion string `json:"v" cbor:"10,keyasint"`
|
||||
Podman bool `json:"p,omitempty" cbor:"11,keyasint,omitempty"`
|
||||
Podman bool `json:"p,omitempty" cbor:"11,keyasint,omitempty"` // deprecated - moved to Details struct
|
||||
GpuPct float64 `json:"g,omitempty" cbor:"12,keyasint,omitempty"`
|
||||
DashboardTemp float64 `json:"dt,omitempty" cbor:"13,keyasint,omitempty"`
|
||||
Os Os `json:"os" cbor:"14,keyasint"`
|
||||
LoadAvg1 float64 `json:"l1,omitempty" cbor:"15,keyasint,omitempty"`
|
||||
LoadAvg5 float64 `json:"l5,omitempty" cbor:"16,keyasint,omitempty"`
|
||||
LoadAvg15 float64 `json:"l15,omitempty" cbor:"17,keyasint,omitempty"`
|
||||
Os Os `json:"os,omitempty" cbor:"14,keyasint,omitempty"` // deprecated - moved to Details struct
|
||||
LoadAvg1 float64 `json:"l1,omitempty" cbor:"15,keyasint,omitempty"` // deprecated - use `la` array instead
|
||||
LoadAvg5 float64 `json:"l5,omitempty" cbor:"16,keyasint,omitempty"` // deprecated - use `la` array instead
|
||||
LoadAvg15 float64 `json:"l15,omitempty" cbor:"17,keyasint,omitempty"` // deprecated - use `la` array instead
|
||||
BandwidthBytes uint64 `json:"bb" cbor:"18,keyasint"`
|
||||
// TODO: remove load fields in future release in favor of load avg array
|
||||
|
||||
LoadAvg [3]float64 `json:"la,omitempty" cbor:"19,keyasint"`
|
||||
ConnectionType ConnectionType `json:"ct,omitempty" cbor:"20,keyasint,omitempty,omitzero"`
|
||||
ExtraFsPct map[string]float64 `json:"efs,omitempty" cbor:"21,keyasint,omitempty"`
|
||||
Services []uint16 `json:"sv,omitempty" cbor:"22,keyasint,omitempty"` // [totalServices, numFailedServices]
|
||||
Battery [2]uint8 `json:"bat,omitzero" cbor:"23,keyasint,omitzero"` // [percent, charge state]
|
||||
}
|
||||
|
||||
// Data that does not change during process lifetime and is not needed in All Systems table
|
||||
type Details struct {
|
||||
Hostname string `cbor:"0,keyasint"`
|
||||
Kernel string `cbor:"1,keyasint,omitempty"`
|
||||
Cores int `cbor:"2,keyasint"`
|
||||
Threads int `cbor:"3,keyasint"`
|
||||
CpuModel string `cbor:"4,keyasint"`
|
||||
Os Os `cbor:"5,keyasint"`
|
||||
OsName string `cbor:"6,keyasint"`
|
||||
Arch string `cbor:"7,keyasint"`
|
||||
Podman bool `cbor:"8,keyasint,omitempty"`
|
||||
MemoryTotal uint64 `cbor:"9,keyasint"`
|
||||
SmartInterval time.Duration `cbor:"10,keyasint,omitempty"`
|
||||
}
|
||||
|
||||
// Final data structure to return to the hub
|
||||
@@ -156,4 +174,5 @@ type CombinedData struct {
|
||||
Info Info `json:"info" cbor:"1,keyasint"`
|
||||
Containers []*container.Stats `json:"container" cbor:"2,keyasint"`
|
||||
SystemdServices []*systemd.Service `json:"systemd,omitempty" cbor:"3,keyasint,omitempty"`
|
||||
Details *Details `cbor:"4,keyasint,omitempty"`
|
||||
}
|
||||
|
||||
@@ -66,6 +66,15 @@ func (acr *agentConnectRequest) agentConnect() (err error) {
|
||||
|
||||
// Check if token is an active universal token
|
||||
acr.userId, acr.isUniversalToken = universalTokenMap.GetMap().GetOk(acr.token)
|
||||
if !acr.isUniversalToken {
|
||||
// Fallback: check for a permanent universal token stored in the DB
|
||||
if rec, err := acr.hub.FindFirstRecordByFilter("universal_tokens", "token = {:token}", dbx.Params{"token": acr.token}); err == nil {
|
||||
if userID := rec.GetString("user"); userID != "" {
|
||||
acr.userId = userID
|
||||
acr.isUniversalToken = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find matching fingerprint records for this token
|
||||
fpRecords := getFingerprintRecordsByToken(acr.token, acr.hub)
|
||||
|
||||
@@ -1169,6 +1169,106 @@ func TestMultipleSystemsWithSameUniversalToken(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestPermanentUniversalTokenFromDB verifies that a universal token persisted in the DB
|
||||
// (universal_tokens collection) is accepted for agent self-registration even if it is not
|
||||
// present in the in-memory universalTokenMap.
|
||||
func TestPermanentUniversalTokenFromDB(t *testing.T) {
|
||||
// Create hub and test app
|
||||
hub, testApp, err := createTestHub(t)
|
||||
require.NoError(t, err)
|
||||
defer testApp.Cleanup()
|
||||
|
||||
// Get the hub's SSH key
|
||||
hubSigner, err := hub.GetSSHKey("")
|
||||
require.NoError(t, err)
|
||||
goodPubKey := hubSigner.PublicKey()
|
||||
|
||||
// Create test user
|
||||
userRecord, err := createTestUser(testApp)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create a permanent universal token record in the DB (do NOT add it to universalTokenMap)
|
||||
universalToken := "db-universal-token-123"
|
||||
_, err = createTestRecord(testApp, "universal_tokens", map[string]any{
|
||||
"user": userRecord.Id,
|
||||
"token": universalToken,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create HTTP server with the actual API route
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path == "/api/beszel/agent-connect" {
|
||||
acr := &agentConnectRequest{
|
||||
hub: hub,
|
||||
req: r,
|
||||
res: w,
|
||||
}
|
||||
acr.agentConnect()
|
||||
} else {
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
// Create and configure agent
|
||||
agentDataDir := t.TempDir()
|
||||
err = os.WriteFile(filepath.Join(agentDataDir, "fingerprint"), []byte("db-token-system-fingerprint"), 0644)
|
||||
require.NoError(t, err)
|
||||
|
||||
testAgent, err := agent.NewAgent(agentDataDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Set up environment variables for the agent
|
||||
os.Setenv("BESZEL_AGENT_HUB_URL", ts.URL)
|
||||
os.Setenv("BESZEL_AGENT_TOKEN", universalToken)
|
||||
defer func() {
|
||||
os.Unsetenv("BESZEL_AGENT_HUB_URL")
|
||||
os.Unsetenv("BESZEL_AGENT_TOKEN")
|
||||
}()
|
||||
|
||||
// Start agent in background
|
||||
done := make(chan error, 1)
|
||||
go func() {
|
||||
serverOptions := agent.ServerOptions{
|
||||
Network: "tcp",
|
||||
Addr: "127.0.0.1:46050",
|
||||
Keys: []ssh.PublicKey{goodPubKey},
|
||||
}
|
||||
done <- testAgent.Start(serverOptions)
|
||||
}()
|
||||
|
||||
// Wait for connection result
|
||||
maxWait := 2 * time.Second
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
checkInterval := 20 * time.Millisecond
|
||||
timeout := time.After(maxWait)
|
||||
ticker := time.Tick(checkInterval)
|
||||
|
||||
connectionManager := testAgent.GetConnectionManager()
|
||||
for {
|
||||
select {
|
||||
case <-timeout:
|
||||
t.Fatalf("Expected connection to succeed but timed out - agent state: %d", connectionManager.State)
|
||||
case <-ticker:
|
||||
if connectionManager.State == agent.WebSocketConnected {
|
||||
// Success
|
||||
goto verify
|
||||
}
|
||||
case err := <-done:
|
||||
// If Start returns early, treat it as failure
|
||||
if err != nil {
|
||||
t.Fatalf("Agent failed to start/connect: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
verify:
|
||||
// Verify that a system was created for the user (self-registration path)
|
||||
systemsAfter, err := testApp.FindRecordsByFilter("systems", "users ~ {:userId}", "", -1, 0, map[string]any{"userId": userRecord.Id})
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, systemsAfter, "Expected a system to be created for DB-backed universal token")
|
||||
}
|
||||
|
||||
// TestFindOrCreateSystemForToken tests the findOrCreateSystemForToken function
|
||||
func TestFindOrCreateSystemForToken(t *testing.T) {
|
||||
hub, testApp, err := createTestHub(t)
|
||||
|
||||
@@ -415,7 +415,11 @@ func TestExpiryMap_RemoveValue_WithExpiration(t *testing.T) {
|
||||
// Wait for first value to expire
|
||||
time.Sleep(time.Millisecond * 20)
|
||||
|
||||
// Try to remove the expired value - should remove one of the "value1" entries
|
||||
// Trigger lazy cleanup of the expired key
|
||||
_, ok := em.GetOk("key1")
|
||||
assert.False(t, ok)
|
||||
|
||||
// Try to remove the remaining "value1" entry (key3)
|
||||
removedValue, ok := em.RemovebyValue("value1")
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, "value1", removedValue)
|
||||
@@ -423,14 +427,9 @@ func TestExpiryMap_RemoveValue_WithExpiration(t *testing.T) {
|
||||
// Should still have key2 (different value)
|
||||
assert.True(t, em.Has("key2"))
|
||||
|
||||
// Should have removed one of the "value1" entries (either key1 or key3)
|
||||
// But we can't predict which one due to map iteration order
|
||||
key1Exists := em.Has("key1")
|
||||
key3Exists := em.Has("key3")
|
||||
|
||||
// Exactly one of key1 or key3 should be gone
|
||||
assert.False(t, key1Exists && key3Exists) // Both shouldn't exist
|
||||
assert.True(t, key1Exists || key3Exists) // At least one should still exist
|
||||
// key1 should be gone due to expiration and key3 should be removed by value.
|
||||
assert.False(t, em.Has("key1"))
|
||||
assert.False(t, em.Has("key3"))
|
||||
}
|
||||
|
||||
func TestExpiryMap_ValueOperations_Integration(t *testing.T) {
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"github.com/henrygd/beszel/internal/users"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/pocketbase/dbx"
|
||||
"github.com/pocketbase/pocketbase"
|
||||
"github.com/pocketbase/pocketbase/apis"
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
@@ -288,24 +289,90 @@ func (h *Hub) getUniversalToken(e *core.RequestEvent) error {
|
||||
userID := e.Auth.Id
|
||||
query := e.Request.URL.Query()
|
||||
token := query.Get("token")
|
||||
enable := query.Get("enable")
|
||||
permanent := query.Get("permanent")
|
||||
|
||||
// helper for deleting any existing permanent token record for this user
|
||||
deletePermanent := func() error {
|
||||
rec, err := h.FindFirstRecordByFilter("universal_tokens", "user = {:user}", dbx.Params{"user": userID})
|
||||
if err != nil {
|
||||
return nil // no record
|
||||
}
|
||||
return h.Delete(rec)
|
||||
}
|
||||
|
||||
// helper for upserting a permanent token record for this user
|
||||
upsertPermanent := func(token string) error {
|
||||
rec, err := h.FindFirstRecordByFilter("universal_tokens", "user = {:user}", dbx.Params{"user": userID})
|
||||
if err == nil {
|
||||
rec.Set("token", token)
|
||||
return h.Save(rec)
|
||||
}
|
||||
|
||||
col, err := h.FindCachedCollectionByNameOrId("universal_tokens")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newRec := core.NewRecord(col)
|
||||
newRec.Set("user", userID)
|
||||
newRec.Set("token", token)
|
||||
return h.Save(newRec)
|
||||
}
|
||||
|
||||
// Disable universal tokens (both ephemeral and permanent)
|
||||
if enable == "0" {
|
||||
tokenMap.RemovebyValue(userID)
|
||||
_ = deletePermanent()
|
||||
return e.JSON(http.StatusOK, map[string]any{"token": token, "active": false, "permanent": false})
|
||||
}
|
||||
|
||||
// Enable universal token (ephemeral or permanent)
|
||||
if enable == "1" {
|
||||
if token == "" {
|
||||
token = uuid.New().String()
|
||||
}
|
||||
|
||||
if permanent == "1" {
|
||||
// make token permanent (persist across restarts)
|
||||
tokenMap.RemovebyValue(userID)
|
||||
if err := upsertPermanent(token); err != nil {
|
||||
return err
|
||||
}
|
||||
return e.JSON(http.StatusOK, map[string]any{"token": token, "active": true, "permanent": true})
|
||||
}
|
||||
|
||||
// default: ephemeral mode (1 hour)
|
||||
_ = deletePermanent()
|
||||
tokenMap.Set(token, userID, time.Hour)
|
||||
return e.JSON(http.StatusOK, map[string]any{"token": token, "active": true, "permanent": false})
|
||||
}
|
||||
|
||||
// Read current state
|
||||
// Prefer permanent token if it exists.
|
||||
if rec, err := h.FindFirstRecordByFilter("universal_tokens", "user = {:user}", dbx.Params{"user": userID}); err == nil {
|
||||
dbToken := rec.GetString("token")
|
||||
// If no token was provided, or the caller is asking about their permanent token, return it.
|
||||
if token == "" || token == dbToken {
|
||||
return e.JSON(http.StatusOK, map[string]any{"token": dbToken, "active": true, "permanent": true})
|
||||
}
|
||||
// Token doesn't match their permanent token (avoid leaking other info)
|
||||
return e.JSON(http.StatusOK, map[string]any{"token": token, "active": false, "permanent": false})
|
||||
}
|
||||
|
||||
// No permanent token; fall back to ephemeral token map.
|
||||
if token == "" {
|
||||
// return existing token if it exists
|
||||
if token, _, ok := tokenMap.GetByValue(userID); ok {
|
||||
return e.JSON(http.StatusOK, map[string]any{"token": token, "active": true})
|
||||
return e.JSON(http.StatusOK, map[string]any{"token": token, "active": true, "permanent": false})
|
||||
}
|
||||
// if no token is provided, generate a new one
|
||||
token = uuid.New().String()
|
||||
}
|
||||
response := map[string]any{"token": token}
|
||||
|
||||
switch query.Get("enable") {
|
||||
case "1":
|
||||
tokenMap.Set(token, userID, time.Hour)
|
||||
case "0":
|
||||
tokenMap.RemovebyValue(userID)
|
||||
}
|
||||
_, response["active"] = tokenMap.GetOk(token)
|
||||
// Token is considered active only if it belongs to the current user.
|
||||
activeUser, ok := tokenMap.GetOk(token)
|
||||
active := ok && activeUser == userID
|
||||
response := map[string]any{"token": token, "active": active, "permanent": false}
|
||||
return e.JSON(http.StatusOK, response)
|
||||
}
|
||||
|
||||
|
||||
@@ -378,7 +378,18 @@ func TestApiRoutesAuthentication(t *testing.T) {
|
||||
"Authorization": userToken,
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{"active", "token"},
|
||||
ExpectedContent: []string{"active", "token", "permanent"},
|
||||
TestAppFactory: testAppFactory,
|
||||
},
|
||||
{
|
||||
Name: "GET /universal-token - enable permanent should succeed",
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/beszel/universal-token?enable=1&permanent=1&token=permanent-token-123",
|
||||
Headers: map[string]string{
|
||||
"Authorization": userToken,
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{"\"permanent\":true", "permanent-token-123"},
|
||||
TestAppFactory: testAppFactory,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -9,13 +9,15 @@ import (
|
||||
"math/rand"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/henrygd/beszel/internal/common"
|
||||
"github.com/henrygd/beszel/internal/hub/transport"
|
||||
"github.com/henrygd/beszel/internal/hub/ws"
|
||||
|
||||
"github.com/henrygd/beszel/internal/entities/container"
|
||||
"github.com/henrygd/beszel/internal/entities/smart"
|
||||
"github.com/henrygd/beszel/internal/entities/system"
|
||||
"github.com/henrygd/beszel/internal/entities/systemd"
|
||||
|
||||
@@ -23,25 +25,30 @@ import (
|
||||
|
||||
"github.com/blang/semver"
|
||||
"github.com/fxamacker/cbor/v2"
|
||||
"github.com/lxzan/gws"
|
||||
"github.com/pocketbase/dbx"
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
type System struct {
|
||||
Id string `db:"id"`
|
||||
Host string `db:"host"`
|
||||
Port string `db:"port"`
|
||||
Status string `db:"status"`
|
||||
manager *SystemManager // Manager that this system belongs to
|
||||
client *ssh.Client // SSH client for fetching data
|
||||
data *system.CombinedData // system data from agent
|
||||
ctx context.Context // Context for stopping the updater
|
||||
cancel context.CancelFunc // Stops and removes system from updater
|
||||
WsConn *ws.WsConn // Handler for agent WebSocket connection
|
||||
agentVersion semver.Version // Agent version
|
||||
updateTicker *time.Ticker // Ticker for updating the system
|
||||
smartOnce sync.Once // Once for fetching and saving smart devices
|
||||
Id string `db:"id"`
|
||||
Host string `db:"host"`
|
||||
Port string `db:"port"`
|
||||
Status string `db:"status"`
|
||||
manager *SystemManager // Manager that this system belongs to
|
||||
client *ssh.Client // SSH client for fetching data
|
||||
sshTransport *transport.SSHTransport // SSH transport for requests
|
||||
data *system.CombinedData // system data from agent
|
||||
ctx context.Context // Context for stopping the updater
|
||||
cancel context.CancelFunc // Stops and removes system from updater
|
||||
WsConn *ws.WsConn // Handler for agent WebSocket connection
|
||||
agentVersion semver.Version // Agent version
|
||||
updateTicker *time.Ticker // Ticker for updating the system
|
||||
detailsFetched atomic.Bool // True if static system details have been fetched and saved
|
||||
smartFetching atomic.Bool // True if SMART devices are currently being fetched
|
||||
smartInterval time.Duration // Interval for periodic SMART data updates
|
||||
lastSmartFetch atomic.Int64 // Unix milliseconds of last SMART data fetch
|
||||
}
|
||||
|
||||
func (sm *SystemManager) NewSystem(systemId string) *System {
|
||||
@@ -114,10 +121,37 @@ func (sys *System) update() error {
|
||||
sys.handlePaused()
|
||||
return nil
|
||||
}
|
||||
data, err := sys.fetchDataFromAgent(common.DataRequestOptions{CacheTimeMs: uint16(interval)})
|
||||
if err == nil {
|
||||
_, err = sys.createRecords(data)
|
||||
options := common.DataRequestOptions{
|
||||
CacheTimeMs: uint16(interval),
|
||||
}
|
||||
// fetch system details if not already fetched
|
||||
if !sys.detailsFetched.Load() {
|
||||
options.IncludeDetails = true
|
||||
}
|
||||
|
||||
data, err := sys.fetchDataFromAgent(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// create system records
|
||||
_, err = sys.createRecords(data)
|
||||
|
||||
// Fetch and save SMART devices when system first comes online or at intervals
|
||||
if backgroundSmartFetchEnabled() {
|
||||
if sys.smartInterval <= 0 {
|
||||
sys.smartInterval = time.Hour
|
||||
}
|
||||
lastFetch := sys.lastSmartFetch.Load()
|
||||
if time.Since(time.UnixMilli(lastFetch)) >= sys.smartInterval && sys.smartFetching.CompareAndSwap(false, true) {
|
||||
go func() {
|
||||
defer sys.smartFetching.Store(false)
|
||||
sys.lastSmartFetch.Store(time.Now().UnixMilli())
|
||||
_ = sys.FetchAndSaveSmartDevices()
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -142,12 +176,11 @@ func (sys *System) createRecords(data *system.CombinedData) (*core.Record, error
|
||||
}
|
||||
hub := sys.manager.hub
|
||||
err = hub.RunInTransaction(func(txApp core.App) error {
|
||||
// add system_stats and container_stats records
|
||||
// add system_stats record
|
||||
systemStatsCollection, err := txApp.FindCachedCollectionByNameOrId("system_stats")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
systemStatsRecord := core.NewRecord(systemStatsCollection)
|
||||
systemStatsRecord.Set("system", systemRecord.Id)
|
||||
systemStatsRecord.Set("stats", data.Stats)
|
||||
@@ -155,14 +188,14 @@ func (sys *System) createRecords(data *system.CombinedData) (*core.Record, error
|
||||
if err := txApp.SaveNoValidate(systemStatsRecord); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// add containers and container_stats records
|
||||
if len(data.Containers) > 0 {
|
||||
// add / update containers records
|
||||
if data.Containers[0].Id != "" {
|
||||
if err := createContainerRecords(txApp, data.Containers, sys.Id); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// add new container_stats record
|
||||
containerStatsCollection, err := txApp.FindCachedCollectionByNameOrId("container_stats")
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -183,9 +216,20 @@ func (sys *System) createRecords(data *system.CombinedData) (*core.Record, error
|
||||
}
|
||||
}
|
||||
|
||||
// add system details record
|
||||
if data.Details != nil {
|
||||
if err := createSystemDetailsRecord(txApp, data.Details, sys.Id); err != nil {
|
||||
return err
|
||||
}
|
||||
sys.detailsFetched.Store(true)
|
||||
// update smart interval if it's set on the agent side
|
||||
if data.Details.SmartInterval > 0 {
|
||||
sys.smartInterval = data.Details.SmartInterval
|
||||
}
|
||||
}
|
||||
|
||||
// update system record (do this last because it triggers alerts and we need above records to be inserted first)
|
||||
systemRecord.Set("status", up)
|
||||
|
||||
systemRecord.Set("info", data.Info)
|
||||
if err := txApp.SaveNoValidate(systemRecord); err != nil {
|
||||
return err
|
||||
@@ -193,16 +237,34 @@ func (sys *System) createRecords(data *system.CombinedData) (*core.Record, error
|
||||
return nil
|
||||
})
|
||||
|
||||
// Fetch and save SMART devices when system first comes online
|
||||
if err == nil {
|
||||
sys.smartOnce.Do(func() {
|
||||
go sys.FetchAndSaveSmartDevices()
|
||||
})
|
||||
}
|
||||
|
||||
return systemRecord, err
|
||||
}
|
||||
|
||||
func createSystemDetailsRecord(app core.App, data *system.Details, systemId string) error {
|
||||
collectionName := "system_details"
|
||||
params := dbx.Params{
|
||||
"id": systemId,
|
||||
"system": systemId,
|
||||
"hostname": data.Hostname,
|
||||
"kernel": data.Kernel,
|
||||
"cores": data.Cores,
|
||||
"threads": data.Threads,
|
||||
"cpu": data.CpuModel,
|
||||
"os": data.Os,
|
||||
"os_name": data.OsName,
|
||||
"arch": data.Arch,
|
||||
"memory": data.MemoryTotal,
|
||||
"podman": data.Podman,
|
||||
"updated": time.Now().UTC(),
|
||||
}
|
||||
result, err := app.DB().Update(collectionName, params, dbx.HashExp{"id": systemId}).Execute()
|
||||
rowsAffected, _ := result.RowsAffected()
|
||||
if err != nil || rowsAffected == 0 {
|
||||
_, err = app.DB().Insert(collectionName, params).Execute()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func createSystemdStatsRecords(app core.App, data []*systemd.Service, systemId string) error {
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
@@ -301,8 +363,78 @@ func (sys *System) getContext() (context.Context, context.CancelFunc) {
|
||||
return sys.ctx, sys.cancel
|
||||
}
|
||||
|
||||
// fetchDataFromAgent attempts to fetch data from the agent,
|
||||
// prioritizing WebSocket if available.
|
||||
// request sends a request to the agent, trying WebSocket first, then SSH.
|
||||
// This is the unified request method that uses the transport abstraction.
|
||||
func (sys *System) request(ctx context.Context, action common.WebSocketAction, req any, dest any) error {
|
||||
// Try WebSocket first
|
||||
if sys.WsConn != nil && sys.WsConn.IsConnected() {
|
||||
wsTransport := transport.NewWebSocketTransport(sys.WsConn)
|
||||
if err := wsTransport.Request(ctx, action, req, dest); err == nil {
|
||||
return nil
|
||||
} else if !shouldFallbackToSSH(err) {
|
||||
return err
|
||||
} else if shouldCloseWebSocket(err) {
|
||||
sys.closeWebSocketConnection()
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to SSH if WebSocket fails
|
||||
if err := sys.ensureSSHTransport(); err != nil {
|
||||
return err
|
||||
}
|
||||
err := sys.sshTransport.RequestWithRetry(ctx, action, req, dest, 1)
|
||||
// Keep legacy SSH client/version fields in sync for other code paths.
|
||||
if sys.sshTransport != nil {
|
||||
sys.client = sys.sshTransport.GetClient()
|
||||
sys.agentVersion = sys.sshTransport.GetAgentVersion()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func shouldFallbackToSSH(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) {
|
||||
return true
|
||||
}
|
||||
if errors.Is(err, gws.ErrConnClosed) {
|
||||
return true
|
||||
}
|
||||
return errors.Is(err, transport.ErrWebSocketNotConnected)
|
||||
}
|
||||
|
||||
func shouldCloseWebSocket(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
return errors.Is(err, gws.ErrConnClosed) || errors.Is(err, transport.ErrWebSocketNotConnected)
|
||||
}
|
||||
|
||||
// ensureSSHTransport ensures the SSH transport is initialized and connected.
|
||||
func (sys *System) ensureSSHTransport() error {
|
||||
if sys.sshTransport == nil {
|
||||
if sys.manager.sshConfig == nil {
|
||||
if err := sys.manager.createSSHClientConfig(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
sys.sshTransport = transport.NewSSHTransport(transport.SSHTransportConfig{
|
||||
Host: sys.Host,
|
||||
Port: sys.Port,
|
||||
Config: sys.manager.sshConfig,
|
||||
Timeout: 4 * time.Second,
|
||||
})
|
||||
}
|
||||
// Sync client state with transport
|
||||
if sys.client != nil {
|
||||
sys.sshTransport.SetClient(sys.client)
|
||||
sys.sshTransport.SetAgentVersion(sys.agentVersion)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// fetchDataFromAgent attempts to fetch data from the agent, prioritizing WebSocket if available.
|
||||
func (sys *System) fetchDataFromAgent(options common.DataRequestOptions) (*system.CombinedData, error) {
|
||||
if sys.data == nil {
|
||||
sys.data = &system.CombinedData{}
|
||||
@@ -328,112 +460,47 @@ func (sys *System) fetchDataViaWebSocket(options common.DataRequestOptions) (*sy
|
||||
if sys.WsConn == nil || !sys.WsConn.IsConnected() {
|
||||
return nil, errors.New("no websocket connection")
|
||||
}
|
||||
err := sys.WsConn.RequestSystemData(context.Background(), sys.data, options)
|
||||
wsTransport := transport.NewWebSocketTransport(sys.WsConn)
|
||||
err := wsTransport.Request(context.Background(), common.GetData, options, sys.data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sys.data, nil
|
||||
}
|
||||
|
||||
// fetchStringFromAgentViaSSH is a generic function to fetch strings via SSH
|
||||
func (sys *System) fetchStringFromAgentViaSSH(action common.WebSocketAction, requestData any, errorMsg string) (string, error) {
|
||||
var result string
|
||||
err := sys.runSSHOperation(4*time.Second, 1, func(session *ssh.Session) (bool, error) {
|
||||
stdout, err := session.StdoutPipe()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
stdin, stdinErr := session.StdinPipe()
|
||||
if stdinErr != nil {
|
||||
return false, stdinErr
|
||||
}
|
||||
if err := session.Shell(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
req := common.HubRequest[any]{Action: action, Data: requestData}
|
||||
_ = cbor.NewEncoder(stdin).Encode(req)
|
||||
_ = stdin.Close()
|
||||
var resp common.AgentResponse
|
||||
err = cbor.NewDecoder(stdout).Decode(&resp)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if resp.String == nil {
|
||||
return false, errors.New(errorMsg)
|
||||
}
|
||||
result = *resp.String
|
||||
return false, nil
|
||||
})
|
||||
return result, err
|
||||
}
|
||||
|
||||
// FetchContainerInfoFromAgent fetches container info from the agent
|
||||
func (sys *System) FetchContainerInfoFromAgent(containerID string) (string, error) {
|
||||
// fetch via websocket
|
||||
if sys.WsConn != nil && sys.WsConn.IsConnected() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
return sys.WsConn.RequestContainerInfo(ctx, containerID)
|
||||
}
|
||||
// fetch via SSH
|
||||
return sys.fetchStringFromAgentViaSSH(common.GetContainerInfo, common.ContainerInfoRequest{ContainerID: containerID}, "no info in response")
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
var result string
|
||||
err := sys.request(ctx, common.GetContainerInfo, common.ContainerInfoRequest{ContainerID: containerID}, &result)
|
||||
return result, err
|
||||
}
|
||||
|
||||
// FetchContainerLogsFromAgent fetches container logs from the agent
|
||||
func (sys *System) FetchContainerLogsFromAgent(containerID string) (string, error) {
|
||||
// fetch via websocket
|
||||
if sys.WsConn != nil && sys.WsConn.IsConnected() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
return sys.WsConn.RequestContainerLogs(ctx, containerID)
|
||||
}
|
||||
// fetch via SSH
|
||||
return sys.fetchStringFromAgentViaSSH(common.GetContainerLogs, common.ContainerLogsRequest{ContainerID: containerID}, "no logs in response")
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
var result string
|
||||
err := sys.request(ctx, common.GetContainerLogs, common.ContainerLogsRequest{ContainerID: containerID}, &result)
|
||||
return result, err
|
||||
}
|
||||
|
||||
// FetchSystemdInfoFromAgent fetches detailed systemd service information from the agent
|
||||
func (sys *System) FetchSystemdInfoFromAgent(serviceName string) (systemd.ServiceDetails, error) {
|
||||
// fetch via websocket
|
||||
if sys.WsConn != nil && sys.WsConn.IsConnected() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
return sys.WsConn.RequestSystemdInfo(ctx, serviceName)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
var result systemd.ServiceDetails
|
||||
err := sys.runSSHOperation(5*time.Second, 1, func(session *ssh.Session) (bool, error) {
|
||||
stdout, err := session.StdoutPipe()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
stdin, stdinErr := session.StdinPipe()
|
||||
if stdinErr != nil {
|
||||
return false, stdinErr
|
||||
}
|
||||
if err := session.Shell(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
req := common.HubRequest[any]{Action: common.GetSystemdInfo, Data: common.SystemdInfoRequest{ServiceName: serviceName}}
|
||||
if err := cbor.NewEncoder(stdin).Encode(req); err != nil {
|
||||
return false, err
|
||||
}
|
||||
_ = stdin.Close()
|
||||
|
||||
var resp common.AgentResponse
|
||||
if err := cbor.NewDecoder(stdout).Decode(&resp); err != nil {
|
||||
return false, err
|
||||
}
|
||||
if resp.ServiceInfo == nil {
|
||||
if resp.Error != "" {
|
||||
return false, errors.New(resp.Error)
|
||||
}
|
||||
return false, errors.New("no systemd info in response")
|
||||
}
|
||||
result = resp.ServiceInfo
|
||||
return false, nil
|
||||
})
|
||||
err := sys.request(ctx, common.GetSystemdInfo, common.SystemdInfoRequest{ServiceName: serviceName}, &result)
|
||||
return result, err
|
||||
}
|
||||
|
||||
// FetchSmartDataFromAgent fetches SMART data from the agent
|
||||
func (sys *System) FetchSmartDataFromAgent() (map[string]smart.SmartData, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
||||
defer cancel()
|
||||
var result map[string]smart.SmartData
|
||||
err := sys.request(ctx, common.GetSmartData, nil, &result)
|
||||
return result, err
|
||||
}
|
||||
|
||||
@@ -598,6 +665,9 @@ func (sys *System) createSessionWithTimeout(timeout time.Duration) (*ssh.Session
|
||||
|
||||
// closeSSHConnection closes the SSH connection but keeps the system in the manager
|
||||
func (sys *System) closeSSHConnection() {
|
||||
if sys.sshTransport != nil {
|
||||
sys.sshTransport.Close()
|
||||
}
|
||||
if sys.client != nil {
|
||||
sys.client.Close()
|
||||
sys.client = nil
|
||||
|
||||
@@ -1,54 +1,14 @@
|
||||
package systems
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/fxamacker/cbor/v2"
|
||||
"github.com/henrygd/beszel/internal/common"
|
||||
"github.com/henrygd/beszel/internal/entities/smart"
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
// FetchSmartDataFromAgent fetches SMART data from the agent
|
||||
func (sys *System) FetchSmartDataFromAgent() (map[string]smart.SmartData, error) {
|
||||
// fetch via websocket
|
||||
if sys.WsConn != nil && sys.WsConn.IsConnected() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
return sys.WsConn.RequestSmartData(ctx)
|
||||
}
|
||||
// fetch via SSH
|
||||
var result map[string]smart.SmartData
|
||||
err := sys.runSSHOperation(5*time.Second, 1, func(session *ssh.Session) (bool, error) {
|
||||
stdout, err := session.StdoutPipe()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
stdin, stdinErr := session.StdinPipe()
|
||||
if stdinErr != nil {
|
||||
return false, stdinErr
|
||||
}
|
||||
if err := session.Shell(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
req := common.HubRequest[any]{Action: common.GetSmartData}
|
||||
_ = cbor.NewEncoder(stdin).Encode(req)
|
||||
_ = stdin.Close()
|
||||
var resp common.AgentResponse
|
||||
if err := cbor.NewDecoder(stdout).Decode(&resp); err != nil {
|
||||
return false, err
|
||||
}
|
||||
result = resp.SmartData
|
||||
return false, nil
|
||||
})
|
||||
return result, err
|
||||
}
|
||||
|
||||
// FetchAndSaveSmartDevices fetches SMART data from the agent and saves it to the database
|
||||
func (sys *System) FetchAndSaveSmartDevices() error {
|
||||
smartData, err := sys.FetchSmartDataFromAgent()
|
||||
|
||||
10
internal/hub/systems/systems_production.go
Normal file
10
internal/hub/systems/systems_production.go
Normal file
@@ -0,0 +1,10 @@
|
||||
//go:build !testing
|
||||
// +build !testing
|
||||
|
||||
package systems
|
||||
|
||||
// Background SMART fetching is enabled in production but disabled for tests (systems_test_helpers.go).
|
||||
//
|
||||
// The hub integration tests create/replace systems and clean up the test apps quickly.
|
||||
// Background SMART fetching can outlive teardown and crash in PocketBase internals (nil DB).
|
||||
func backgroundSmartFetchEnabled() bool { return true }
|
||||
@@ -266,18 +266,20 @@ func testOld(t *testing.T, hub *tests.TestHub) {
|
||||
|
||||
// Create test system data
|
||||
testData := &system.CombinedData{
|
||||
Details: &system.Details{
|
||||
Hostname: "data-test.example.com",
|
||||
Kernel: "5.15.0-generic",
|
||||
Cores: 4,
|
||||
Threads: 8,
|
||||
CpuModel: "Test CPU",
|
||||
},
|
||||
Info: system.Info{
|
||||
Hostname: "data-test.example.com",
|
||||
KernelVersion: "5.15.0-generic",
|
||||
Cores: 4,
|
||||
Threads: 8,
|
||||
CpuModel: "Test CPU",
|
||||
Uptime: 3600,
|
||||
Cpu: 25.5,
|
||||
MemPct: 40.2,
|
||||
DiskPct: 60.0,
|
||||
Bandwidth: 100.0,
|
||||
AgentVersion: "1.0.0",
|
||||
Uptime: 3600,
|
||||
Cpu: 25.5,
|
||||
MemPct: 40.2,
|
||||
DiskPct: 60.0,
|
||||
Bandwidth: 100.0,
|
||||
AgentVersion: "1.0.0",
|
||||
},
|
||||
Stats: system.Stats{
|
||||
Cpu: 25.5,
|
||||
|
||||
@@ -10,6 +10,13 @@ import (
|
||||
entities "github.com/henrygd/beszel/internal/entities/system"
|
||||
)
|
||||
|
||||
// The hub integration tests create/replace systems and cleanup the test apps quickly.
|
||||
// Background SMART fetching can outlive teardown and crash in PocketBase internals (nil DB).
|
||||
//
|
||||
// We keep the explicit SMART refresh endpoint / method available, but disable
|
||||
// the automatic background fetch during tests.
|
||||
func backgroundSmartFetchEnabled() bool { return false }
|
||||
|
||||
// TESTING ONLY: GetSystemCount returns the number of systems in the store
|
||||
func (sm *SystemManager) GetSystemCount() int {
|
||||
return sm.systems.Length()
|
||||
|
||||
227
internal/hub/transport/ssh.go
Normal file
227
internal/hub/transport/ssh.go
Normal file
@@ -0,0 +1,227 @@
|
||||
package transport
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/blang/semver"
|
||||
"github.com/fxamacker/cbor/v2"
|
||||
"github.com/henrygd/beszel/internal/common"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
// SSHTransport implements Transport over SSH connections.
|
||||
type SSHTransport struct {
|
||||
client *ssh.Client
|
||||
config *ssh.ClientConfig
|
||||
host string
|
||||
port string
|
||||
agentVersion semver.Version
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
// SSHTransportConfig holds configuration for creating an SSH transport.
|
||||
type SSHTransportConfig struct {
|
||||
Host string
|
||||
Port string
|
||||
Config *ssh.ClientConfig
|
||||
AgentVersion semver.Version
|
||||
Timeout time.Duration
|
||||
}
|
||||
|
||||
// NewSSHTransport creates a new SSH transport with the given configuration.
|
||||
func NewSSHTransport(cfg SSHTransportConfig) *SSHTransport {
|
||||
timeout := cfg.Timeout
|
||||
if timeout == 0 {
|
||||
timeout = 4 * time.Second
|
||||
}
|
||||
return &SSHTransport{
|
||||
config: cfg.Config,
|
||||
host: cfg.Host,
|
||||
port: cfg.Port,
|
||||
agentVersion: cfg.AgentVersion,
|
||||
timeout: timeout,
|
||||
}
|
||||
}
|
||||
|
||||
// SetClient sets the SSH client for reuse across requests.
|
||||
func (t *SSHTransport) SetClient(client *ssh.Client) {
|
||||
t.client = client
|
||||
}
|
||||
|
||||
// SetAgentVersion sets the agent version (extracted from SSH handshake).
|
||||
func (t *SSHTransport) SetAgentVersion(version semver.Version) {
|
||||
t.agentVersion = version
|
||||
}
|
||||
|
||||
// GetClient returns the current SSH client (for connection management).
|
||||
func (t *SSHTransport) GetClient() *ssh.Client {
|
||||
return t.client
|
||||
}
|
||||
|
||||
// GetAgentVersion returns the agent version.
|
||||
func (t *SSHTransport) GetAgentVersion() semver.Version {
|
||||
return t.agentVersion
|
||||
}
|
||||
|
||||
// Request sends a request to the agent via SSH and unmarshals the response.
|
||||
func (t *SSHTransport) Request(ctx context.Context, action common.WebSocketAction, req any, dest any) error {
|
||||
if t.client == nil {
|
||||
if err := t.connect(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
session, err := t.createSessionWithTimeout(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer session.Close()
|
||||
|
||||
stdout, err := session.StdoutPipe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
stdin, err := session.StdinPipe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := session.Shell(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Send request
|
||||
hubReq := common.HubRequest[any]{Action: action, Data: req}
|
||||
if err := cbor.NewEncoder(stdin).Encode(hubReq); err != nil {
|
||||
return fmt.Errorf("failed to encode request: %w", err)
|
||||
}
|
||||
stdin.Close()
|
||||
|
||||
// Read response
|
||||
var resp common.AgentResponse
|
||||
if err := cbor.NewDecoder(stdout).Decode(&resp); err != nil {
|
||||
return fmt.Errorf("failed to decode response: %w", err)
|
||||
}
|
||||
|
||||
if resp.Error != "" {
|
||||
return errors.New(resp.Error)
|
||||
}
|
||||
|
||||
if err := session.Wait(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return UnmarshalResponse(resp, action, dest)
|
||||
}
|
||||
|
||||
// IsConnected returns true if the SSH connection is active.
|
||||
func (t *SSHTransport) IsConnected() bool {
|
||||
return t.client != nil
|
||||
}
|
||||
|
||||
// Close terminates the SSH connection.
|
||||
func (t *SSHTransport) Close() {
|
||||
if t.client != nil {
|
||||
t.client.Close()
|
||||
t.client = nil
|
||||
}
|
||||
}
|
||||
|
||||
// connect establishes a new SSH connection.
|
||||
func (t *SSHTransport) connect() error {
|
||||
if t.config == nil {
|
||||
return errors.New("SSH config not set")
|
||||
}
|
||||
|
||||
network := "tcp"
|
||||
host := t.host
|
||||
if strings.HasPrefix(host, "/") {
|
||||
network = "unix"
|
||||
} else {
|
||||
host = net.JoinHostPort(host, t.port)
|
||||
}
|
||||
|
||||
client, err := ssh.Dial(network, host, t.config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.client = client
|
||||
|
||||
// Extract agent version from server version string
|
||||
t.agentVersion, _ = extractAgentVersion(string(client.Conn.ServerVersion()))
|
||||
return nil
|
||||
}
|
||||
|
||||
// createSessionWithTimeout creates a new SSH session with a timeout.
|
||||
func (t *SSHTransport) createSessionWithTimeout(ctx context.Context) (*ssh.Session, error) {
|
||||
if t.client == nil {
|
||||
return nil, errors.New("client not initialized")
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(ctx, t.timeout)
|
||||
defer cancel()
|
||||
|
||||
sessionChan := make(chan *ssh.Session, 1)
|
||||
errChan := make(chan error, 1)
|
||||
|
||||
go func() {
|
||||
session, err := t.client.NewSession()
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
} else {
|
||||
sessionChan <- session
|
||||
}
|
||||
}()
|
||||
|
||||
select {
|
||||
case session := <-sessionChan:
|
||||
return session, nil
|
||||
case err := <-errChan:
|
||||
return nil, err
|
||||
case <-ctx.Done():
|
||||
return nil, errors.New("timeout creating session")
|
||||
}
|
||||
}
|
||||
|
||||
// extractAgentVersion extracts the beszel version from SSH server version string.
|
||||
func extractAgentVersion(versionString string) (semver.Version, error) {
|
||||
_, after, _ := strings.Cut(versionString, "_")
|
||||
return semver.Parse(after)
|
||||
}
|
||||
|
||||
// RequestWithRetry sends a request with automatic retry on connection failures.
|
||||
func (t *SSHTransport) RequestWithRetry(ctx context.Context, action common.WebSocketAction, req any, dest any, retries int) error {
|
||||
var lastErr error
|
||||
for attempt := 0; attempt <= retries; attempt++ {
|
||||
err := t.Request(ctx, action, req, dest)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
lastErr = err
|
||||
|
||||
// Check if it's a connection error that warrants a retry
|
||||
if isConnectionError(err) && attempt < retries {
|
||||
t.Close()
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
return lastErr
|
||||
}
|
||||
|
||||
// isConnectionError checks if an error indicates a connection problem.
|
||||
func isConnectionError(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
errStr := err.Error()
|
||||
return strings.Contains(errStr, "connection") ||
|
||||
strings.Contains(errStr, "EOF") ||
|
||||
strings.Contains(errStr, "closed") ||
|
||||
errors.Is(err, io.EOF)
|
||||
}
|
||||
112
internal/hub/transport/transport.go
Normal file
112
internal/hub/transport/transport.go
Normal file
@@ -0,0 +1,112 @@
|
||||
// Package transport provides a unified abstraction for hub-agent communication
|
||||
// over different transports (WebSocket, SSH).
|
||||
package transport
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/fxamacker/cbor/v2"
|
||||
"github.com/henrygd/beszel/internal/common"
|
||||
"github.com/henrygd/beszel/internal/entities/smart"
|
||||
"github.com/henrygd/beszel/internal/entities/system"
|
||||
"github.com/henrygd/beszel/internal/entities/systemd"
|
||||
)
|
||||
|
||||
// Transport defines the interface for hub-agent communication.
|
||||
// Both WebSocket and SSH transports implement this interface.
|
||||
type Transport interface {
|
||||
// Request sends a request to the agent and unmarshals the response into dest.
|
||||
// The dest parameter should be a pointer to the expected response type.
|
||||
Request(ctx context.Context, action common.WebSocketAction, req any, dest any) error
|
||||
// IsConnected returns true if the transport connection is active.
|
||||
IsConnected() bool
|
||||
// Close terminates the transport connection.
|
||||
Close()
|
||||
}
|
||||
|
||||
// UnmarshalResponse unmarshals an AgentResponse into the destination type.
|
||||
// It first checks the generic Data field (0.19+ agents), then falls back
|
||||
// to legacy typed fields for backward compatibility with 0.18.0 agents.
|
||||
func UnmarshalResponse(resp common.AgentResponse, action common.WebSocketAction, dest any) error {
|
||||
if dest == nil {
|
||||
return errors.New("nil destination")
|
||||
}
|
||||
// Try generic Data field first (0.19+)
|
||||
if len(resp.Data) > 0 {
|
||||
if err := cbor.Unmarshal(resp.Data, dest); err != nil {
|
||||
return fmt.Errorf("failed to unmarshal generic response data: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// Fall back to legacy typed fields for older agents/hubs.
|
||||
return unmarshalLegacyResponse(resp, action, dest)
|
||||
}
|
||||
|
||||
// unmarshalLegacyResponse handles legacy responses that use typed fields.
|
||||
func unmarshalLegacyResponse(resp common.AgentResponse, action common.WebSocketAction, dest any) error {
|
||||
switch action {
|
||||
case common.GetData:
|
||||
d, ok := dest.(*system.CombinedData)
|
||||
if !ok {
|
||||
return fmt.Errorf("unexpected dest type for GetData: %T", dest)
|
||||
}
|
||||
if resp.SystemData == nil {
|
||||
return errors.New("no system data in response")
|
||||
}
|
||||
*d = *resp.SystemData
|
||||
return nil
|
||||
case common.CheckFingerprint:
|
||||
d, ok := dest.(*common.FingerprintResponse)
|
||||
if !ok {
|
||||
return fmt.Errorf("unexpected dest type for CheckFingerprint: %T", dest)
|
||||
}
|
||||
if resp.Fingerprint == nil {
|
||||
return errors.New("no fingerprint in response")
|
||||
}
|
||||
*d = *resp.Fingerprint
|
||||
return nil
|
||||
case common.GetContainerLogs:
|
||||
d, ok := dest.(*string)
|
||||
if !ok {
|
||||
return fmt.Errorf("unexpected dest type for GetContainerLogs: %T", dest)
|
||||
}
|
||||
if resp.String == nil {
|
||||
return errors.New("no logs in response")
|
||||
}
|
||||
*d = *resp.String
|
||||
return nil
|
||||
case common.GetContainerInfo:
|
||||
d, ok := dest.(*string)
|
||||
if !ok {
|
||||
return fmt.Errorf("unexpected dest type for GetContainerInfo: %T", dest)
|
||||
}
|
||||
if resp.String == nil {
|
||||
return errors.New("no info in response")
|
||||
}
|
||||
*d = *resp.String
|
||||
return nil
|
||||
case common.GetSmartData:
|
||||
d, ok := dest.(*map[string]smart.SmartData)
|
||||
if !ok {
|
||||
return fmt.Errorf("unexpected dest type for GetSmartData: %T", dest)
|
||||
}
|
||||
if resp.SmartData == nil {
|
||||
return errors.New("no SMART data in response")
|
||||
}
|
||||
*d = resp.SmartData
|
||||
return nil
|
||||
case common.GetSystemdInfo:
|
||||
d, ok := dest.(*systemd.ServiceDetails)
|
||||
if !ok {
|
||||
return fmt.Errorf("unexpected dest type for GetSystemdInfo: %T", dest)
|
||||
}
|
||||
if resp.ServiceInfo == nil {
|
||||
return errors.New("no systemd info in response")
|
||||
}
|
||||
*d = resp.ServiceInfo
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("unsupported action: %d", action)
|
||||
}
|
||||
74
internal/hub/transport/websocket.go
Normal file
74
internal/hub/transport/websocket.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package transport
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/fxamacker/cbor/v2"
|
||||
"github.com/henrygd/beszel"
|
||||
"github.com/henrygd/beszel/internal/common"
|
||||
"github.com/henrygd/beszel/internal/hub/ws"
|
||||
)
|
||||
|
||||
// ErrWebSocketNotConnected indicates a WebSocket transport is not currently connected.
|
||||
var ErrWebSocketNotConnected = errors.New("websocket not connected")
|
||||
|
||||
// WebSocketTransport implements Transport over WebSocket connections.
|
||||
type WebSocketTransport struct {
|
||||
wsConn *ws.WsConn
|
||||
}
|
||||
|
||||
// NewWebSocketTransport creates a new WebSocket transport wrapper.
|
||||
func NewWebSocketTransport(wsConn *ws.WsConn) *WebSocketTransport {
|
||||
return &WebSocketTransport{wsConn: wsConn}
|
||||
}
|
||||
|
||||
// Request sends a request to the agent via WebSocket and unmarshals the response.
|
||||
func (t *WebSocketTransport) Request(ctx context.Context, action common.WebSocketAction, req any, dest any) error {
|
||||
if !t.IsConnected() {
|
||||
return ErrWebSocketNotConnected
|
||||
}
|
||||
|
||||
pendingReq, err := t.wsConn.SendRequest(ctx, action, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Wait for response
|
||||
select {
|
||||
case message := <-pendingReq.ResponseCh:
|
||||
defer message.Close()
|
||||
defer pendingReq.Cancel()
|
||||
|
||||
// Legacy agents (< MinVersionAgentResponse) respond with a raw payload instead of an AgentResponse wrapper.
|
||||
if t.wsConn.AgentVersion().LT(beszel.MinVersionAgentResponse) {
|
||||
return cbor.Unmarshal(message.Data.Bytes(), dest)
|
||||
}
|
||||
|
||||
var agentResponse common.AgentResponse
|
||||
if err := cbor.Unmarshal(message.Data.Bytes(), &agentResponse); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if agentResponse.Error != "" {
|
||||
return errors.New(agentResponse.Error)
|
||||
}
|
||||
|
||||
return UnmarshalResponse(agentResponse, action, dest)
|
||||
|
||||
case <-pendingReq.Context.Done():
|
||||
return pendingReq.Context.Err()
|
||||
}
|
||||
}
|
||||
|
||||
// IsConnected returns true if the WebSocket connection is active.
|
||||
func (t *WebSocketTransport) IsConnected() bool {
|
||||
return t.wsConn != nil && t.wsConn.IsConnected()
|
||||
}
|
||||
|
||||
// Close terminates the WebSocket connection.
|
||||
func (t *WebSocketTransport) Close() {
|
||||
if t.wsConn != nil {
|
||||
t.wsConn.Close(nil)
|
||||
}
|
||||
}
|
||||
@@ -6,14 +6,12 @@ import (
|
||||
|
||||
"github.com/fxamacker/cbor/v2"
|
||||
"github.com/henrygd/beszel/internal/common"
|
||||
"github.com/henrygd/beszel/internal/entities/smart"
|
||||
"github.com/henrygd/beszel/internal/entities/system"
|
||||
"github.com/henrygd/beszel/internal/entities/systemd"
|
||||
"github.com/lxzan/gws"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
// ResponseHandler defines interface for handling agent responses
|
||||
// ResponseHandler defines interface for handling agent responses.
|
||||
// This is used by handleAgentRequest for legacy response handling.
|
||||
type ResponseHandler interface {
|
||||
Handle(agentResponse common.AgentResponse) error
|
||||
HandleLegacy(rawData []byte) error
|
||||
@@ -27,167 +25,7 @@ func (h *BaseHandler) HandleLegacy(rawData []byte) error {
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// systemDataHandler implements ResponseHandler for system data requests
|
||||
type systemDataHandler struct {
|
||||
data *system.CombinedData
|
||||
}
|
||||
|
||||
func (h *systemDataHandler) HandleLegacy(rawData []byte) error {
|
||||
return cbor.Unmarshal(rawData, h.data)
|
||||
}
|
||||
|
||||
func (h *systemDataHandler) Handle(agentResponse common.AgentResponse) error {
|
||||
if agentResponse.SystemData != nil {
|
||||
*h.data = *agentResponse.SystemData
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RequestSystemData requests system metrics from the agent and unmarshals the response.
|
||||
func (ws *WsConn) RequestSystemData(ctx context.Context, data *system.CombinedData, options common.DataRequestOptions) error {
|
||||
if !ws.IsConnected() {
|
||||
return gws.ErrConnClosed
|
||||
}
|
||||
|
||||
req, err := ws.requestManager.SendRequest(ctx, common.GetData, options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
handler := &systemDataHandler{data: data}
|
||||
return ws.handleAgentRequest(req, handler)
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// stringResponseHandler is a generic handler for string responses from agents
|
||||
type stringResponseHandler struct {
|
||||
BaseHandler
|
||||
value string
|
||||
errorMsg string
|
||||
}
|
||||
|
||||
func (h *stringResponseHandler) Handle(agentResponse common.AgentResponse) error {
|
||||
if agentResponse.String == nil {
|
||||
return errors.New(h.errorMsg)
|
||||
}
|
||||
h.value = *agentResponse.String
|
||||
return nil
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// requestContainerStringViaWS is a generic function to request container-related strings via WebSocket
|
||||
func (ws *WsConn) requestContainerStringViaWS(ctx context.Context, action common.WebSocketAction, requestData any, errorMsg string) (string, error) {
|
||||
if !ws.IsConnected() {
|
||||
return "", gws.ErrConnClosed
|
||||
}
|
||||
|
||||
req, err := ws.requestManager.SendRequest(ctx, action, requestData)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
handler := &stringResponseHandler{errorMsg: errorMsg}
|
||||
if err := ws.handleAgentRequest(req, handler); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return handler.value, nil
|
||||
}
|
||||
|
||||
// RequestContainerLogs requests logs for a specific container via WebSocket.
|
||||
func (ws *WsConn) RequestContainerLogs(ctx context.Context, containerID string) (string, error) {
|
||||
return ws.requestContainerStringViaWS(ctx, common.GetContainerLogs, common.ContainerLogsRequest{ContainerID: containerID}, "no logs in response")
|
||||
}
|
||||
|
||||
// RequestContainerInfo requests information about a specific container via WebSocket.
|
||||
func (ws *WsConn) RequestContainerInfo(ctx context.Context, containerID string) (string, error) {
|
||||
return ws.requestContainerStringViaWS(ctx, common.GetContainerInfo, common.ContainerInfoRequest{ContainerID: containerID}, "no info in response")
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// RequestSystemdInfo requests detailed information about a systemd service via WebSocket.
|
||||
func (ws *WsConn) RequestSystemdInfo(ctx context.Context, serviceName string) (systemd.ServiceDetails, error) {
|
||||
if !ws.IsConnected() {
|
||||
return nil, gws.ErrConnClosed
|
||||
}
|
||||
|
||||
req, err := ws.requestManager.SendRequest(ctx, common.GetSystemdInfo, common.SystemdInfoRequest{ServiceName: serviceName})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result systemd.ServiceDetails
|
||||
handler := &systemdInfoHandler{result: &result}
|
||||
if err := ws.handleAgentRequest(req, handler); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// systemdInfoHandler parses ServiceDetails from AgentResponse
|
||||
type systemdInfoHandler struct {
|
||||
BaseHandler
|
||||
result *systemd.ServiceDetails
|
||||
}
|
||||
|
||||
func (h *systemdInfoHandler) Handle(agentResponse common.AgentResponse) error {
|
||||
if agentResponse.ServiceInfo == nil {
|
||||
return errors.New("no systemd info in response")
|
||||
}
|
||||
*h.result = agentResponse.ServiceInfo
|
||||
return nil
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// RequestSmartData requests SMART data via WebSocket.
|
||||
func (ws *WsConn) RequestSmartData(ctx context.Context) (map[string]smart.SmartData, error) {
|
||||
if !ws.IsConnected() {
|
||||
return nil, gws.ErrConnClosed
|
||||
}
|
||||
req, err := ws.requestManager.SendRequest(ctx, common.GetSmartData, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var result map[string]smart.SmartData
|
||||
handler := ResponseHandler(&smartDataHandler{result: &result})
|
||||
if err := ws.handleAgentRequest(req, handler); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// smartDataHandler parses SMART data map from AgentResponse
|
||||
type smartDataHandler struct {
|
||||
BaseHandler
|
||||
result *map[string]smart.SmartData
|
||||
}
|
||||
|
||||
func (h *smartDataHandler) Handle(agentResponse common.AgentResponse) error {
|
||||
if agentResponse.SmartData == nil {
|
||||
return errors.New("no SMART data in response")
|
||||
}
|
||||
*h.result = agentResponse.SmartData
|
||||
return nil
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Fingerprint handling (used for WebSocket authentication)
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// fingerprintHandler implements ResponseHandler for fingerprint requests
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
//go:build testing
|
||||
|
||||
package ws
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/henrygd/beszel/internal/common"
|
||||
"github.com/henrygd/beszel/internal/entities/systemd"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestSystemdInfoHandlerSuccess(t *testing.T) {
|
||||
handler := &systemdInfoHandler{
|
||||
result: &systemd.ServiceDetails{},
|
||||
}
|
||||
|
||||
// Test successful handling with valid ServiceInfo
|
||||
testDetails := systemd.ServiceDetails{
|
||||
"Id": "nginx.service",
|
||||
"ActiveState": "active",
|
||||
"SubState": "running",
|
||||
"Description": "A high performance web server",
|
||||
"ExecMainPID": 1234,
|
||||
"MemoryCurrent": 1024000,
|
||||
}
|
||||
|
||||
response := common.AgentResponse{
|
||||
ServiceInfo: testDetails,
|
||||
}
|
||||
|
||||
err := handler.Handle(response)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, testDetails, *handler.result)
|
||||
}
|
||||
|
||||
func TestSystemdInfoHandlerError(t *testing.T) {
|
||||
handler := &systemdInfoHandler{
|
||||
result: &systemd.ServiceDetails{},
|
||||
}
|
||||
|
||||
// Test error handling when ServiceInfo is nil
|
||||
response := common.AgentResponse{
|
||||
ServiceInfo: nil,
|
||||
Error: "service not found",
|
||||
}
|
||||
|
||||
err := handler.Handle(response)
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, "no systemd info in response", err.Error())
|
||||
}
|
||||
|
||||
func TestSystemdInfoHandlerEmptyResponse(t *testing.T) {
|
||||
handler := &systemdInfoHandler{
|
||||
result: &systemd.ServiceDetails{},
|
||||
}
|
||||
|
||||
// Test with completely empty response
|
||||
response := common.AgentResponse{}
|
||||
|
||||
err := handler.Handle(response)
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, "no systemd info in response", err.Error())
|
||||
}
|
||||
|
||||
func TestSystemdInfoHandlerLegacyNotSupported(t *testing.T) {
|
||||
handler := &systemdInfoHandler{
|
||||
result: &systemd.ServiceDetails{},
|
||||
}
|
||||
|
||||
// Test that legacy format is not supported
|
||||
err := handler.HandleLegacy([]byte("some data"))
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, "legacy format not supported", err.Error())
|
||||
}
|
||||
@@ -45,7 +45,15 @@ func NewRequestManager(conn *gws.Conn) *RequestManager {
|
||||
func (rm *RequestManager) SendRequest(ctx context.Context, action common.WebSocketAction, data any) (*PendingRequest, error) {
|
||||
reqID := RequestID(rm.nextID.Add(1))
|
||||
|
||||
reqCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
// Respect any caller-provided deadline. If none is set, apply a reasonable default
|
||||
// so pending requests don't live forever if the agent never responds.
|
||||
reqCtx := ctx
|
||||
var cancel context.CancelFunc
|
||||
if _, hasDeadline := ctx.Deadline(); hasDeadline {
|
||||
reqCtx, cancel = context.WithCancel(ctx)
|
||||
} else {
|
||||
reqCtx, cancel = context.WithTimeout(ctx, 5*time.Second)
|
||||
}
|
||||
|
||||
req := &PendingRequest{
|
||||
ID: reqID,
|
||||
@@ -100,6 +108,11 @@ func (rm *RequestManager) handleResponse(message *gws.Message) {
|
||||
return
|
||||
}
|
||||
|
||||
if response.Id == nil {
|
||||
rm.routeLegacyResponse(message)
|
||||
return
|
||||
}
|
||||
|
||||
reqID := RequestID(*response.Id)
|
||||
|
||||
rm.RLock()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package ws
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
"weak"
|
||||
@@ -161,3 +162,14 @@ func (ws *WsConn) handleAgentRequest(req *PendingRequest, handler ResponseHandle
|
||||
func (ws *WsConn) IsConnected() bool {
|
||||
return ws.conn != nil
|
||||
}
|
||||
|
||||
// AgentVersion returns the connected agent's version (as reported during handshake).
|
||||
func (ws *WsConn) AgentVersion() semver.Version {
|
||||
return ws.agentVersion
|
||||
}
|
||||
|
||||
// SendRequest sends a request to the agent and returns a pending request handle.
|
||||
// This is used by the transport layer to send requests.
|
||||
func (ws *WsConn) SendRequest(ctx context.Context, action common.WebSocketAction, data any) (*PendingRequest, error) {
|
||||
return ws.requestManager.SendRequest(ctx, action, data)
|
||||
}
|
||||
|
||||
@@ -184,14 +184,18 @@ func TestCommonActions(t *testing.T) {
|
||||
assert.Equal(t, common.WebSocketAction(2), common.GetContainerLogs, "GetLogs should be action 2")
|
||||
}
|
||||
|
||||
func TestLogsHandler(t *testing.T) {
|
||||
h := &stringResponseHandler{errorMsg: "no logs in response"}
|
||||
func TestFingerprintHandler(t *testing.T) {
|
||||
var result common.FingerprintResponse
|
||||
h := &fingerprintHandler{result: &result}
|
||||
|
||||
logValue := "test logs"
|
||||
resp := common.AgentResponse{String: &logValue}
|
||||
resp := common.AgentResponse{Fingerprint: &common.FingerprintResponse{
|
||||
Fingerprint: "test-fingerprint",
|
||||
Hostname: "test-host",
|
||||
}}
|
||||
err := h.Handle(resp)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, logValue, h.value)
|
||||
assert.Equal(t, "test-fingerprint", result.Fingerprint)
|
||||
assert.Equal(t, "test-host", result.Hostname)
|
||||
}
|
||||
|
||||
// TestHandler tests that we can create a Handler
|
||||
|
||||
@@ -78,7 +78,8 @@ func init() {
|
||||
"GPU",
|
||||
"LoadAvg1",
|
||||
"LoadAvg5",
|
||||
"LoadAvg15"
|
||||
"LoadAvg15",
|
||||
"Battery"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1438,6 +1439,252 @@ func init() {
|
||||
"type": "base",
|
||||
"updateRule": null,
|
||||
"viewRule": "@request.auth.id != \"\" && system.users.id ?= @request.auth.id"
|
||||
},
|
||||
{
|
||||
"createRule": "",
|
||||
"deleteRule": "",
|
||||
"fields": [
|
||||
{
|
||||
"autogeneratePattern": "[a-z0-9]{15}",
|
||||
"hidden": false,
|
||||
"id": "text3208210256",
|
||||
"max": 15,
|
||||
"min": 15,
|
||||
"name": "id",
|
||||
"pattern": "^[a-z0-9]+$",
|
||||
"presentable": false,
|
||||
"primaryKey": true,
|
||||
"required": true,
|
||||
"system": true,
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"cascadeDelete": true,
|
||||
"collectionId": "2hz5ncl8tizk5nx",
|
||||
"hidden": false,
|
||||
"id": "relation3377271179",
|
||||
"maxSelect": 1,
|
||||
"minSelect": 0,
|
||||
"name": "system",
|
||||
"presentable": false,
|
||||
"required": true,
|
||||
"system": false,
|
||||
"type": "relation"
|
||||
},
|
||||
{
|
||||
"autogeneratePattern": "",
|
||||
"hidden": false,
|
||||
"id": "text3847340049",
|
||||
"max": 0,
|
||||
"min": 0,
|
||||
"name": "hostname",
|
||||
"pattern": "",
|
||||
"presentable": false,
|
||||
"primaryKey": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"hidden": false,
|
||||
"id": "number1789936913",
|
||||
"max": null,
|
||||
"min": null,
|
||||
"name": "os",
|
||||
"onlyInt": false,
|
||||
"presentable": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"autogeneratePattern": "",
|
||||
"hidden": false,
|
||||
"id": "text2818598173",
|
||||
"max": 0,
|
||||
"min": 0,
|
||||
"name": "os_name",
|
||||
"pattern": "",
|
||||
"presentable": false,
|
||||
"primaryKey": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"autogeneratePattern": "",
|
||||
"hidden": false,
|
||||
"id": "text1574083243",
|
||||
"max": 0,
|
||||
"min": 0,
|
||||
"name": "kernel",
|
||||
"pattern": "",
|
||||
"presentable": false,
|
||||
"primaryKey": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"autogeneratePattern": "",
|
||||
"hidden": false,
|
||||
"id": "text3128971310",
|
||||
"max": 0,
|
||||
"min": 0,
|
||||
"name": "cpu",
|
||||
"pattern": "",
|
||||
"presentable": false,
|
||||
"primaryKey": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"autogeneratePattern": "",
|
||||
"hidden": false,
|
||||
"id": "text4161937994",
|
||||
"max": 0,
|
||||
"min": 0,
|
||||
"name": "arch",
|
||||
"pattern": "",
|
||||
"presentable": false,
|
||||
"primaryKey": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"hidden": false,
|
||||
"id": "number4245036687",
|
||||
"max": null,
|
||||
"min": null,
|
||||
"name": "cores",
|
||||
"onlyInt": false,
|
||||
"presentable": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"hidden": false,
|
||||
"id": "number1871592925",
|
||||
"max": null,
|
||||
"min": null,
|
||||
"name": "threads",
|
||||
"onlyInt": false,
|
||||
"presentable": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"hidden": false,
|
||||
"id": "number3933025333",
|
||||
"max": null,
|
||||
"min": null,
|
||||
"name": "memory",
|
||||
"onlyInt": false,
|
||||
"presentable": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"hidden": false,
|
||||
"id": "bool2200265312",
|
||||
"name": "podman",
|
||||
"presentable": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "bool"
|
||||
},
|
||||
{
|
||||
"hidden": false,
|
||||
"id": "autodate3332085495",
|
||||
"name": "updated",
|
||||
"onCreate": true,
|
||||
"onUpdate": true,
|
||||
"presentable": false,
|
||||
"system": false,
|
||||
"type": "autodate"
|
||||
}
|
||||
],
|
||||
"id": "pbc_3116237454",
|
||||
"indexes": [],
|
||||
"listRule": "@request.auth.id != \"\" && system.users.id ?= @request.auth.id",
|
||||
"name": "system_details",
|
||||
"system": false,
|
||||
"type": "base",
|
||||
"updateRule": "",
|
||||
"viewRule": "@request.auth.id != \"\" && system.users.id ?= @request.auth.id"
|
||||
},
|
||||
{
|
||||
"createRule": null,
|
||||
"deleteRule": null,
|
||||
"fields": [
|
||||
{
|
||||
"autogeneratePattern": "[a-z0-9]{10}",
|
||||
"hidden": false,
|
||||
"id": "text3208210256",
|
||||
"max": 10,
|
||||
"min": 10,
|
||||
"name": "id",
|
||||
"pattern": "^[a-z0-9]+$",
|
||||
"presentable": false,
|
||||
"primaryKey": true,
|
||||
"required": true,
|
||||
"system": true,
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"cascadeDelete": true,
|
||||
"collectionId": "_pb_users_auth_",
|
||||
"hidden": false,
|
||||
"id": "relation2375276105",
|
||||
"maxSelect": 1,
|
||||
"minSelect": 0,
|
||||
"name": "user",
|
||||
"presentable": false,
|
||||
"required": true,
|
||||
"system": false,
|
||||
"type": "relation"
|
||||
},
|
||||
{
|
||||
"autogeneratePattern": "",
|
||||
"hidden": false,
|
||||
"id": "text1597481275",
|
||||
"max": 0,
|
||||
"min": 0,
|
||||
"name": "token",
|
||||
"pattern": "",
|
||||
"presentable": false,
|
||||
"primaryKey": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"hidden": false,
|
||||
"id": "autodate2990389176",
|
||||
"name": "created",
|
||||
"onCreate": true,
|
||||
"onUpdate": false,
|
||||
"presentable": false,
|
||||
"system": false,
|
||||
"type": "autodate"
|
||||
}
|
||||
],
|
||||
"id": "pbc_3383022248",
|
||||
"indexes": [
|
||||
"CREATE INDEX ` + "`" + `idx_iaD9Y2Lgbl` + "`" + ` ON ` + "`" + `universal_tokens` + "`" + ` (` + "`" + `token` + "`" + `)",
|
||||
"CREATE UNIQUE INDEX ` + "`" + `idx_wdR0A4PbRG` + "`" + ` ON ` + "`" + `universal_tokens` + "`" + ` (` + "`" + `user` + "`" + `)"
|
||||
],
|
||||
"listRule": null,
|
||||
"name": "universal_tokens",
|
||||
"system": false,
|
||||
"type": "base",
|
||||
"updateRule": null,
|
||||
"viewRule": null
|
||||
}
|
||||
]`
|
||||
|
||||
@@ -33,10 +33,7 @@
|
||||
"noUnusedFunctionParameters": "error",
|
||||
"noUnusedPrivateClassMembers": "error",
|
||||
"useExhaustiveDependencies": {
|
||||
"level": "warn",
|
||||
"options": {
|
||||
"reportUnnecessaryDependencies": false
|
||||
}
|
||||
"level": "off"
|
||||
},
|
||||
"useUniqueElementIds": "off",
|
||||
"noUnusedVariables": "error"
|
||||
|
||||
@@ -36,8 +36,8 @@
|
||||
"lucide-react": "^0.452.0",
|
||||
"nanostores": "^0.11.4",
|
||||
"pocketbase": "^0.26.2",
|
||||
"react": "^19.1.1",
|
||||
"react-dom": "^19.1.1",
|
||||
"react": "^19.1.2",
|
||||
"react-dom": "^19.1.2",
|
||||
"recharts": "^2.15.4",
|
||||
"shiki": "^3.13.0",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
@@ -811,9 +811,9 @@
|
||||
|
||||
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
|
||||
|
||||
"react": ["react@19.1.1", "", {}, "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ=="],
|
||||
"react": ["react@19.2.3", "", {}, "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA=="],
|
||||
|
||||
"react-dom": ["react-dom@19.1.1", "", { "dependencies": { "scheduler": "^0.26.0" }, "peerDependencies": { "react": "^19.1.1" } }, "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw=="],
|
||||
"react-dom": ["react-dom@19.2.3", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.3" } }, "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg=="],
|
||||
|
||||
"react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="],
|
||||
|
||||
@@ -851,7 +851,7 @@
|
||||
|
||||
"safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
|
||||
|
||||
"scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="],
|
||||
"scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
|
||||
|
||||
"semver": ["semver@6.3.1", "", { "bin": "bin/semver.js" }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||
|
||||
@@ -971,8 +971,6 @@
|
||||
|
||||
"prop-types/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
|
||||
|
||||
"pseudolocale/commander": ["commander@10.0.1", "", {}, "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug=="],
|
||||
|
||||
"readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||
|
||||
"restore-cursor/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="],
|
||||
@@ -981,28 +979,18 @@
|
||||
|
||||
"string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||
|
||||
"string-width-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
||||
|
||||
"strip-ansi-cjs/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||
|
||||
"wrap-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="],
|
||||
|
||||
"wrap-ansi/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="],
|
||||
|
||||
"wrap-ansi-cjs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
||||
|
||||
"wrap-ansi-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
||||
|
||||
"@isaacs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.0.1", "", {}, "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA=="],
|
||||
|
||||
"string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||
|
||||
"string-width/strip-ansi/ansi-regex": ["ansi-regex@6.0.1", "", {}, "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA=="],
|
||||
|
||||
"wrap-ansi-cjs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||
|
||||
"wrap-ansi-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||
|
||||
"wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@6.0.1", "", {}, "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA=="],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ export default defineConfig({
|
||||
"he",
|
||||
"hr",
|
||||
"hu",
|
||||
"id",
|
||||
"it",
|
||||
"ja",
|
||||
"ko",
|
||||
@@ -24,6 +25,7 @@ export default defineConfig({
|
||||
"tr",
|
||||
"ru",
|
||||
"sl",
|
||||
"sr",
|
||||
"sv",
|
||||
"uk",
|
||||
"vi",
|
||||
|
||||
329
internal/site/package-lock.json
generated
329
internal/site/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "beszel",
|
||||
"version": "0.16.1",
|
||||
"version": "0.18.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "beszel",
|
||||
"version": "0.16.1",
|
||||
"version": "0.18.0",
|
||||
"dependencies": {
|
||||
"@henrygd/queue": "^1.0.7",
|
||||
"@henrygd/semaphore": "^0.0.2",
|
||||
@@ -39,8 +39,8 @@
|
||||
"lucide-react": "^0.452.0",
|
||||
"nanostores": "^0.11.4",
|
||||
"pocketbase": "^0.26.2",
|
||||
"react": "^19.1.1",
|
||||
"react-dom": "^19.1.1",
|
||||
"react": "^19.1.2",
|
||||
"react-dom": "^19.1.2",
|
||||
"recharts": "^2.15.4",
|
||||
"shiki": "^3.13.0",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
@@ -111,6 +111,7 @@
|
||||
"integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@ampproject/remapping": "^2.2.0",
|
||||
"@babel/code-frame": "^7.27.1",
|
||||
@@ -986,6 +987,29 @@
|
||||
"integrity": "sha512-N3W7MKwTRmAxOjeG0NAT18oe2Xn3KdjkpMR6crbkF1UDamMGPjyigqEsefiv+qTaxibtc1a+zXCVzb9YXANVqw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@isaacs/balanced-match": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz",
|
||||
"integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/@isaacs/brace-expansion": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz",
|
||||
"integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@isaacs/balanced-match": "^4.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/@isaacs/cliui": {
|
||||
"version": "8.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
||||
@@ -1114,6 +1138,7 @@
|
||||
"integrity": "sha512-9IO+PDvdneY8OCI8zvI1oDXpzryTMtyRv7uq9O0U1mFCvIPVd5dWQKQDu/CpgpYAc2+JG/izn5PNl9xzPc6ckw==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.20.12",
|
||||
"@babel/runtime": "^7.20.13",
|
||||
@@ -1206,30 +1231,6 @@
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@lingui/cli/node_modules/glob": {
|
||||
"version": "11.0.1",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-11.0.1.tgz",
|
||||
"integrity": "sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"foreground-child": "^3.1.0",
|
||||
"jackspeak": "^4.0.1",
|
||||
"minimatch": "^10.0.0",
|
||||
"minipass": "^7.1.2",
|
||||
"package-json-from-dist": "^1.0.0",
|
||||
"path-scurry": "^2.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"glob": "dist/esm/bin.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/@lingui/cli/node_modules/glob-parent": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||
@@ -1243,65 +1244,6 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/@lingui/cli/node_modules/jackspeak": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.0.tgz",
|
||||
"integrity": "sha512-9DDdhb5j6cpeitCbvLO7n7J4IxnbM6hoF6O1g4HQ5TfhvvKN8ywDM7668ZhMHRqVmxqhps/F6syWK2KcPxYlkw==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"@isaacs/cliui": "^8.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/@lingui/cli/node_modules/lru-cache": {
|
||||
"version": "11.0.2",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.2.tgz",
|
||||
"integrity": "sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/@lingui/cli/node_modules/minimatch": {
|
||||
"version": "10.0.1",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz",
|
||||
"integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/@lingui/cli/node_modules/path-scurry": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz",
|
||||
"integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"lru-cache": "^11.0.0",
|
||||
"minipass": "^7.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/@lingui/cli/node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||
@@ -1350,6 +1292,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@lingui/core/-/core-5.4.1.tgz",
|
||||
"integrity": "sha512-4FeIh56PH5vziPg2BYo4XYWWOHE4XaY/XR8Jakwn0/qwtLpydWMNVpZOpGWi7nfPZtcLaJLmZKup6UNxEl1Pfw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@lingui/message-utils": "5.4.1"
|
||||
@@ -3545,6 +3488,7 @@
|
||||
"integrity": "sha512-lr3jdBw/BGj49Eps7EvqlUaoeA0xpj3pc0RoJkHpYaCHkVK7i28dKyImLQb3JVlqs3aYSXf7qYuWOW/fgZnTXQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
@@ -3555,6 +3499,7 @@
|
||||
"integrity": "sha512-i5ZzwYpqjmrKenzkoLM2Ibzt6mAsM7pxB6BCIouEVVmgiqaMj1TjaK7hnA36hbW5aZv20kx7Lw6hWzPWg0Rurw==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"peerDependencies": {
|
||||
"@types/react": "^19.0.0"
|
||||
}
|
||||
@@ -3606,9 +3551,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-regex": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
|
||||
"integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
|
||||
"version": "6.2.2",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
|
||||
"integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -3680,13 +3625,6 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/base64-js": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||
@@ -3733,16 +3671,6 @@
|
||||
"readable-stream": "^3.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
|
||||
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/braces": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
||||
@@ -3776,6 +3704,7 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"caniuse-lite": "^1.0.30001726",
|
||||
"electron-to-chromium": "^1.5.173",
|
||||
@@ -4486,13 +4415,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/foreground-child": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.2.1.tgz",
|
||||
"integrity": "sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==",
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
|
||||
"integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"cross-spawn": "^7.0.0",
|
||||
"cross-spawn": "^7.0.6",
|
||||
"signal-exit": "^4.0.1"
|
||||
},
|
||||
"engines": {
|
||||
@@ -4536,6 +4465,30 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/glob": {
|
||||
"version": "11.1.0",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz",
|
||||
"integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"foreground-child": "^3.3.1",
|
||||
"jackspeak": "^4.1.1",
|
||||
"minimatch": "^10.1.1",
|
||||
"minipass": "^7.1.2",
|
||||
"package-json-from-dist": "^1.0.0",
|
||||
"path-scurry": "^2.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"glob": "dist/esm/bin.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/graceful-fs": {
|
||||
"version": "4.2.11",
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
||||
@@ -4756,6 +4709,22 @@
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/jackspeak": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz",
|
||||
"integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"@isaacs/cliui": "^8.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/jest-get-type": {
|
||||
"version": "29.6.3",
|
||||
"resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz",
|
||||
@@ -5180,9 +5149,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/mdast-util-to-hast": {
|
||||
"version": "13.2.0",
|
||||
"resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz",
|
||||
"integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==",
|
||||
"version": "13.2.1",
|
||||
"resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz",
|
||||
"integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/hast": "^3.0.0",
|
||||
@@ -5326,6 +5295,22 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz",
|
||||
"integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"@isaacs/brace-expansion": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/minipass": {
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
|
||||
@@ -5408,6 +5393,7 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^18.0.0 || >=20.0.0"
|
||||
}
|
||||
@@ -5567,6 +5553,33 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/path-scurry": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz",
|
||||
"integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"lru-cache": "^11.0.0",
|
||||
"minipass": "^7.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/path-scurry/node_modules/lru-cache": {
|
||||
"version": "11.2.4",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz",
|
||||
"integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/path-type": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
|
||||
@@ -5590,6 +5603,7 @@
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -5731,24 +5745,26 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react": {
|
||||
"version": "19.1.1",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-19.1.1.tgz",
|
||||
"integrity": "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==",
|
||||
"version": "19.1.2",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-19.1.2.tgz",
|
||||
"integrity": "sha512-MdWVitvLbQULD+4DP8GYjZUrepGW7d+GQkNVqJEzNxE+e9WIa4egVFE/RDfVb1u9u/Jw7dNMmPB4IqxzbFYJ0w==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-dom": {
|
||||
"version": "19.1.1",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.1.tgz",
|
||||
"integrity": "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==",
|
||||
"version": "19.1.2",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.2.tgz",
|
||||
"integrity": "sha512-dEoydsCp50i7kS1xHOmPXq4zQYoGWedUsvqv9H6zdif2r7yLHygyfP9qou71TulRN0d6ng9EbRVsQhSqfUc19g==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"scheduler": "^0.26.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^19.1.1"
|
||||
"react": "^19.1.2"
|
||||
}
|
||||
},
|
||||
"node_modules/react-is": {
|
||||
@@ -6171,6 +6187,16 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/string-width-cjs/node_modules/ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/string-width-cjs/node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
@@ -6191,16 +6217,6 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/string-width-cjs/node_modules/strip-ansi/node_modules/ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/stringify-entities": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz",
|
||||
@@ -6216,9 +6232,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/strip-ansi": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
|
||||
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz",
|
||||
"integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -6283,7 +6299,8 @@
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.12.tgz",
|
||||
"integrity": "sha512-DzFtxOi+7NsFf7DBtI3BJsynR+0Yp6etH+nRPTbpWnS2pZBaSksv/JGctNwSWzbFjp0vxSqknaUylseZqMDGrA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/tapable": {
|
||||
"version": "2.2.3",
|
||||
@@ -6405,6 +6422,7 @@
|
||||
"integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
@@ -6639,6 +6657,7 @@
|
||||
"integrity": "sha512-uzcxnSDVjAopEUjljkWh8EIrg6tlzrjFUfMcR1EVsRDGwf/ccef0qQPRyOrROwhrTDaApueq+ja+KLPlzR/zdg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.25.0",
|
||||
"fdir": "^6.5.0",
|
||||
@@ -6790,6 +6809,23 @@
|
||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs/node_modules/ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs/node_modules/string-width": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
@@ -6805,13 +6841,6 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs/node_modules/string-width/node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs/node_modules/strip-ansi": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
@@ -6825,20 +6854,10 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs/node_modules/strip-ansi/node_modules/ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi/node_modules/ansi-styles": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
|
||||
"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
|
||||
"version": "6.2.3",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
|
||||
"integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "beszel",
|
||||
"private": true,
|
||||
"version": "0.16.1",
|
||||
"version": "0.18.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite --host",
|
||||
@@ -46,8 +46,8 @@
|
||||
"lucide-react": "^0.452.0",
|
||||
"nanostores": "^0.11.4",
|
||||
"pocketbase": "^0.26.2",
|
||||
"react": "^19.1.1",
|
||||
"react-dom": "^19.1.1",
|
||||
"react": "^19.1.2",
|
||||
"react-dom": "^19.1.2",
|
||||
"recharts": "^2.15.4",
|
||||
"shiki": "^3.13.0",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
@@ -77,4 +77,4 @@
|
||||
"optionalDependencies": {
|
||||
"@esbuild/linux-arm64": "^0.21.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +61,11 @@ export const ActiveAlerts = () => {
|
||||
<AlertDescription>
|
||||
{alert.name === "Status" ? (
|
||||
<Trans>Connection is down</Trans>
|
||||
) : info.invert ? (
|
||||
<Trans>
|
||||
Below {alert.value}
|
||||
{info.unit} in last <Plural value={alert.min} one="# minute" other="# minutes" />
|
||||
</Trans>
|
||||
) : (
|
||||
<Trans>
|
||||
Exceeds {alert.value}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { t } from "@lingui/core/macro"
|
||||
import { msg, t } from "@lingui/core/macro"
|
||||
import { Trans } from "@lingui/react/macro"
|
||||
import { useStore } from "@nanostores/react"
|
||||
import { getPagePath } from "@nanostores/router"
|
||||
@@ -36,31 +36,28 @@ import { AppleIcon, DockerIcon, FreeBsdIcon, TuxIcon, WindowsIcon } from "./ui/i
|
||||
import { InputCopy } from "./ui/input-copy"
|
||||
|
||||
export function AddSystemButton({ className }: { className?: string }) {
|
||||
if (isReadOnlyUser()) {
|
||||
return null
|
||||
}
|
||||
const [open, setOpen] = useState(false)
|
||||
const opened = useRef(false)
|
||||
if (open) {
|
||||
opened.current = true
|
||||
}
|
||||
if (isReadOnlyUser()) {
|
||||
return null
|
||||
}
|
||||
const [open, setOpen] = useState(false)
|
||||
const opened = useRef(false)
|
||||
if (open) {
|
||||
opened.current = true
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
className={cn("flex gap-1 max-xs:h-[2.4rem]", className)}
|
||||
>
|
||||
<PlusIcon className="h-4 w-4 -ms-1" />
|
||||
<Trans>
|
||||
Add <span className="hidden sm:inline">System</span>
|
||||
</Trans>
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
{opened.current && <SystemDialog setOpen={setOpen} />}
|
||||
</Dialog>
|
||||
)
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogTrigger asChild>
|
||||
<Button variant="outline" className={cn("flex gap-1 max-xs:h-[2.4rem]", className)}>
|
||||
<PlusIcon className="h-4 w-4 -ms-1" />
|
||||
<Trans>
|
||||
Add <span className="hidden sm:inline">System</span>
|
||||
</Trans>
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
{opened.current && <SystemDialog setOpen={setOpen} />}
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -127,6 +124,8 @@ export const SystemDialog = ({ setOpen, system }: { setOpen: (open: boolean) =>
|
||||
}
|
||||
}
|
||||
|
||||
const systemTranslation = t`System`
|
||||
|
||||
return (
|
||||
<DialogContent
|
||||
className="w-[90%] sm:w-auto sm:ns-dialog max-w-full rounded-lg"
|
||||
@@ -137,7 +136,11 @@ export const SystemDialog = ({ setOpen, system }: { setOpen: (open: boolean) =>
|
||||
<Tabs defaultValue={tab} onValueChange={setTab}>
|
||||
<DialogHeader>
|
||||
<DialogTitle className="mb-1 pb-1 max-w-100 truncate pr-8">
|
||||
{system ? `${t`Edit`} ${system?.name}` : <Trans>Add New System</Trans>}
|
||||
{system ? (
|
||||
<Trans>Edit {{ foo: systemTranslation }}</Trans>
|
||||
) : (
|
||||
<Trans>Add {{ foo: systemTranslation }}</Trans>
|
||||
)}
|
||||
</DialogTitle>
|
||||
<TabsList className="grid w-full grid-cols-2">
|
||||
<TabsTrigger value="docker">Docker</TabsTrigger>
|
||||
|
||||
@@ -245,13 +245,23 @@ export function AlertContent({
|
||||
{!singleDescription && (
|
||||
<div>
|
||||
<p id={`v${name}`} className="text-sm block h-8">
|
||||
<Trans>
|
||||
Average exceeds{" "}
|
||||
<strong className="text-foreground">
|
||||
{value}
|
||||
{alertData.unit}
|
||||
</strong>
|
||||
</Trans>
|
||||
{alertData.invert ? (
|
||||
<Trans>
|
||||
Average drops below{" "}
|
||||
<strong className="text-foreground">
|
||||
{value}
|
||||
{alertData.unit}
|
||||
</strong>
|
||||
</Trans>
|
||||
) : (
|
||||
<Trans>
|
||||
Average exceeds{" "}
|
||||
<strong className="text-foreground">
|
||||
{value}
|
||||
{alertData.unit}
|
||||
</strong>
|
||||
</Trans>
|
||||
)}
|
||||
</p>
|
||||
<div className="flex gap-3">
|
||||
<Slider
|
||||
|
||||
@@ -55,8 +55,11 @@ export default function ContainersTable({ systemId }: { systemId?: string }) {
|
||||
filter: systemId ? pb.filter("system={:system}", { system: systemId }) : undefined,
|
||||
})
|
||||
.then(
|
||||
({ items }) =>
|
||||
items.length &&
|
||||
({ items }) => {
|
||||
if (items.length === 0) {
|
||||
setData([]);
|
||||
return;
|
||||
}
|
||||
setData((curItems) => {
|
||||
const lastUpdated = Math.max(items[0].updated, items.at(-1)?.updated ?? 0)
|
||||
const containerIds = new Set()
|
||||
@@ -74,6 +77,7 @@ export default function ContainersTable({ systemId }: { systemId?: string }) {
|
||||
}
|
||||
return newItems
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -333,12 +337,12 @@ function ContainerSheet({
|
||||
setLogsDisplay("")
|
||||
setInfoDisplay("")
|
||||
if (!container) return
|
||||
;(async () => {
|
||||
const [logsHtml, infoHtml] = await Promise.all([getLogsHtml(container), getInfoHtml(container)])
|
||||
setLogsDisplay(logsHtml)
|
||||
setInfoDisplay(infoHtml)
|
||||
setTimeout(scrollLogsToBottom, 20)
|
||||
})()
|
||||
;(async () => {
|
||||
const [logsHtml, infoHtml] = await Promise.all([getLogsHtml(container), getInfoHtml(container)])
|
||||
setLogsDisplay(logsHtml)
|
||||
setInfoDisplay(infoHtml)
|
||||
setTimeout(scrollLogsToBottom, 20)
|
||||
})()
|
||||
}, [container])
|
||||
|
||||
return (
|
||||
|
||||
@@ -18,7 +18,7 @@ export function LangToggle() {
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="grid grid-cols-3">
|
||||
{languages.map(({ lang, label, e }) => (
|
||||
{languages.map(([lang, label, e]) => (
|
||||
<DropdownMenuItem
|
||||
key={lang}
|
||||
className={cn("px-2.5 flex gap-2.5 cursor-pointer", lang === i18n.locale && "bg-accent/70 font-medium")}
|
||||
|
||||
@@ -25,13 +25,13 @@ const passwordSchema = v.pipe(
|
||||
)
|
||||
|
||||
const LoginSchema = v.looseObject({
|
||||
name: honeypot,
|
||||
company_website: honeypot,
|
||||
email: emailSchema,
|
||||
password: passwordSchema,
|
||||
})
|
||||
|
||||
const RegisterSchema = v.looseObject({
|
||||
name: honeypot,
|
||||
company_website: honeypot,
|
||||
email: emailSchema,
|
||||
password: passwordSchema,
|
||||
passwordConfirm: passwordSchema,
|
||||
@@ -248,8 +248,8 @@ export function UserAuthForm({
|
||||
)}
|
||||
<div className="sr-only">
|
||||
{/* honeypot */}
|
||||
<label htmlFor="name"></label>
|
||||
<input id="name" type="text" name="name" tabIndex={-1} autoComplete="off" />
|
||||
<label htmlFor="company_website"></label>
|
||||
<input id="company_website" type="text" name="company_website" tabIndex={-1} autoComplete="off" />
|
||||
</div>
|
||||
<button className={cn(buttonVariants())} disabled={isLoading}>
|
||||
{isLoading ? (
|
||||
@@ -305,9 +305,9 @@ export function UserAuthForm({
|
||||
className="me-2 h-4 w-4 dark:brightness-0 dark:invert"
|
||||
src={getAuthProviderIcon(provider)}
|
||||
alt=""
|
||||
// onError={(e) => {
|
||||
// e.currentTarget.src = "/static/lock.svg"
|
||||
// }}
|
||||
// onError={(e) => {
|
||||
// e.currentTarget.src = "/static/lock.svg"
|
||||
// }}
|
||||
/>
|
||||
)}
|
||||
<span className="translate-y-px">{provider.displayName}</span>
|
||||
|
||||
@@ -68,10 +68,10 @@ export default function SettingsProfilePage({ userSettings }: { userSettings: Us
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{languages.map((lang) => (
|
||||
<SelectItem key={lang.lang} value={lang.lang}>
|
||||
<span className="me-2.5">{lang.e}</span>
|
||||
{lang.label}
|
||||
{languages.map(([lang, label, e]) => (
|
||||
<SelectItem key={lang} value={lang}>
|
||||
<span className="me-2.5">{e}</span>
|
||||
{label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
|
||||
@@ -24,7 +24,13 @@ import {
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog"
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Label } from "@/components/ui/label"
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
|
||||
@@ -36,13 +42,14 @@ import { $systems } from "@/lib/stores"
|
||||
import { formatShortDate } from "@/lib/utils"
|
||||
import type { QuietHoursRecord, SystemRecord } from "@/types"
|
||||
|
||||
const quietHoursTranslation = t`Quiet Hours`
|
||||
|
||||
export function QuietHours() {
|
||||
const [data, setData] = useState<QuietHoursRecord[]>([])
|
||||
const [dialogOpen, setDialogOpen] = useState(false)
|
||||
const [editingRecord, setEditingRecord] = useState<QuietHoursRecord | null>(null)
|
||||
const { toast } = useToast()
|
||||
const systems = useStore($systems)
|
||||
|
||||
useEffect(() => {
|
||||
let unsubscribe: (() => void) | undefined
|
||||
const pbOptions = {
|
||||
@@ -157,9 +164,7 @@ export function QuietHours() {
|
||||
<>
|
||||
<div className="grid grid-cols-1 sm:flex items-center justify-between gap-4 mb-3">
|
||||
<div>
|
||||
<h3 className="mb-1 text-lg font-medium">
|
||||
<Trans>Quiet hours</Trans>
|
||||
</h3>
|
||||
<h3 className="mb-1 text-lg font-medium">{quietHoursTranslation}</h3>
|
||||
<p className="text-sm text-muted-foreground leading-relaxed">
|
||||
<Trans>
|
||||
Schedule quiet hours where notifications will not be sent, such as during maintenance periods.
|
||||
@@ -171,7 +176,7 @@ export function QuietHours() {
|
||||
<Button variant="outline" className="h-10 shrink-0" onClick={() => setEditingRecord(null)}>
|
||||
<PlusIcon className="size-4" />
|
||||
<span className="ms-1">
|
||||
<Trans>Add Quiet Hours</Trans>
|
||||
<Trans>Add {{ foo: quietHoursTranslation }}</Trans>
|
||||
</span>
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
@@ -249,6 +254,7 @@ export function QuietHours() {
|
||||
<PenSquareIcon className="me-2.5 size-4" />
|
||||
<Trans>Edit</Trans>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem onClick={() => handleDelete(record.id)}>
|
||||
<Trash2Icon className="me-2.5 size-4" />
|
||||
<Trans>Delete</Trans>
|
||||
@@ -382,9 +388,15 @@ function QuietHoursDialog({
|
||||
return (
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>{editingRecord ? <Trans>Edit Quiet Hours</Trans> : <Trans>Add Quiet Hours</Trans>}</DialogTitle>
|
||||
<DialogTitle>
|
||||
{editingRecord ? (
|
||||
<Trans>Edit {{ foo: quietHoursTranslation }}</Trans>
|
||||
) : (
|
||||
<Trans>Add {{ foo: quietHoursTranslation }}</Trans>
|
||||
)}
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
<Trans>Configure quiet hours where notifications will not be sent.</Trans>
|
||||
<Trans>Schedule quiet hours where notifications will not be sent.</Trans>
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
@@ -405,7 +417,7 @@ function QuietHoursDialog({
|
||||
</Label>
|
||||
<Select value={selectedSystem} onValueChange={setSelectedSystem}>
|
||||
<SelectTrigger id="system">
|
||||
<SelectValue placeholder={t`Select a system`} />
|
||||
<SelectValue placeholder={t`Select ${{ foo: t`System`.toLocaleLowerCase() }}`} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{systems.map((system) => (
|
||||
|
||||
@@ -32,6 +32,7 @@ import {
|
||||
import { AppleIcon, DockerIcon, FreeBsdIcon, TuxIcon, WindowsIcon } from "@/components/ui/icons"
|
||||
import { Separator } from "@/components/ui/separator"
|
||||
import { Switch } from "@/components/ui/switch"
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
|
||||
import { toast } from "@/components/ui/use-toast"
|
||||
import { isReadOnlyUser, pb } from "@/lib/api"
|
||||
@@ -137,21 +138,23 @@ const SectionUniversalToken = memo(() => {
|
||||
const [token, setToken] = useState("")
|
||||
const [isLoading, setIsLoading] = useState(true)
|
||||
const [checked, setChecked] = useState(false)
|
||||
const [isPermanent, setIsPermanent] = useState(false)
|
||||
|
||||
async function updateToken(enable: number = -1) {
|
||||
async function updateToken(enable: number = -1, permanent: number = -1) {
|
||||
// enable: 0 for disable, 1 for enable, -1 (unset) for get current state
|
||||
const data = await pb.send(`/api/beszel/universal-token`, {
|
||||
query: {
|
||||
token,
|
||||
enable,
|
||||
permanent,
|
||||
},
|
||||
})
|
||||
setToken(data.token)
|
||||
setChecked(data.active)
|
||||
setIsPermanent(!!data.permanent)
|
||||
setIsLoading(false)
|
||||
}
|
||||
|
||||
// biome-ignore lint/correctness/useExhaustiveDependencies: only on mount
|
||||
useEffect(() => {
|
||||
updateToken()
|
||||
}, [])
|
||||
@@ -162,30 +165,64 @@ const SectionUniversalToken = memo(() => {
|
||||
<Trans>Universal token</Trans>
|
||||
</h3>
|
||||
<p className="text-sm text-muted-foreground leading-relaxed">
|
||||
<Trans>
|
||||
When enabled, this token allows agents to self-register without prior system creation. Expires after one hour
|
||||
or on hub restart.
|
||||
</Trans>
|
||||
<Trans>When enabled, this token allows agents to self-register without prior system creation.</Trans>
|
||||
</p>
|
||||
<div className="min-h-16 overflow-auto max-w-full inline-flex items-center gap-5 mt-3 border py-2 ps-5 pe-4 rounded-md">
|
||||
<div className="mt-3 border rounded-md px-4 py-3 max-w-full">
|
||||
{!isLoading && (
|
||||
<>
|
||||
<Switch
|
||||
defaultChecked={checked}
|
||||
onCheckedChange={(checked) => {
|
||||
updateToken(checked ? 1 : 0)
|
||||
}}
|
||||
/>
|
||||
<span
|
||||
className={cn(
|
||||
"text-sm text-primary opacity-60 transition-opacity",
|
||||
checked ? "opacity-100" : "select-none"
|
||||
)}
|
||||
>
|
||||
{token}
|
||||
</span>
|
||||
<ActionsButtonUniversalToken token={token} checked={checked} />
|
||||
</>
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className="flex items-center gap-4 min-w-0">
|
||||
<Switch
|
||||
checked={checked}
|
||||
onCheckedChange={(checked) => {
|
||||
// Keep current permanence preference when enabling/disabling
|
||||
updateToken(checked ? 1 : 0, isPermanent ? 1 : 0)
|
||||
}}
|
||||
/>
|
||||
<div className="min-w-0 flex-1 overflow-auto">
|
||||
<span
|
||||
className={cn(
|
||||
"text-sm text-primary opacity-60 transition-opacity",
|
||||
checked ? "opacity-100" : "select-none"
|
||||
)}
|
||||
>
|
||||
{token}
|
||||
</span>
|
||||
</div>
|
||||
<ActionsButtonUniversalToken token={token} checked={checked} />
|
||||
</div>
|
||||
|
||||
{checked && (
|
||||
<div className="border-t pt-3">
|
||||
<div className="text-sm font-medium">
|
||||
<Trans>Persistence</Trans>
|
||||
</div>
|
||||
<Tabs
|
||||
value={isPermanent ? "permanent" : "ephemeral"}
|
||||
onValueChange={(value) => updateToken(1, value === "permanent" ? 1 : 0)}
|
||||
className="mt-2"
|
||||
>
|
||||
<TabsList>
|
||||
<TabsTrigger className="xs:min-w-40" value="ephemeral">
|
||||
<Trans>Ephemeral</Trans>
|
||||
</TabsTrigger>
|
||||
<TabsTrigger className="xs:min-w-40" value="permanent">
|
||||
<Trans>Permanent</Trans>
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="ephemeral" className="mt-3">
|
||||
<p className="text-sm text-muted-foreground leading-relaxed">
|
||||
<Trans>Expires after one hour or on hub restart.</Trans>
|
||||
</p>
|
||||
</TabsContent>
|
||||
<TabsContent value="permanent" className="mt-3">
|
||||
<p className="text-sm text-muted-foreground leading-relaxed">
|
||||
<Trans>Saved in the database and does not expire until you disable it.</Trans>
|
||||
</p>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,15 +3,7 @@ import { Trans, useLingui } from "@lingui/react/macro"
|
||||
import { useStore } from "@nanostores/react"
|
||||
import { getPagePath } from "@nanostores/router"
|
||||
import { timeTicks } from "d3-time"
|
||||
import {
|
||||
ChevronRightSquareIcon,
|
||||
ClockArrowUp,
|
||||
CpuIcon,
|
||||
GlobeIcon,
|
||||
LayoutGridIcon,
|
||||
MonitorIcon,
|
||||
XIcon,
|
||||
} from "lucide-react"
|
||||
import { XIcon } from "lucide-react"
|
||||
import { subscribeKeys } from "nanostores"
|
||||
import React, { type JSX, lazy, memo, useCallback, useEffect, useMemo, useRef, useState } from "react"
|
||||
import AreaChartDefault, { type DataPoint } from "@/components/charts/area-chart"
|
||||
@@ -24,7 +16,7 @@ import MemChart from "@/components/charts/mem-chart"
|
||||
import SwapChart from "@/components/charts/swap-chart"
|
||||
import TemperatureChart from "@/components/charts/temperature-chart"
|
||||
import { getPbTimestamp, pb } from "@/lib/api"
|
||||
import { ChartType, ConnectionType, connectionTypeLabels, Os, SystemStatus, Unit } from "@/lib/enums"
|
||||
import { ChartType, SystemStatus, Unit } from "@/lib/enums"
|
||||
import { batteryStateTranslations } from "@/lib/i18n"
|
||||
import {
|
||||
$allSystemsById,
|
||||
@@ -44,8 +36,6 @@ import {
|
||||
compareSemVer,
|
||||
decimalString,
|
||||
formatBytes,
|
||||
secondsToString,
|
||||
getHostDisplayValue,
|
||||
listen,
|
||||
parseSemVer,
|
||||
toFixedFloat,
|
||||
@@ -56,25 +46,24 @@ import type {
|
||||
ChartTimes,
|
||||
ContainerStatsRecord,
|
||||
GPUData,
|
||||
SystemDetailsRecord,
|
||||
SystemInfo,
|
||||
SystemRecord,
|
||||
SystemStats,
|
||||
SystemStatsRecord,
|
||||
} from "@/types"
|
||||
import ChartTimeSelect from "../charts/chart-time-select"
|
||||
import { $router, navigate } from "../router"
|
||||
import Spinner from "../spinner"
|
||||
import { Button } from "../ui/button"
|
||||
import { Card, CardDescription, CardHeader, CardTitle } from "../ui/card"
|
||||
import { AppleIcon, ChartAverage, ChartMax, FreeBsdIcon, Rows, TuxIcon, WebSocketIcon, WindowsIcon } from "../ui/icons"
|
||||
import { ChartAverage, ChartMax } from "../ui/icons"
|
||||
import { Input } from "../ui/input"
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../ui/select"
|
||||
import { Separator } from "../ui/separator"
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "../ui/tooltip"
|
||||
import NetworkSheet from "./system/network-sheet"
|
||||
import CpuCoresSheet from "./system/cpu-sheet"
|
||||
import LineChartDefault from "../charts/line-chart"
|
||||
import { pinnedAxisDomain } from "../ui/chart"
|
||||
import InfoBar from "./system/info-bar"
|
||||
|
||||
type ChartTimeData = {
|
||||
time: number
|
||||
@@ -154,8 +143,8 @@ async function getStats<T extends SystemStatsRecord | ContainerStatsRecord>(
|
||||
})
|
||||
}
|
||||
|
||||
function dockerOrPodman(str: string, system: SystemRecord): string {
|
||||
if (system.info.p) {
|
||||
function dockerOrPodman(str: string, isPodman: boolean): string {
|
||||
if (isPodman) {
|
||||
return str.replace("docker", "podman").replace("Docker", "Podman")
|
||||
}
|
||||
return str
|
||||
@@ -178,6 +167,7 @@ export default memo(function SystemDetail({ id }: { id: string }) {
|
||||
const isLongerChart = !["1m", "1h"].includes(chartTime) // true if chart time is not 1m or 1h
|
||||
const userSettings = $userSettings.get()
|
||||
const chartWrapRef = useRef<HTMLDivElement>(null)
|
||||
const [details, setDetails] = useState<SystemDetailsRecord>({} as SystemDetailsRecord)
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
@@ -187,6 +177,7 @@ export default memo(function SystemDetail({ id }: { id: string }) {
|
||||
persistChartTime.current = false
|
||||
setSystemStats([])
|
||||
setContainerData([])
|
||||
setDetails({} as SystemDetailsRecord)
|
||||
$containerFilter.set("")
|
||||
}
|
||||
}, [id])
|
||||
@@ -214,10 +205,25 @@ export default memo(function SystemDetail({ id }: { id: string }) {
|
||||
}
|
||||
}, [system?.info?.v])
|
||||
|
||||
// subscribe to realtime metrics if chart time is 1m
|
||||
// biome-ignore lint/correctness/useExhaustiveDependencies: not necessary
|
||||
// fetch system details
|
||||
useEffect(() => {
|
||||
let unsub = () => { }
|
||||
// if system.info.m exists, agent is old version without system details
|
||||
if (!system.id || system.info?.m) {
|
||||
return
|
||||
}
|
||||
pb.collection<SystemDetailsRecord>("system_details")
|
||||
.getOne(system.id, {
|
||||
fields: "hostname,kernel,cores,threads,cpu,os,os_name,arch,memory,podman",
|
||||
headers: {
|
||||
"Cache-Control": "public, max-age=60",
|
||||
},
|
||||
})
|
||||
.then(setDetails)
|
||||
}, [system.id])
|
||||
|
||||
// subscribe to realtime metrics if chart time is 1m
|
||||
useEffect(() => {
|
||||
let unsub = () => {}
|
||||
if (!system.id || chartTime !== "1m") {
|
||||
return
|
||||
}
|
||||
@@ -253,7 +259,6 @@ export default memo(function SystemDetail({ id }: { id: string }) {
|
||||
}
|
||||
}, [chartTime, system.id])
|
||||
|
||||
// biome-ignore lint/correctness/useExhaustiveDependencies: not necessary
|
||||
const chartData: ChartData = useMemo(() => {
|
||||
const lastCreated = Math.max(
|
||||
(systemStats.at(-1)?.created as number) ?? 0,
|
||||
@@ -293,7 +298,6 @@ export default memo(function SystemDetail({ id }: { id: string }) {
|
||||
}, [])
|
||||
|
||||
// get stats
|
||||
// biome-ignore lint/correctness/useExhaustiveDependencies: not necessary
|
||||
useEffect(() => {
|
||||
if (!system.id || !chartTime || chartTime === "1m") {
|
||||
return
|
||||
@@ -333,63 +337,6 @@ export default memo(function SystemDetail({ id }: { id: string }) {
|
||||
})
|
||||
}, [system, chartTime])
|
||||
|
||||
// values for system info bar
|
||||
const systemInfo = useMemo(() => {
|
||||
if (!system.info) {
|
||||
return []
|
||||
}
|
||||
|
||||
const osInfo = {
|
||||
[Os.Linux]: {
|
||||
Icon: TuxIcon,
|
||||
value: system.info.k,
|
||||
label: t({ comment: "Linux kernel", message: "Kernel" }),
|
||||
},
|
||||
[Os.Darwin]: {
|
||||
Icon: AppleIcon,
|
||||
value: `macOS ${system.info.k}`,
|
||||
},
|
||||
[Os.Windows]: {
|
||||
Icon: WindowsIcon,
|
||||
value: system.info.k,
|
||||
},
|
||||
[Os.FreeBSD]: {
|
||||
Icon: FreeBsdIcon,
|
||||
value: system.info.k,
|
||||
},
|
||||
}
|
||||
let uptime: string
|
||||
if (system.info.u < 3600) {
|
||||
uptime = secondsToString(system.info.u, "minute")
|
||||
} else if (system.info.u < 360000) {
|
||||
uptime = secondsToString(system.info.u, "hour")
|
||||
} else {
|
||||
uptime = secondsToString(system.info.u, "day")
|
||||
}
|
||||
return [
|
||||
{ value: getHostDisplayValue(system), Icon: GlobeIcon },
|
||||
{
|
||||
value: system.info.h,
|
||||
Icon: MonitorIcon,
|
||||
label: "Hostname",
|
||||
// hide if hostname is same as host or name
|
||||
hide: system.info.h === system.host || system.info.h === system.name,
|
||||
},
|
||||
{ value: uptime, Icon: ClockArrowUp, label: t`Uptime`, hide: !system.info.u },
|
||||
osInfo[system.info.os ?? Os.Linux],
|
||||
{
|
||||
value: `${system.info.m} (${system.info.c}c${system.info.t ? `/${system.info.t}t` : ""})`,
|
||||
Icon: CpuIcon,
|
||||
hide: !system.info.m,
|
||||
},
|
||||
] as {
|
||||
value: string | number | undefined
|
||||
label?: string
|
||||
Icon: React.ElementType
|
||||
hide?: boolean
|
||||
}[]
|
||||
}, [system, t])
|
||||
|
||||
/** Space for tooltip if more than 10 sensors and no containers table */
|
||||
useEffect(() => {
|
||||
const sensors = Object.keys(systemStats.at(-1)?.stats.t ?? {})
|
||||
@@ -453,118 +400,42 @@ export default memo(function SystemDetail({ id }: { id: string }) {
|
||||
const containerFilterBar = containerData.length ? <FilterBar /> : null
|
||||
|
||||
const dataEmpty = !chartLoading && chartData.systemStats.length === 0
|
||||
const lastGpuVals = Object.values(systemStats.at(-1)?.stats.g ?? {})
|
||||
const hasGpuData = lastGpuVals.length > 0
|
||||
const hasGpuPowerData = lastGpuVals.some((gpu) => gpu.p !== undefined || gpu.pp !== undefined)
|
||||
const hasGpuEnginesData = lastGpuVals.some((gpu) => gpu.e !== undefined)
|
||||
const lastGpus = systemStats.at(-1)?.stats?.g
|
||||
|
||||
let translatedStatus: string = system.status
|
||||
if (system.status === SystemStatus.Up) {
|
||||
translatedStatus = t({ message: "Up", comment: "Context: System is up" })
|
||||
} else if (system.status === SystemStatus.Down) {
|
||||
translatedStatus = t({ message: "Down", comment: "Context: System is down" })
|
||||
let hasGpuData = false
|
||||
let hasGpuEnginesData = false
|
||||
let hasGpuPowerData = false
|
||||
|
||||
if (lastGpus) {
|
||||
// check if there are any GPUs with engines
|
||||
for (const id in lastGpus) {
|
||||
hasGpuData = true
|
||||
if (lastGpus[id].e !== undefined) {
|
||||
hasGpuEnginesData = true
|
||||
break
|
||||
}
|
||||
}
|
||||
// check if there are any GPUs with power data
|
||||
for (let i = 0; i < systemStats.length && !hasGpuPowerData; i++) {
|
||||
const gpus = systemStats[i].stats?.g
|
||||
if (!gpus) continue
|
||||
for (const id in gpus) {
|
||||
if (gpus[id].p !== undefined || gpus[id].pp !== undefined) {
|
||||
hasGpuPowerData = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const isLinux = !(details?.os ?? system.info?.os)
|
||||
const isPodman = details?.podman ?? system.info?.p ?? false
|
||||
|
||||
return (
|
||||
<>
|
||||
<div ref={chartWrapRef} className="grid gap-4 mb-14 overflow-x-clip">
|
||||
{/* system info */}
|
||||
<Card>
|
||||
<div className="grid xl:flex gap-4 px-4 sm:px-6 pt-3 sm:pt-4 pb-5">
|
||||
<div>
|
||||
<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">
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<div className="capitalize flex gap-2 items-center">
|
||||
<span className={cn("relative flex h-3 w-3")}>
|
||||
{system.status === SystemStatus.Up && (
|
||||
<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>
|
||||
<div className="flex gap-1 items-center">
|
||||
{system.info.ct === ConnectionType.WebSocket ? (
|
||||
<WebSocketIcon className="size-4" />
|
||||
) : (
|
||||
<ChevronRightSquareIcon className="size-4" strokeWidth={2} />
|
||||
)}
|
||||
{connectionTypeLabels[system.info.ct as ConnectionType]}
|
||||
</div>
|
||||
</TooltipContent>
|
||||
)}
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
|
||||
{systemInfo.map(({ value, label, Icon, hide }) => {
|
||||
if (hide || !value) {
|
||||
return null
|
||||
}
|
||||
const content = (
|
||||
<div className="flex gap-1.5 items-center">
|
||||
<Icon className="h-4 w-4" /> {value}
|
||||
</div>
|
||||
)
|
||||
return (
|
||||
<div key={value} className="contents">
|
||||
<Separator orientation="vertical" className="h-4 bg-primary/30" />
|
||||
{label ? (
|
||||
<TooltipProvider>
|
||||
<Tooltip delayDuration={150}>
|
||||
<TooltipTrigger asChild>{content}</TooltipTrigger>
|
||||
<TooltipContent>{label}</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
) : (
|
||||
content
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
<div className="xl:ms-auto flex items-center gap-2 max-sm:-mb-1">
|
||||
<ChartTimeSelect className="w-full xl:w-40" agentVersion={chartData.agentVersion} />
|
||||
<TooltipProvider delayDuration={100}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
aria-label={t`Toggle grid`}
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className="hidden xl:flex p-0 text-primary"
|
||||
onClick={() => setGrid(!grid)}
|
||||
>
|
||||
{grid ? (
|
||||
<LayoutGridIcon className="h-[1.2rem] w-[1.2rem] opacity-75" />
|
||||
) : (
|
||||
<Rows className="h-[1.3rem] w-[1.3rem] opacity-75" />
|
||||
)}
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>{t`Toggle grid`}</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<InfoBar system={system} chartData={chartData} grid={grid} setGrid={setGrid} details={details} />
|
||||
|
||||
{/* <Tabs defaultValue="overview" className="w-full">
|
||||
<TabsList className="w-full h-11">
|
||||
@@ -576,7 +447,6 @@ export default memo(function SystemDetail({ id }: { id: string }) {
|
||||
</TabsContent>
|
||||
</Tabs> */}
|
||||
|
||||
|
||||
{/* main charts */}
|
||||
<div className="grid xl:grid-cols-2 gap-4">
|
||||
<ChartCard
|
||||
@@ -612,7 +482,7 @@ export default memo(function SystemDetail({ id }: { id: string }) {
|
||||
<ChartCard
|
||||
empty={dataEmpty}
|
||||
grid={grid}
|
||||
title={dockerOrPodman(t`Docker CPU Usage`, system)}
|
||||
title={dockerOrPodman(t`Docker CPU Usage`, isPodman)}
|
||||
description={t`Average CPU utilization of containers`}
|
||||
cornerEl={containerFilterBar}
|
||||
>
|
||||
@@ -639,8 +509,8 @@ export default memo(function SystemDetail({ id }: { id: string }) {
|
||||
<ChartCard
|
||||
empty={dataEmpty}
|
||||
grid={grid}
|
||||
title={dockerOrPodman(t`Docker Memory Usage`, system)}
|
||||
description={dockerOrPodman(t`Memory usage of docker containers`, system)}
|
||||
title={dockerOrPodman(t`Docker Memory Usage`, isPodman)}
|
||||
description={dockerOrPodman(t`Memory usage of docker containers`, isPodman)}
|
||||
cornerEl={containerFilterBar}
|
||||
>
|
||||
<ContainerChart
|
||||
@@ -760,8 +630,8 @@ export default memo(function SystemDetail({ id }: { id: string }) {
|
||||
<ChartCard
|
||||
empty={dataEmpty}
|
||||
grid={grid}
|
||||
title={dockerOrPodman(t`Docker Network I/O`, system)}
|
||||
description={dockerOrPodman(t`Network traffic of docker containers`, system)}
|
||||
title={dockerOrPodman(t`Docker Network I/O`, isPodman)}
|
||||
description={dockerOrPodman(t`Network traffic of docker containers`, isPodman)}
|
||||
cornerEl={containerFilterBar}
|
||||
>
|
||||
<ContainerChart
|
||||
@@ -800,10 +670,7 @@ export default memo(function SystemDetail({ id }: { id: string }) {
|
||||
|
||||
{/* Temperature chart */}
|
||||
{systemStats.at(-1)?.stats.t && (
|
||||
<div
|
||||
ref={temperatureChartRef}
|
||||
className={cn("odd:last-of-type:col-span-full", { "col-span-full": !grid })}
|
||||
>
|
||||
<div ref={temperatureChartRef} className={cn("odd:last-of-type:col-span-full", { "col-span-full": !grid })}>
|
||||
<ChartCard
|
||||
empty={dataEmpty}
|
||||
grid={grid}
|
||||
@@ -872,64 +739,65 @@ export default memo(function SystemDetail({ id }: { id: string }) {
|
||||
<GpuEnginesChart chartData={chartData} />
|
||||
</ChartCard>
|
||||
)}
|
||||
{Object.keys(systemStats.at(-1)?.stats.g ?? {}).map((id) => {
|
||||
const gpu = systemStats.at(-1)?.stats.g?.[id] as GPUData
|
||||
return (
|
||||
<div key={id} className="contents">
|
||||
<ChartCard
|
||||
className={cn(grid && "!col-span-1")}
|
||||
empty={dataEmpty}
|
||||
grid={grid}
|
||||
title={`${gpu.n} ${t`Usage`}`}
|
||||
description={t`Average utilization of ${gpu.n}`}
|
||||
>
|
||||
<AreaChartDefault
|
||||
chartData={chartData}
|
||||
dataPoints={[
|
||||
{
|
||||
label: t`Usage`,
|
||||
dataKey: ({ stats }) => stats?.g?.[id]?.u ?? 0,
|
||||
color: 1,
|
||||
opacity: 0.35,
|
||||
},
|
||||
]}
|
||||
tickFormatter={(val) => `${toFixedFloat(val, 2)}%`}
|
||||
contentFormatter={({ value }) => `${decimalString(value)}%`}
|
||||
/>
|
||||
</ChartCard>
|
||||
|
||||
{(gpu.mt ?? 0) > 0 && (
|
||||
{lastGpus &&
|
||||
Object.keys(lastGpus).map((id) => {
|
||||
const gpu = lastGpus[id] as GPUData
|
||||
return (
|
||||
<div key={id} className="contents">
|
||||
<ChartCard
|
||||
className={cn(grid && "!col-span-1")}
|
||||
empty={dataEmpty}
|
||||
grid={grid}
|
||||
title={`${gpu.n} VRAM`}
|
||||
description={t`Precise utilization at the recorded time`}
|
||||
title={`${gpu.n} ${t`Usage`}`}
|
||||
description={t`Average utilization of ${gpu.n}`}
|
||||
>
|
||||
<AreaChartDefault
|
||||
chartData={chartData}
|
||||
dataPoints={[
|
||||
{
|
||||
label: t`Usage`,
|
||||
dataKey: ({ stats }) => stats?.g?.[id]?.mu ?? 0,
|
||||
color: 2,
|
||||
opacity: 0.25,
|
||||
dataKey: ({ stats }) => stats?.g?.[id]?.u ?? 0,
|
||||
color: 1,
|
||||
opacity: 0.35,
|
||||
},
|
||||
]}
|
||||
max={gpu.mt}
|
||||
tickFormatter={(val) => {
|
||||
const { value, unit } = formatBytes(val, false, Unit.Bytes, true)
|
||||
return `${toFixedFloat(value, value >= 10 ? 0 : 1)} ${unit}`
|
||||
}}
|
||||
contentFormatter={({ value }) => {
|
||||
const { value: convertedValue, unit } = formatBytes(value, false, Unit.Bytes, true)
|
||||
return `${decimalString(convertedValue)} ${unit}`
|
||||
}}
|
||||
tickFormatter={(val) => `${toFixedFloat(val, 2)}%`}
|
||||
contentFormatter={({ value }) => `${decimalString(value)}%`}
|
||||
/>
|
||||
</ChartCard>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
|
||||
{(gpu.mt ?? 0) > 0 && (
|
||||
<ChartCard
|
||||
empty={dataEmpty}
|
||||
grid={grid}
|
||||
title={`${gpu.n} VRAM`}
|
||||
description={t`Precise utilization at the recorded time`}
|
||||
>
|
||||
<AreaChartDefault
|
||||
chartData={chartData}
|
||||
dataPoints={[
|
||||
{
|
||||
label: t`Usage`,
|
||||
dataKey: ({ stats }) => stats?.g?.[id]?.mu ?? 0,
|
||||
color: 2,
|
||||
opacity: 0.25,
|
||||
},
|
||||
]}
|
||||
max={gpu.mt}
|
||||
tickFormatter={(val) => {
|
||||
const { value, unit } = formatBytes(val, false, Unit.Bytes, true)
|
||||
return `${toFixedFloat(value, value >= 10 ? 0 : 1)} ${unit}`
|
||||
}}
|
||||
contentFormatter={({ value }) => {
|
||||
const { value: convertedValue, unit } = formatBytes(value, false, Unit.Bytes, true)
|
||||
return `${decimalString(convertedValue)} ${unit}`
|
||||
}}
|
||||
/>
|
||||
</ChartCard>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -965,7 +833,9 @@ export default memo(function SystemDetail({ id }: { id: string }) {
|
||||
label: t`Write`,
|
||||
dataKey: ({ stats }) => {
|
||||
if (showMax) {
|
||||
return stats?.efs?.[extraFsName]?.wbm || (stats?.efs?.[extraFsName]?.wm ?? 0) * 1024 * 1024
|
||||
return (
|
||||
stats?.efs?.[extraFsName]?.wbm || (stats?.efs?.[extraFsName]?.wm ?? 0) * 1024 * 1024
|
||||
)
|
||||
}
|
||||
return stats?.efs?.[extraFsName]?.wb || (stats?.efs?.[extraFsName]?.w ?? 0) * 1024 * 1024
|
||||
},
|
||||
@@ -1003,15 +873,13 @@ export default memo(function SystemDetail({ id }: { id: string }) {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{compareSemVer(chartData.agentVersion, parseSemVer("0.15.0")) >= 0 && (
|
||||
<LazySmartTable systemId={system.id} />
|
||||
)}
|
||||
{compareSemVer(chartData.agentVersion, parseSemVer("0.15.0")) >= 0 && <LazySmartTable systemId={system.id} />}
|
||||
|
||||
{containerData.length > 0 && compareSemVer(chartData.agentVersion, parseSemVer("0.14.0")) >= 0 && (
|
||||
<LazyContainersTable systemId={system.id} />
|
||||
)}
|
||||
|
||||
{system.info?.os === Os.Linux && compareSemVer(chartData.agentVersion, parseSemVer("0.16.0")) >= 0 && (
|
||||
{isLinux && compareSemVer(chartData.agentVersion, parseSemVer("0.16.0")) >= 0 && (
|
||||
<LazySystemdTable systemId={system.id} />
|
||||
)}
|
||||
</div>
|
||||
@@ -1061,13 +929,10 @@ function FilterBar({ store = $containerFilter }: { store?: typeof $containerFilt
|
||||
return () => clearTimeout(handle)
|
||||
}, [inputValue, storeValue, store])
|
||||
|
||||
const handleChange = useCallback(
|
||||
(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const value = e.target.value
|
||||
setInputValue(value)
|
||||
},
|
||||
[]
|
||||
)
|
||||
const handleChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const value = e.target.value
|
||||
setInputValue(value)
|
||||
}, [])
|
||||
|
||||
const handleClear = useCallback(() => {
|
||||
setInputValue("")
|
||||
@@ -1194,4 +1059,4 @@ function LazySystemdTable({ systemId }: { systemId: string }) {
|
||||
{isIntersecting && <SystemdTable systemId={systemId} />}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
229
internal/site/src/components/routes/system/info-bar.tsx
Normal file
229
internal/site/src/components/routes/system/info-bar.tsx
Normal file
@@ -0,0 +1,229 @@
|
||||
import { plural } from "@lingui/core/macro"
|
||||
import { useLingui } from "@lingui/react/macro"
|
||||
import {
|
||||
AppleIcon,
|
||||
ChevronRightSquareIcon,
|
||||
ClockArrowUp,
|
||||
CpuIcon,
|
||||
GlobeIcon,
|
||||
LayoutGridIcon,
|
||||
MemoryStickIcon,
|
||||
MonitorIcon,
|
||||
Rows,
|
||||
} from "lucide-react"
|
||||
import { useMemo } from "react"
|
||||
import ChartTimeSelect from "@/components/charts/chart-time-select"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Card } from "@/components/ui/card"
|
||||
import { FreeBsdIcon, TuxIcon, WebSocketIcon, WindowsIcon } from "@/components/ui/icons"
|
||||
import { Separator } from "@/components/ui/separator"
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"
|
||||
import { ConnectionType, connectionTypeLabels, Os, SystemStatus } from "@/lib/enums"
|
||||
import { cn, formatBytes, getHostDisplayValue, secondsToString, toFixedFloat } from "@/lib/utils"
|
||||
import type { ChartData, SystemDetailsRecord, SystemRecord } from "@/types"
|
||||
|
||||
export default function InfoBar({
|
||||
system,
|
||||
chartData,
|
||||
grid,
|
||||
setGrid,
|
||||
details,
|
||||
}: {
|
||||
system: SystemRecord
|
||||
chartData: ChartData
|
||||
grid: boolean
|
||||
setGrid: (grid: boolean) => void
|
||||
details: SystemDetailsRecord | null
|
||||
}) {
|
||||
const { t } = useLingui()
|
||||
|
||||
// values for system info bar - use details with fallback to system.info
|
||||
const systemInfo = useMemo(() => {
|
||||
if (!system.info) {
|
||||
return []
|
||||
}
|
||||
|
||||
// Use details if available, otherwise fall back to system.info
|
||||
const hostname = details?.hostname ?? system.info.h
|
||||
const kernel = details?.kernel ?? system.info.k
|
||||
const cores = details?.cores ?? system.info.c
|
||||
const threads = details?.threads ?? system.info.t ?? 0
|
||||
const cpuModel = details?.cpu ?? system.info.m
|
||||
const os = details?.os ?? system.info.os ?? Os.Linux
|
||||
const osName = details?.os_name
|
||||
const arch = details?.arch
|
||||
const memory = details?.memory
|
||||
|
||||
const osInfo = {
|
||||
[Os.Linux]: {
|
||||
Icon: TuxIcon,
|
||||
// show kernel in tooltip if os name is available, otherwise show the kernel
|
||||
value: osName || kernel,
|
||||
label: osName ? kernel : undefined,
|
||||
},
|
||||
[Os.Darwin]: {
|
||||
Icon: AppleIcon,
|
||||
value: osName || `macOS ${kernel}`,
|
||||
},
|
||||
[Os.Windows]: {
|
||||
Icon: WindowsIcon,
|
||||
value: osName || kernel,
|
||||
label: osName ? kernel : undefined,
|
||||
},
|
||||
[Os.FreeBSD]: {
|
||||
Icon: FreeBsdIcon,
|
||||
value: osName || kernel,
|
||||
label: osName ? kernel : undefined,
|
||||
},
|
||||
}
|
||||
|
||||
let uptime: string
|
||||
if (system.info.u < 3600) {
|
||||
uptime = secondsToString(system.info.u, "minute")
|
||||
} else if (system.info.u < 360000) {
|
||||
uptime = secondsToString(system.info.u, "hour")
|
||||
} else {
|
||||
uptime = secondsToString(system.info.u, "day")
|
||||
}
|
||||
const info = [
|
||||
{ value: getHostDisplayValue(system), Icon: GlobeIcon },
|
||||
{
|
||||
value: hostname,
|
||||
Icon: MonitorIcon,
|
||||
label: "Hostname",
|
||||
// hide if hostname is same as host or name
|
||||
hide: hostname === system.host || hostname === system.name,
|
||||
},
|
||||
{ value: uptime, Icon: ClockArrowUp, label: t`Uptime`, hide: !system.info.u },
|
||||
osInfo[os],
|
||||
{
|
||||
value: cpuModel,
|
||||
Icon: CpuIcon,
|
||||
hide: !cpuModel,
|
||||
label: `${plural(cores, { one: "# core", other: "# cores" })} / ${plural(threads, { one: "# thread", other: "# threads" })}${arch ? ` / ${arch}` : ""}`,
|
||||
},
|
||||
] as {
|
||||
value: string | number | undefined
|
||||
label?: string
|
||||
Icon: React.ElementType
|
||||
hide?: boolean
|
||||
}[]
|
||||
|
||||
if (memory) {
|
||||
const memValue = formatBytes(memory, false, undefined, false)
|
||||
info.push({
|
||||
value: `${toFixedFloat(memValue.value, memValue.value >= 10 ? 1 : 2)} ${memValue.unit}`,
|
||||
Icon: MemoryStickIcon,
|
||||
hide: !memory,
|
||||
label: t`Memory`,
|
||||
})
|
||||
}
|
||||
|
||||
return info
|
||||
}, [system, details, t])
|
||||
|
||||
let translatedStatus: string = system.status
|
||||
if (system.status === SystemStatus.Up) {
|
||||
translatedStatus = t({ message: "Up", comment: "Context: System is up" })
|
||||
} else if (system.status === SystemStatus.Down) {
|
||||
translatedStatus = t({ message: "Down", comment: "Context: System is down" })
|
||||
}
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<div className="grid xl:flex gap-4 px-4 sm:px-6 pt-3 sm:pt-4 pb-5">
|
||||
<div>
|
||||
<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">
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<div className="capitalize flex gap-2 items-center">
|
||||
<span className={cn("relative flex h-3 w-3")}>
|
||||
{system.status === SystemStatus.Up && (
|
||||
<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>
|
||||
<div className="flex gap-1 items-center">
|
||||
{system.info.ct === ConnectionType.WebSocket ? (
|
||||
<WebSocketIcon className="size-4" />
|
||||
) : (
|
||||
<ChevronRightSquareIcon className="size-4" strokeWidth={2} />
|
||||
)}
|
||||
{connectionTypeLabels[system.info.ct as ConnectionType]}
|
||||
</div>
|
||||
</TooltipContent>
|
||||
)}
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
|
||||
{systemInfo.map(({ value, label, Icon, hide }) => {
|
||||
if (hide || !value) {
|
||||
return null
|
||||
}
|
||||
const content = (
|
||||
<div className="flex gap-1.5 items-center">
|
||||
<Icon className="h-4 w-4" /> {value}
|
||||
</div>
|
||||
)
|
||||
return (
|
||||
<div key={value} className="contents">
|
||||
<Separator orientation="vertical" className="h-4 bg-primary/30" />
|
||||
{label ? (
|
||||
<TooltipProvider>
|
||||
<Tooltip delayDuration={150}>
|
||||
<TooltipTrigger asChild>{content}</TooltipTrigger>
|
||||
<TooltipContent>{label}</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
) : (
|
||||
content
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
<div className="xl:ms-auto flex items-center gap-2 max-sm:-mb-1">
|
||||
<ChartTimeSelect className="w-full xl:w-40" agentVersion={chartData.agentVersion} />
|
||||
<TooltipProvider delayDuration={100}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
aria-label={t`Toggle grid`}
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className="hidden xl:flex p-0 text-primary"
|
||||
onClick={() => setGrid(!grid)}
|
||||
>
|
||||
{grid ? (
|
||||
<LayoutGridIcon className="h-[1.2rem] w-[1.2rem] opacity-75" />
|
||||
) : (
|
||||
<Rows className="h-[1.3rem] w-[1.3rem] opacity-75" />
|
||||
)}
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>{t`Toggle grid`}</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
@@ -93,51 +93,15 @@ export const smartColumns: ColumnDef<SmartAttribute>[] = [
|
||||
},
|
||||
]
|
||||
|
||||
export type DiskInfo = {
|
||||
id: string
|
||||
system: string
|
||||
device: string
|
||||
model: string
|
||||
capacity: string
|
||||
status: string
|
||||
temperature: number
|
||||
deviceType: string
|
||||
powerOnHours?: number
|
||||
powerCycles?: number
|
||||
attributes?: SmartAttribute[]
|
||||
updated: string
|
||||
}
|
||||
|
||||
// Function to format capacity display
|
||||
function formatCapacity(bytes: number): string {
|
||||
const { value, unit } = formatBytes(bytes)
|
||||
return `${toFixedFloat(value, value >= 10 ? 1 : 2)} ${unit}`
|
||||
}
|
||||
|
||||
// Function to convert SmartDeviceRecord to DiskInfo
|
||||
function convertSmartDeviceRecordToDiskInfo(records: SmartDeviceRecord[]): DiskInfo[] {
|
||||
const unknown = "Unknown"
|
||||
return records.map((record) => ({
|
||||
id: record.id,
|
||||
system: record.system,
|
||||
device: record.name || unknown,
|
||||
model: record.model || unknown,
|
||||
serialNumber: record.serial || unknown,
|
||||
firmwareVersion: record.firmware || unknown,
|
||||
capacity: record.capacity ? formatCapacity(record.capacity) : unknown,
|
||||
status: record.state || unknown,
|
||||
temperature: record.temp || 0,
|
||||
deviceType: record.type || unknown,
|
||||
attributes: record.attributes,
|
||||
updated: record.updated,
|
||||
powerOnHours: record.hours,
|
||||
powerCycles: record.cycles,
|
||||
}))
|
||||
}
|
||||
|
||||
const SMART_DEVICE_FIELDS = "id,system,name,model,state,capacity,temp,type,hours,cycles,updated"
|
||||
|
||||
export const columns: ColumnDef<DiskInfo>[] = [
|
||||
export const columns: ColumnDef<SmartDeviceRecord>[] = [
|
||||
{
|
||||
id: "system",
|
||||
accessorFn: (record) => record.system,
|
||||
@@ -154,12 +118,12 @@ export const columns: ColumnDef<DiskInfo>[] = [
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "device",
|
||||
sortingFn: (a, b) => a.original.device.localeCompare(b.original.device),
|
||||
accessorKey: "name",
|
||||
sortingFn: (a, b) => a.original.name.localeCompare(b.original.name),
|
||||
header: ({ column }) => <HeaderButton column={column} name={t`Device`} Icon={HardDrive} />,
|
||||
cell: ({ row }) => (
|
||||
<div className="font-medium max-w-40 truncate ms-1.5" title={row.getValue("device")}>
|
||||
{row.getValue("device")}
|
||||
cell: ({ getValue }) => (
|
||||
<div className="font-medium max-w-40 truncate ms-1.5" title={getValue() as string}>
|
||||
{getValue() as string}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
@@ -167,19 +131,20 @@ export const columns: ColumnDef<DiskInfo>[] = [
|
||||
accessorKey: "model",
|
||||
sortingFn: (a, b) => a.original.model.localeCompare(b.original.model),
|
||||
header: ({ column }) => <HeaderButton column={column} name={t`Model`} Icon={Box} />,
|
||||
cell: ({ row }) => (
|
||||
<div className="max-w-48 truncate ms-1.5" title={row.getValue("model")}>
|
||||
{row.getValue("model")}
|
||||
cell: ({ getValue }) => (
|
||||
<div className="max-w-48 truncate ms-1.5" title={getValue() as string}>
|
||||
{getValue() as string}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: "capacity",
|
||||
invertSorting: true,
|
||||
header: ({ column }) => <HeaderButton column={column} name={t`Capacity`} Icon={BinaryIcon} />,
|
||||
cell: ({ getValue }) => <span className="ms-1.5">{getValue() as string}</span>,
|
||||
cell: ({ getValue }) => <span className="ms-1.5">{formatCapacity(getValue() as number)}</span>,
|
||||
},
|
||||
{
|
||||
accessorKey: "status",
|
||||
accessorKey: "state",
|
||||
header: ({ column }) => <HeaderButton column={column} name={t`Status`} Icon={Activity} />,
|
||||
cell: ({ getValue }) => {
|
||||
const status = getValue() as string
|
||||
@@ -191,8 +156,8 @@ export const columns: ColumnDef<DiskInfo>[] = [
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "deviceType",
|
||||
sortingFn: (a, b) => a.original.deviceType.localeCompare(b.original.deviceType),
|
||||
accessorKey: "type",
|
||||
sortingFn: (a, b) => a.original.type.localeCompare(b.original.type),
|
||||
header: ({ column }) => <HeaderButton column={column} name={t`Type`} Icon={ArrowLeftRightIcon} />,
|
||||
cell: ({ getValue }) => (
|
||||
<div className="ms-1.5">
|
||||
@@ -203,7 +168,7 @@ export const columns: ColumnDef<DiskInfo>[] = [
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: "powerOnHours",
|
||||
accessorKey: "hours",
|
||||
invertSorting: true,
|
||||
header: ({ column }) => (
|
||||
<HeaderButton column={column} name={t({ message: "Power On", comment: "Power On Time" })} Icon={Clock} />
|
||||
@@ -223,7 +188,7 @@ export const columns: ColumnDef<DiskInfo>[] = [
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "powerCycles",
|
||||
accessorKey: "cycles",
|
||||
invertSorting: true,
|
||||
header: ({ column }) => (
|
||||
<HeaderButton column={column} name={t({ message: "Cycles", comment: "Power Cycles" })} Icon={RotateCwIcon} />
|
||||
@@ -233,11 +198,11 @@ export const columns: ColumnDef<DiskInfo>[] = [
|
||||
if (!cycles && cycles !== 0) {
|
||||
return <div className="text-muted-foreground ms-1.5">N/A</div>
|
||||
}
|
||||
return <span className="ms-1.5">{cycles}</span>
|
||||
return <span className="ms-1.5">{cycles.toLocaleString()}</span>
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "temperature",
|
||||
accessorKey: "temp",
|
||||
invertSorting: true,
|
||||
header: ({ column }) => <HeaderButton column={column} name={t`Temp`} Icon={ThermometerIcon} />,
|
||||
cell: ({ getValue }) => {
|
||||
@@ -246,14 +211,14 @@ export const columns: ColumnDef<DiskInfo>[] = [
|
||||
},
|
||||
},
|
||||
// {
|
||||
// accessorKey: "serialNumber",
|
||||
// sortingFn: (a, b) => a.original.serialNumber.localeCompare(b.original.serialNumber),
|
||||
// accessorKey: "serial",
|
||||
// sortingFn: (a, b) => a.original.serial.localeCompare(b.original.serial),
|
||||
// header: ({ column }) => <HeaderButton column={column} name={t`Serial Number`} Icon={HashIcon} />,
|
||||
// cell: ({ getValue }) => <span className="ms-1.5">{getValue() as string}</span>,
|
||||
// },
|
||||
// {
|
||||
// accessorKey: "firmwareVersion",
|
||||
// sortingFn: (a, b) => a.original.firmwareVersion.localeCompare(b.original.firmwareVersion),
|
||||
// accessorKey: "firmware",
|
||||
// sortingFn: (a, b) => a.original.firmware.localeCompare(b.original.firmware),
|
||||
// header: ({ column }) => <HeaderButton column={column} name={t`Firmware`} Icon={CpuIcon} />,
|
||||
// cell: ({ getValue }) => <span className="ms-1.5">{getValue() as string}</span>,
|
||||
// },
|
||||
@@ -272,7 +237,15 @@ export const columns: ColumnDef<DiskInfo>[] = [
|
||||
},
|
||||
]
|
||||
|
||||
function HeaderButton({ column, name, Icon }: { column: Column<DiskInfo>; name: string; Icon: React.ElementType }) {
|
||||
function HeaderButton({
|
||||
column,
|
||||
name,
|
||||
Icon,
|
||||
}: {
|
||||
column: Column<SmartDeviceRecord>
|
||||
name: string
|
||||
Icon: React.ElementType
|
||||
}) {
|
||||
const isSorted = column.getIsSorted()
|
||||
return (
|
||||
<Button
|
||||
@@ -290,7 +263,7 @@ function HeaderButton({ column, name, Icon }: { column: Column<DiskInfo>; name:
|
||||
}
|
||||
|
||||
export default function DisksTable({ systemId }: { systemId?: string }) {
|
||||
const [sorting, setSorting] = useState<SortingState>([{ id: systemId ? "device" : "system", desc: false }])
|
||||
const [sorting, setSorting] = useState<SortingState>([{ id: systemId ? "name" : "system", desc: false }])
|
||||
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([])
|
||||
const [rowSelection, setRowSelection] = useState({})
|
||||
const [smartDevices, setSmartDevices] = useState<SmartDeviceRecord[] | undefined>(undefined)
|
||||
@@ -299,96 +272,95 @@ export default function DisksTable({ systemId }: { systemId?: string }) {
|
||||
const [rowActionState, setRowActionState] = useState<{ type: "refresh" | "delete"; id: string } | null>(null)
|
||||
const [globalFilter, setGlobalFilter] = useState("")
|
||||
|
||||
const openSheet = (disk: DiskInfo) => {
|
||||
const openSheet = (disk: SmartDeviceRecord) => {
|
||||
setActiveDiskId(disk.id)
|
||||
setSheetOpen(true)
|
||||
}
|
||||
|
||||
// Fetch smart devices from collection (without attributes to save bandwidth)
|
||||
const fetchSmartDevices = useCallback(() => {
|
||||
// Fetch smart devices
|
||||
useEffect(() => {
|
||||
const controller = new AbortController()
|
||||
|
||||
pb.collection<SmartDeviceRecord>("smart_devices")
|
||||
.getFullList({
|
||||
filter: systemId ? pb.filter("system = {:system}", { system: systemId }) : undefined,
|
||||
fields: SMART_DEVICE_FIELDS,
|
||||
signal: controller.signal,
|
||||
})
|
||||
.then((records) => {
|
||||
setSmartDevices(records)
|
||||
.then(setSmartDevices)
|
||||
.catch((err) => {
|
||||
if (!err.isAbort) {
|
||||
setSmartDevices([])
|
||||
}
|
||||
})
|
||||
.catch(() => setSmartDevices([]))
|
||||
|
||||
return () => controller.abort()
|
||||
}, [systemId])
|
||||
|
||||
// Fetch smart devices when component mounts or systemId changes
|
||||
useEffect(() => {
|
||||
fetchSmartDevices()
|
||||
}, [fetchSmartDevices])
|
||||
|
||||
// Subscribe to live updates so rows add/remove without manual refresh/filtering
|
||||
// Subscribe to updates
|
||||
useEffect(() => {
|
||||
let unsubscribe: (() => void) | undefined
|
||||
const pbOptions = systemId
|
||||
? { fields: SMART_DEVICE_FIELDS, filter: pb.filter("system = {:system}", { system: systemId }) }
|
||||
: { fields: SMART_DEVICE_FIELDS }
|
||||
|
||||
;(async () => {
|
||||
try {
|
||||
unsubscribe = await pb.collection("smart_devices").subscribe(
|
||||
"*",
|
||||
(event) => {
|
||||
const record = event.record as SmartDeviceRecord
|
||||
setSmartDevices((currentDevices) => {
|
||||
const devices = currentDevices ?? []
|
||||
const matchesSystemScope = !systemId || record.system === systemId
|
||||
; (async () => {
|
||||
try {
|
||||
unsubscribe = await pb.collection("smart_devices").subscribe(
|
||||
"*",
|
||||
(event) => {
|
||||
const record = event.record as SmartDeviceRecord
|
||||
setSmartDevices((currentDevices) => {
|
||||
const devices = currentDevices ?? []
|
||||
const matchesSystemScope = !systemId || record.system === systemId
|
||||
|
||||
if (event.action === "delete") {
|
||||
return devices.filter((device) => device.id !== record.id)
|
||||
}
|
||||
if (event.action === "delete") {
|
||||
return devices.filter((device) => device.id !== record.id)
|
||||
}
|
||||
|
||||
if (!matchesSystemScope) {
|
||||
// Record moved out of scope; ensure it disappears locally.
|
||||
return devices.filter((device) => device.id !== record.id)
|
||||
}
|
||||
if (!matchesSystemScope) {
|
||||
// Record moved out of scope; ensure it disappears locally.
|
||||
return devices.filter((device) => device.id !== record.id)
|
||||
}
|
||||
|
||||
const existingIndex = devices.findIndex((device) => device.id === record.id)
|
||||
if (existingIndex === -1) {
|
||||
return [record, ...devices]
|
||||
}
|
||||
const existingIndex = devices.findIndex((device) => device.id === record.id)
|
||||
if (existingIndex === -1) {
|
||||
return [record, ...devices]
|
||||
}
|
||||
|
||||
const next = [...devices]
|
||||
next[existingIndex] = record
|
||||
return next
|
||||
})
|
||||
},
|
||||
pbOptions
|
||||
)
|
||||
} catch (error) {
|
||||
console.error("Failed to subscribe to SMART device updates:", error)
|
||||
}
|
||||
})()
|
||||
const next = [...devices]
|
||||
next[existingIndex] = record
|
||||
return next
|
||||
})
|
||||
},
|
||||
pbOptions
|
||||
)
|
||||
} catch (error) {
|
||||
console.error("Failed to subscribe to SMART device updates:", error)
|
||||
}
|
||||
})()
|
||||
|
||||
return () => {
|
||||
unsubscribe?.()
|
||||
}
|
||||
}, [systemId])
|
||||
|
||||
const handleRowRefresh = useCallback(
|
||||
async (disk: DiskInfo) => {
|
||||
if (!disk.system) return
|
||||
setRowActionState({ type: "refresh", id: disk.id })
|
||||
try {
|
||||
await pb.send("/api/beszel/smart/refresh", {
|
||||
method: "POST",
|
||||
query: { system: disk.system },
|
||||
})
|
||||
} catch (error) {
|
||||
console.error("Failed to refresh SMART device:", error)
|
||||
} finally {
|
||||
setRowActionState((state) => (state?.id === disk.id ? null : state))
|
||||
}
|
||||
},
|
||||
[fetchSmartDevices]
|
||||
)
|
||||
const handleRowRefresh = useCallback(async (disk: SmartDeviceRecord) => {
|
||||
if (!disk.system) return
|
||||
setRowActionState({ type: "refresh", id: disk.id })
|
||||
try {
|
||||
await pb.send("/api/beszel/smart/refresh", {
|
||||
method: "POST",
|
||||
query: { system: disk.system },
|
||||
})
|
||||
} catch (error) {
|
||||
console.error("Failed to refresh SMART device:", error)
|
||||
} finally {
|
||||
setRowActionState((state) => (state?.id === disk.id ? null : state))
|
||||
}
|
||||
}, [])
|
||||
|
||||
const handleDeleteDevice = useCallback(async (disk: DiskInfo) => {
|
||||
const handleDeleteDevice = useCallback(async (disk: SmartDeviceRecord) => {
|
||||
setRowActionState({ type: "delete", id: disk.id })
|
||||
try {
|
||||
await pb.collection("smart_devices").delete(disk.id)
|
||||
@@ -400,7 +372,7 @@ export default function DisksTable({ systemId }: { systemId?: string }) {
|
||||
}
|
||||
}, [])
|
||||
|
||||
const actionColumn = useMemo<ColumnDef<DiskInfo>>(
|
||||
const actionColumn = useMemo<ColumnDef<SmartDeviceRecord>>(
|
||||
() => ({
|
||||
id: "actions",
|
||||
enableSorting: false,
|
||||
@@ -421,14 +393,14 @@ export default function DisksTable({ systemId }: { systemId?: string }) {
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="size-8"
|
||||
className="size-10"
|
||||
onClick={(event) => event.stopPropagation()}
|
||||
onMouseDown={(event) => event.stopPropagation()}
|
||||
>
|
||||
<span className="sr-only">
|
||||
<Trans>Open menu</Trans>
|
||||
</span>
|
||||
<MoreHorizontalIcon className="size-4" />
|
||||
<MoreHorizontalIcon className="w-5" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" onClick={(event) => event.stopPropagation()}>
|
||||
@@ -468,13 +440,8 @@ export default function DisksTable({ systemId }: { systemId?: string }) {
|
||||
return [...baseColumns, actionColumn]
|
||||
}, [systemId, actionColumn])
|
||||
|
||||
// Convert SmartDeviceRecord to DiskInfo
|
||||
const diskData = useMemo(() => {
|
||||
return smartDevices ? convertSmartDeviceRecordToDiskInfo(smartDevices) : []
|
||||
}, [smartDevices])
|
||||
|
||||
const table = useReactTable({
|
||||
data: diskData,
|
||||
data: smartDevices || ([] as SmartDeviceRecord[]),
|
||||
columns: tableColumns,
|
||||
onSortingChange: setSorting,
|
||||
onColumnFiltersChange: setColumnFilters,
|
||||
@@ -492,10 +459,10 @@ export default function DisksTable({ systemId }: { systemId?: string }) {
|
||||
globalFilterFn: (row, _columnId, filterValue) => {
|
||||
const disk = row.original
|
||||
const systemName = $allSystemsById.get()[disk.system]?.name ?? ""
|
||||
const device = disk.device ?? ""
|
||||
const device = disk.name ?? ""
|
||||
const model = disk.model ?? ""
|
||||
const status = disk.status ?? ""
|
||||
const type = disk.deviceType ?? ""
|
||||
const status = disk.state ?? ""
|
||||
const type = disk.type ?? ""
|
||||
const searchString = `${systemName} ${device} ${model} ${status} ${type}`.toLowerCase()
|
||||
return (filterValue as string)
|
||||
.toLowerCase()
|
||||
@@ -505,7 +472,7 @@ export default function DisksTable({ systemId }: { systemId?: string }) {
|
||||
})
|
||||
|
||||
// Hide the table on system pages if there's no data, but always show on global page
|
||||
if (systemId && !diskData.length && !columnFilters.length) {
|
||||
if (systemId && !smartDevices?.length && !columnFilters.length) {
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -680,7 +647,7 @@ function DiskSheet({
|
||||
<span>{firmwareVersion}</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<Trans>Firmware Version</Trans>
|
||||
<Trans>Firmware</Trans>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</SheetDescription>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/** biome-ignore-all lint/correctness/useHookAtTopLevel: <explanation> */
|
||||
/** biome-ignore-all lint/correctness/useHookAtTopLevel: Hooks live inside memoized column definitions */
|
||||
import { t } from "@lingui/core/macro"
|
||||
import { Trans, useLingui } from "@lingui/react/macro"
|
||||
import { useStore } from "@nanostores/react"
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
import { memo, useMemo, useRef, useState } from "react"
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip"
|
||||
import { isReadOnlyUser, pb } from "@/lib/api"
|
||||
import { ConnectionType, connectionTypeLabels, MeterState, SystemStatus } from "@/lib/enums"
|
||||
import { BatteryState, ConnectionType, connectionTypeLabels, MeterState, SystemStatus } from "@/lib/enums"
|
||||
import { $longestSystemNameLen, $userSettings } from "@/lib/stores"
|
||||
import {
|
||||
cn,
|
||||
@@ -35,6 +35,7 @@ import {
|
||||
getMeterState,
|
||||
parseSemVer,
|
||||
} from "@/lib/utils"
|
||||
import { batteryStateTranslations } from "@/lib/i18n"
|
||||
import type { SystemRecord } from "@/types"
|
||||
import { SystemDialog } from "../add-system"
|
||||
import AlertButton from "../alerts/alert-button"
|
||||
@@ -58,7 +59,18 @@ import {
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from "../ui/dropdown-menu"
|
||||
import { EthernetIcon, GpuIcon, HourglassIcon, ThermometerIcon, WebSocketIcon } from "../ui/icons"
|
||||
import {
|
||||
BatteryMediumIcon,
|
||||
EthernetIcon,
|
||||
GpuIcon,
|
||||
HourglassIcon,
|
||||
ThermometerIcon,
|
||||
WebSocketIcon,
|
||||
BatteryHighIcon,
|
||||
BatteryLowIcon,
|
||||
PlugChargingIcon,
|
||||
BatteryFullIcon,
|
||||
} from "../ui/icons"
|
||||
|
||||
const STATUS_COLORS = {
|
||||
[SystemStatus.Up]: "bg-green-500",
|
||||
@@ -261,6 +273,52 @@ export function SystemsTableColumns(viewMode: "table" | "grid"): ColumnDef<Syste
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorFn: ({ info }) => info.bat?.[0],
|
||||
id: "battery",
|
||||
name: () => t({ message: "Bat", comment: "Battery label in systems table header" }),
|
||||
size: 70,
|
||||
Icon: BatteryMediumIcon,
|
||||
header: sortableHeader,
|
||||
hideSort: true,
|
||||
cell(info) {
|
||||
const [pct, state] = info.row.original.info.bat ?? []
|
||||
if (pct === undefined) {
|
||||
return null
|
||||
}
|
||||
|
||||
let Icon = PlugChargingIcon
|
||||
let iconColor = "text-muted-foreground"
|
||||
|
||||
if (state !== BatteryState.Charging) {
|
||||
if (pct < 25) {
|
||||
iconColor = pct < 11 ? "text-red-500" : "text-yellow-500"
|
||||
Icon = BatteryLowIcon
|
||||
} else if (pct < 75) {
|
||||
Icon = BatteryMediumIcon
|
||||
} else if (pct < 95) {
|
||||
Icon = BatteryHighIcon
|
||||
} else {
|
||||
Icon = BatteryFullIcon
|
||||
}
|
||||
}
|
||||
|
||||
const stateLabel =
|
||||
state !== undefined ? (batteryStateTranslations[state as BatteryState]?.() ?? undefined) : undefined
|
||||
|
||||
return (
|
||||
<Link
|
||||
tabIndex={-1}
|
||||
href={getPagePath($router, "system", { id: info.row.original.id })}
|
||||
className="flex items-center gap-1 tabular-nums tracking-tight relative z-10"
|
||||
title={stateLabel}
|
||||
>
|
||||
<Icon className={cn("size-3.5", iconColor)} />
|
||||
<span className="min-w-10">{pct}%</span>
|
||||
</Link>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorFn: ({ info }) => info.sv?.[0],
|
||||
id: "services",
|
||||
@@ -599,5 +657,5 @@ export const ActionsButton = memo(({ system }: { system: SystemRecord }) => {
|
||||
</AlertDialog>
|
||||
</>
|
||||
)
|
||||
}, [id, status, host, name, t, deleteOpen, editOpen])
|
||||
}, [id, status, host, name, system, t, deleteOpen, editOpen])
|
||||
})
|
||||
|
||||
@@ -131,6 +131,7 @@ export function HourglassIcon(props: SVGProps<SVGSVGElement>) {
|
||||
)
|
||||
}
|
||||
|
||||
// Apache 2.0 https://github.com/Templarian/MaterialDesign/blob/master/LICENSE
|
||||
export function WebSocketIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg viewBox="0 0 256 193" {...props} fill="currentColor">
|
||||
@@ -139,3 +140,48 @@ export function WebSocketIcon(props: SVGProps<SVGSVGElement>) {
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
// Apache 2.0 https://github.com/Templarian/MaterialDesign/blob/master/LICENSE
|
||||
export function BatteryMediumIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" {...props} fill="currentColor">
|
||||
<path d="M16 13H8V6h8m.67-2H15V2H9v2H7.33A1.33 1.33 0 0 0 6 5.33v15.34C6 21.4 6.6 22 7.33 22h9.34A1.33 1.33 0 0 0 18 20.67V5.33C18 4.6 17.4 4 16.67 4" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
// Apache 2.0 https://github.com/Templarian/MaterialDesign/blob/master/LICENSE
|
||||
export function BatteryLowIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" {...props} fill="currentColor">
|
||||
<path d="M16 17H8V6h8m.7-2H15V2H9v2H7.3A1.3 1.3 0 0 0 6 5.3v15.4q.1 1.2 1.3 1.3h9.4a1.3 1.3 0 0 0 1.3-1.3V5.3q-.1-1.2-1.3-1.3" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
// Apache 2.0 https://github.com/Templarian/MaterialDesign/blob/master/LICENSE
|
||||
export function BatteryHighIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" {...props} fill="currentColor">
|
||||
<path d="M16 9H8V6h8m.67-2H15V2H9v2H7.33A1.33 1.33 0 0 0 6 5.33v15.34C6 21.4 6.6 22 7.33 22h9.34A1.33 1.33 0 0 0 18 20.67V5.33C18 4.6 17.4 4 16.67 4" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
// Apache 2.0 https://github.com/Templarian/MaterialDesign/blob/master/LICENSE
|
||||
export function BatteryFullIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" {...props} fill="currentColor">
|
||||
<path d="M16.67 4H15V2H9v2H7.33A1.33 1.33 0 0 0 6 5.33v15.34C6 21.4 6.6 22 7.33 22h9.34A1.33 1.33 0 0 0 18 20.67V5.33C18 4.6 17.4 4 16.67 4" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
// https://github.com/phosphor-icons/core (MIT license)
|
||||
export function PlugChargingIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg viewBox="0 0 256 256" {...props} fill="currentColor">
|
||||
<path d="M224,48H180V16a12,12,0,0,0-24,0V48H100V16a12,12,0,0,0-24,0V48H32.55C24.4,48,20,54.18,20,60A12,12,0,0,0,32,72H44v92a44.05,44.05,0,0,0,44,44h28v32a12,12,0,0,0,24,0V208h28a44.05,44.05,0,0,0,44-44V72h12a12,12,0,0,0,0-24ZM188,164a20,20,0,0,1-20,20H88a20,20,0,0,1-20-20V72H188Zm-85.86-29.17a12,12,0,0,1-1.38-11l12-32a12,12,0,1,1,22.48,8.42L129.32,116H144a12,12,0,0,1,11.24,16.21l-12,32a12,12,0,0,1-22.48-8.42L126.68,140H112A12,12,0,0,1,102.14,134.83Z" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -161,10 +161,6 @@
|
||||
@utility ns-dialog {
|
||||
/* New system dialog width */
|
||||
min-width: 30.3rem;
|
||||
|
||||
:where(:lang(zh), :lang(zh-CN), :lang(ko)) & {
|
||||
min-width: 27.9rem;
|
||||
}
|
||||
}
|
||||
|
||||
.recharts-tooltip-wrapper {
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { t } from "@lingui/core/macro"
|
||||
import { CpuIcon, HardDriveIcon, HourglassIcon, MemoryStickIcon, ServerIcon, ThermometerIcon } from "lucide-react"
|
||||
import { CpuIcon, HardDriveIcon, MemoryStickIcon, ServerIcon } from "lucide-react"
|
||||
import type { RecordSubscription } from "pocketbase"
|
||||
import { EthernetIcon, GpuIcon } from "@/components/ui/icons"
|
||||
import { $alerts } from "@/lib/stores"
|
||||
import type { AlertInfo, AlertRecord } from "@/types"
|
||||
import { pb } from "./api"
|
||||
import { ThermometerIcon, BatteryMediumIcon, HourglassIcon } from "@/components/ui/icons"
|
||||
|
||||
/** Alert info for each alert type */
|
||||
export const alertInfo: Record<string, AlertInfo> = {
|
||||
@@ -83,6 +84,14 @@ export const alertInfo: Record<string, AlertInfo> = {
|
||||
step: 0.1,
|
||||
desc: () => t`Triggers when 15 minute load average exceeds a threshold`,
|
||||
},
|
||||
Battery: {
|
||||
name: () => t`Battery`,
|
||||
unit: "%",
|
||||
icon: BatteryMediumIcon,
|
||||
desc: () => t`Triggers when battery charge drops below a threshold`,
|
||||
start: 20,
|
||||
invert: true,
|
||||
},
|
||||
} as const
|
||||
|
||||
/** Helper to manage user alerts */
|
||||
|
||||
@@ -53,7 +53,7 @@ export function getLocale() {
|
||||
}
|
||||
locale = (locale || "en").split("-")[0]
|
||||
// use en if locale is not in languages
|
||||
if (!languages.some((l) => l.lang === locale)) {
|
||||
if (!languages.some((l) => l[0] === locale)) {
|
||||
locale = "en"
|
||||
}
|
||||
return locale
|
||||
|
||||
@@ -1,142 +1,32 @@
|
||||
export default [
|
||||
{
|
||||
lang: "ar",
|
||||
label: "العربية",
|
||||
e: "🇵🇸",
|
||||
},
|
||||
{
|
||||
lang: "bg",
|
||||
label: "Български",
|
||||
e: "🇧🇬",
|
||||
},
|
||||
{
|
||||
lang: "cs",
|
||||
label: "Čeština",
|
||||
e: "🇨🇿",
|
||||
},
|
||||
{
|
||||
lang: "da",
|
||||
label: "Dansk",
|
||||
e: "🇩🇰",
|
||||
},
|
||||
{
|
||||
lang: "de",
|
||||
label: "Deutsch",
|
||||
e: "🇩🇪",
|
||||
},
|
||||
{
|
||||
lang: "en",
|
||||
label: "English",
|
||||
e: "🇺🇸",
|
||||
},
|
||||
{
|
||||
lang: "es",
|
||||
label: "Español",
|
||||
e: "🇲🇽",
|
||||
},
|
||||
{
|
||||
lang: "fa",
|
||||
label: "فارسی",
|
||||
e: "🇮🇷",
|
||||
},
|
||||
{
|
||||
lang: "fr",
|
||||
label: "Français",
|
||||
e: "🇫🇷",
|
||||
},
|
||||
{
|
||||
lang: "he",
|
||||
label: "עברית",
|
||||
e: "🕎",
|
||||
},
|
||||
{
|
||||
lang: "hr",
|
||||
label: "Hrvatski",
|
||||
e: "🇭🇷",
|
||||
},
|
||||
{
|
||||
lang: "hu",
|
||||
label: "Magyar",
|
||||
e: "🇭🇺",
|
||||
},
|
||||
{
|
||||
lang: "it",
|
||||
label: "Italiano",
|
||||
e: "🇮🇹",
|
||||
},
|
||||
{
|
||||
lang: "ja",
|
||||
label: "日本語",
|
||||
e: "🇯🇵",
|
||||
},
|
||||
{
|
||||
lang: "ko",
|
||||
label: "한국어",
|
||||
e: "🇰🇷",
|
||||
},
|
||||
{
|
||||
lang: "nl",
|
||||
label: "Nederlands",
|
||||
e: "🇳🇱",
|
||||
},
|
||||
{
|
||||
lang: "no",
|
||||
label: "Norsk",
|
||||
e: "🇳🇴",
|
||||
},
|
||||
{
|
||||
lang: "pl",
|
||||
label: "Polski",
|
||||
e: "🇵🇱",
|
||||
},
|
||||
{
|
||||
lang: "pt",
|
||||
label: "Português",
|
||||
e: "🇧🇷",
|
||||
},
|
||||
{
|
||||
lang: "tr",
|
||||
label: "Türkçe",
|
||||
e: "🇹🇷",
|
||||
},
|
||||
{
|
||||
lang: "ru",
|
||||
label: "Русский",
|
||||
e: "🇷🇺",
|
||||
},
|
||||
{
|
||||
lang: "sl",
|
||||
label: "Slovenščina",
|
||||
e: "🇸🇮",
|
||||
},
|
||||
{
|
||||
lang: "sv",
|
||||
label: "Svenska",
|
||||
e: "🇸🇪",
|
||||
},
|
||||
{
|
||||
lang: "uk",
|
||||
label: "Українська",
|
||||
e: "🇺🇦",
|
||||
},
|
||||
{
|
||||
lang: "vi",
|
||||
label: "Tiếng Việt",
|
||||
e: "🇻🇳",
|
||||
},
|
||||
{
|
||||
lang: "zh-CN",
|
||||
label: "简体中文",
|
||||
e: "🇨🇳",
|
||||
},
|
||||
{
|
||||
lang: "zh-HK",
|
||||
label: "繁體中文",
|
||||
e: "🇭🇰",
|
||||
},
|
||||
{
|
||||
lang: "zh",
|
||||
label: "繁體中文",
|
||||
e: "🇹🇼",
|
||||
},
|
||||
["ar", "العربية", "🇵🇸"],
|
||||
["bg", "Български", "🇧🇬"],
|
||||
["cs", "Čeština", "🇨🇿"],
|
||||
["da", "Dansk", "🇩🇰"],
|
||||
["de", "Deutsch", "🇩🇪"],
|
||||
["en", "English", "🇬🇧"],
|
||||
["es", "Español", "🇪🇸"],
|
||||
["fa", "فارسی", "🇮🇷"],
|
||||
["fr", "Français", "🇫🇷"],
|
||||
["he", "עברית", "🕎"],
|
||||
["hr", "Hrvatski", "🇭🇷"],
|
||||
["hu", "Magyar", "🇭🇺"],
|
||||
["id", "Indonesia", "🇮🇩"],
|
||||
["it", "Italiano", "🇮🇹"],
|
||||
["ja", "日本語", "🇯🇵"],
|
||||
["ko", "한국어", "🇰🇷"],
|
||||
["nl", "Nederlands", "🇳🇱"],
|
||||
["no", "Norsk", "🇳🇴"],
|
||||
["pl", "Polski", "🇵🇱"],
|
||||
["pt", "Português", "🇵🇹"],
|
||||
["ru", "Русский", "🇷🇺"],
|
||||
["sl", "Slovenščina", "🇸🇮"],
|
||||
["sr", "Српски", "🇷🇸"],
|
||||
["sv", "Svenska", "🇸🇪"],
|
||||
["tr", "Türkçe", "🇹🇷"],
|
||||
["uk", "Українська", "🇺🇦"],
|
||||
["vi", "Tiếng Việt", "🇻🇳"],
|
||||
["zh-CN", "简体中文", "🇨🇳"],
|
||||
["zh-HK", "繁體中文", "🇭🇰"],
|
||||
["zh", "繁體中文", "🇹🇼"],
|
||||
] as const
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: ar\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-10-30 21:52\n"
|
||||
"PO-Revision-Date: 2025-12-25 19:15\n"
|
||||
"Last-Translator: \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"
|
||||
@@ -24,6 +24,10 @@ msgstr ""
|
||||
msgid "{0} of {1} row(s) selected."
|
||||
msgstr "تم تحديد {0} من {1} صف"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "{cores, plural, one {# core} other {# cores}}"
|
||||
msgstr "{cores, plural, one {# نواة} other {# نواة}}"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "{count, plural, one {{countString} day} other {{countString} days}}"
|
||||
msgstr "{count, plural, one {{countString} يوم} other {{countString} أيام}}"
|
||||
@@ -36,6 +40,10 @@ msgstr "{count, plural, one {{countString} ساعة} other {{countString} ساع
|
||||
msgid "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
|
||||
msgstr "{count, plural, one {{countString} دقيقة} few {{countString} دقائق} many {{countString} دقيقة} other {{countString} دقيقة}}"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "{threads, plural, one {# thread} other {# threads}}"
|
||||
msgstr "{threads, plural, one {# خيط} other {# خيط}}"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 hour"
|
||||
msgstr "1 ساعة"
|
||||
@@ -76,13 +84,16 @@ msgid "5 min"
|
||||
msgstr "5 دقائق"
|
||||
|
||||
#. Table column
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Actions"
|
||||
msgstr "إجراءات"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Active"
|
||||
msgstr "نشط"
|
||||
|
||||
@@ -95,12 +106,14 @@ msgid "Active state"
|
||||
msgstr "الحالة النشطة"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add <0>System</0>"
|
||||
msgstr "إضافة <0>نظام</0>"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Add {foo}"
|
||||
msgstr "إضافة {foo}"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add New System"
|
||||
msgstr "إضافة نظام جديد"
|
||||
msgid "Add <0>System</0>"
|
||||
msgstr "إضافة <0>نظام</0>"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add system"
|
||||
@@ -151,6 +164,7 @@ msgstr "جميع الحاويات"
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/home.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "All Systems"
|
||||
@@ -176,6 +190,11 @@ msgstr "متوسط"
|
||||
msgid "Average CPU utilization of containers"
|
||||
msgstr "متوسط استخدام وحدة المعالجة المركزية للحاويات"
|
||||
|
||||
#. placeholder {0}: alertData.unit
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "Average drops below <0>{value}{0}</0>"
|
||||
msgstr "المتوسط ينخفض أقل من <0>{value}{0}</0>"
|
||||
|
||||
#. placeholder {0}: alertData.unit
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "Average exceeds <0>{value}{0}</0>"
|
||||
@@ -208,7 +227,13 @@ msgstr "النسخ الاحتياطية"
|
||||
msgid "Bandwidth"
|
||||
msgstr "عرض النطاق الترددي"
|
||||
|
||||
#. Battery label in systems table header
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Bat"
|
||||
msgstr "بطارية"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Battery"
|
||||
msgstr "البطارية"
|
||||
|
||||
@@ -224,6 +249,13 @@ msgstr "أصبح غير نشط"
|
||||
msgid "Before"
|
||||
msgstr "قبل"
|
||||
|
||||
#. placeholder {0}: alert.value
|
||||
#. placeholder {1}: info.unit
|
||||
#. placeholder {2}: alert.min
|
||||
#: src/components/active-alerts.tsx
|
||||
msgid "Below {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
|
||||
msgstr "أقل من {0}{1} في آخر {2, plural, one {# دقيقة} other {# دقائق}}"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Beszel supports OpenID Connect and many OAuth2 authentication providers."
|
||||
msgstr "يدعم بيزيل بروتوكول OpenID Connect والعديد من مزوّدي المصادقة عبر بروتوكول OAuth2."
|
||||
@@ -267,6 +299,7 @@ msgid "Can stop"
|
||||
msgstr "يمكن الإيقاف"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Cancel"
|
||||
msgstr "إلغاء"
|
||||
@@ -320,6 +353,12 @@ msgstr "تحقق من السجلات لمزيد من التفاصيل."
|
||||
msgid "Check your notification service"
|
||||
msgstr "تحقق من خدمة الإشعارات الخاصة بك"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Clear"
|
||||
msgstr "مسح"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
msgid "Click on a container to view more information."
|
||||
msgstr "انقر على حاوية لعرض مزيد من المعلومات."
|
||||
@@ -442,6 +481,10 @@ msgstr "تفصيل وقت المعالج"
|
||||
msgid "CPU Usage"
|
||||
msgstr "استخدام وحدة المعالجة المركزية"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Create"
|
||||
msgstr "إنشاء"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Create account"
|
||||
msgstr "إنشاء حساب"
|
||||
@@ -473,15 +516,18 @@ msgstr "الحالة الحالية"
|
||||
msgid "Cycles"
|
||||
msgstr "الدورات"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Dashboard"
|
||||
msgstr "لوحة التحكم"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Daily"
|
||||
msgstr "يوميًا"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Default time period"
|
||||
msgstr "الفترة الزمنية الافتراضية"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Delete"
|
||||
msgstr "حذف"
|
||||
@@ -548,7 +594,7 @@ msgstr "التوثيق"
|
||||
|
||||
#. Context: System is down
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Down"
|
||||
@@ -566,11 +612,16 @@ msgstr "تنزيل"
|
||||
msgid "Duration"
|
||||
msgstr "المدة"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Edit"
|
||||
msgstr "تعديل"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Edit {foo}"
|
||||
msgstr "إضافة {foo}"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
#: src/components/login/otp-forms.tsx
|
||||
@@ -586,6 +637,11 @@ msgstr "إشعارات البريد الإشباكي"
|
||||
msgid "Empty"
|
||||
msgstr "فارغة"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "End Time"
|
||||
msgstr "وقت النهاية"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Enter email address to reset password"
|
||||
msgstr "أدخل عنوان البريد الإشباكي لإعادة تعيين كلمة المرور"
|
||||
@@ -598,10 +654,16 @@ msgstr "أدخل عنوان البريد الإشباكي..."
|
||||
msgid "Enter your one-time password."
|
||||
msgstr "أدخل كلمة المرور لمرة واحدة الخاصة بك."
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Ephemeral"
|
||||
msgstr "مؤقت"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Error"
|
||||
@@ -626,6 +688,10 @@ msgstr "سيتم حذف الأنظمة الحالية غير المعرفة في
|
||||
msgid "Exited active"
|
||||
msgstr "خرج نشطًا"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Expires after one hour or on hub restart."
|
||||
msgstr "ينتهي بعد ساعة واحدة أو عند إعادة تشغيل المحور."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr "تصدير"
|
||||
@@ -656,6 +722,7 @@ msgstr "فشل في المصادقة"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Failed to save settings"
|
||||
msgstr "فشل في حفظ الإعدادات"
|
||||
|
||||
@@ -714,6 +781,10 @@ msgstr "ممتلئة"
|
||||
msgid "General"
|
||||
msgstr "عام"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Global"
|
||||
msgstr "عالمي"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "GPU Engines"
|
||||
msgstr "محركات GPU"
|
||||
@@ -758,15 +829,14 @@ msgctxt "Docker image"
|
||||
msgid "Image"
|
||||
msgstr "صورة"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Inactive"
|
||||
msgstr "غير نشط"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Invalid email address."
|
||||
msgstr "عنوان البريد الإشباكي غير صالح."
|
||||
|
||||
#. Linux kernel
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Kernel"
|
||||
msgstr "النواة"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "اللغة"
|
||||
@@ -859,6 +929,7 @@ msgid "Max 1 min"
|
||||
msgstr "الحد الأقصى دقيقة"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
@@ -959,12 +1030,19 @@ msgstr "دعم OAuth 2 / OIDC"
|
||||
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
|
||||
msgstr "في كل إعادة تشغيل، سيتم تحديث الأنظمة في قاعدة البيانات لتتطابق مع الأنظمة المعرفة في الملف."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "One-time"
|
||||
msgstr "مرة واحدة"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "One-time password"
|
||||
msgstr "كلمة مرور لمرة واحدة"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Open menu"
|
||||
msgstr "فتح القائمة"
|
||||
@@ -981,6 +1059,7 @@ msgstr "أخرى"
|
||||
msgid "Overwrite existing alerts"
|
||||
msgstr "الكتابة فوق التنبيهات الحالية"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Page"
|
||||
@@ -1013,6 +1092,10 @@ msgstr "يجب أن تكون كلمة المرور أقل من 72 بايت."
|
||||
msgid "Password reset request received"
|
||||
msgstr "تم استلام طلب إعادة تعيين كلمة المرور"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Past"
|
||||
msgstr "الماضي"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Pause"
|
||||
msgstr "إيقاف مؤقت"
|
||||
@@ -1034,6 +1117,14 @@ msgstr "متوسط الاستخدام لكل نواة"
|
||||
msgid "Percentage of time spent in each state"
|
||||
msgstr "النسبة المئوية للوقت المقضي في كل حالة"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Permanent"
|
||||
msgstr "دائم"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Persistence"
|
||||
msgstr "الاستمرارية"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
msgstr "يرجى <0>تكوين خادم SMTP</0> لضمان تسليم التنبيهات."
|
||||
@@ -1094,6 +1185,10 @@ msgstr "تم بدء العملية"
|
||||
msgid "Public Key"
|
||||
msgstr "المفتاح العام"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Quiet Hours"
|
||||
msgstr "ساعات الهدوء"
|
||||
|
||||
#. Disk read
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
@@ -1106,6 +1201,7 @@ msgstr "تم الاستلام"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Refresh"
|
||||
msgstr "تحديث"
|
||||
|
||||
@@ -1185,6 +1281,22 @@ msgstr "حفظ الإعدادات"
|
||||
msgid "Save system"
|
||||
msgstr "احفظ النظام"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Saved in the database and does not expire until you disable it."
|
||||
msgstr "محفوظ في قاعدة البيانات ولا ينتهي حتى تقوم بتعطيله."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule"
|
||||
msgstr "جدولة"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule quiet hours where notifications will not be sent, such as during maintenance periods."
|
||||
msgstr "جدولة ساعات الهدوء حيث لن يتم إرسال الإشعارات، مثل أثناء فترات الصيانة."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule quiet hours where notifications will not be sent."
|
||||
msgstr "جدولة ساعات الهدوء حيث لن يتم إرسال الإشعارات."
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Search"
|
||||
msgstr "بحث"
|
||||
@@ -1197,6 +1309,10 @@ msgstr "البحث عن الأنظمة أو الإعدادات..."
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "راجع <0>إعدادات الإشعارات</0> لتكوين كيفية تلقي التنبيهات."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Select {foo}"
|
||||
msgstr "تحديد {foo}"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "تم الإرسال"
|
||||
@@ -1240,8 +1356,14 @@ msgstr "إعدادات SMTP"
|
||||
msgid "Sort By"
|
||||
msgstr "الترتيب حسب"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Start Time"
|
||||
msgstr "وقت البدء"
|
||||
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "الحالة"
|
||||
@@ -1266,9 +1388,15 @@ msgstr "مساحة التبديل المستخدمة من قبل النظام"
|
||||
msgid "Swap Usage"
|
||||
msgstr "استخدام التبديل"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "System"
|
||||
@@ -1353,8 +1481,8 @@ msgstr "تنسيق الوقت"
|
||||
msgid "To email(s)"
|
||||
msgstr "إلى البريد الإشباكي"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "Toggle grid"
|
||||
msgstr "تبديل الشبكة"
|
||||
|
||||
@@ -1423,6 +1551,10 @@ msgstr "يتم التفعيل عندما يتجاوز متوسط التحميل
|
||||
msgid "Triggers when any sensor exceeds a threshold"
|
||||
msgstr "يتم التفعيل عندما يتجاوز أي مستشعر عتبة معينة"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when battery charge drops below a threshold"
|
||||
msgstr "يتم التفعيل عندما تنخفض شحنة البطارية أقل من عتبة معينة"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when combined up/down exceeds a threshold"
|
||||
msgstr "يتم التفعيل عندما يتجاوز الجمع بين الصعود/الهبوط عتبة معينة"
|
||||
@@ -1447,6 +1579,8 @@ msgstr "يتم التفعيل عندما يتغير الحالة بين التش
|
||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||
msgstr "يتم التفعيل عندما يتجاوز استخدام أي قرص عتبة معينة"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Type"
|
||||
msgstr "النوع"
|
||||
@@ -1476,7 +1610,7 @@ msgid "Unlimited"
|
||||
msgstr "غير محدود"
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Up"
|
||||
msgstr "قيد التشغيل"
|
||||
@@ -1485,7 +1619,12 @@ msgstr "قيد التشغيل"
|
||||
msgid "Up ({upSystemsLength})"
|
||||
msgstr "قيد التشغيل ({upSystemsLength})"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Update"
|
||||
msgstr "تحديث"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Updated"
|
||||
msgstr "تم التحديث"
|
||||
@@ -1498,7 +1637,7 @@ msgstr "يتم التحديث كل 10 دقائق."
|
||||
msgid "Upload"
|
||||
msgstr "رفع"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "Uptime"
|
||||
msgstr "مدة التشغيل"
|
||||
|
||||
@@ -1570,8 +1709,8 @@ msgid "Webhook / Push notifications"
|
||||
msgstr "إشعارات Webhook / Push"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation. Expires after one hour or on hub restart."
|
||||
msgstr "عند التفعيل، يسمح هذا الرمز المميز للوكلاء بالتسجيل الذاتي دون إنشاء نظام مسبق. ينتهي بعد ساعة واحدة أو عند إعادة تشغيل المحور."
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation."
|
||||
msgstr "عند التفعيل، يسمح هذا الرمز المميز للوكلاء بالتسجيل الذاتي دون إنشاء نظام مسبق."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: bg\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-10-20 21:37\n"
|
||||
"PO-Revision-Date: 2025-12-02 23:17\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Bulgarian\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -24,6 +24,10 @@ msgstr ""
|
||||
msgid "{0} of {1} row(s) selected."
|
||||
msgstr "{0} от {1} селектирани."
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "{cores, plural, one {# core} other {# cores}}"
|
||||
msgstr "{cores, plural, one {# ядро} other {# ядра}}"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "{count, plural, one {{countString} day} other {{countString} days}}"
|
||||
msgstr "{count, plural, one {{countString} ден} other {{countString} дни}}"
|
||||
@@ -36,6 +40,10 @@ msgstr "{count, plural, one {{countString} час} other {{countString} часа
|
||||
msgid "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
|
||||
msgstr "{count, plural, one {{countString} минута} few {{countString} минути} many {{countString} минути} other {{countString} минути}}"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "{threads, plural, one {# thread} other {# threads}}"
|
||||
msgstr "{threads, plural, one {# нишка} other {# нишки}}"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 hour"
|
||||
msgstr "1 час"
|
||||
@@ -76,13 +84,16 @@ msgid "5 min"
|
||||
msgstr "5 минути"
|
||||
|
||||
#. Table column
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Actions"
|
||||
msgstr "Действия"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Active"
|
||||
msgstr "Активен"
|
||||
|
||||
@@ -95,12 +106,14 @@ msgid "Active state"
|
||||
msgstr "Активно състояние"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add <0>System</0>"
|
||||
msgstr "Добави <0>Система</0>"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Add {foo}"
|
||||
msgstr "Добави {foo}"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add New System"
|
||||
msgstr "Добави нова система"
|
||||
msgid "Add <0>System</0>"
|
||||
msgstr "Добави <0>Система</0>"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add system"
|
||||
@@ -151,6 +164,7 @@ msgstr "Всички контейнери"
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/home.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "All Systems"
|
||||
@@ -176,6 +190,11 @@ msgstr "Средно"
|
||||
msgid "Average CPU utilization of containers"
|
||||
msgstr "Средно използване на процесора на контейнерите"
|
||||
|
||||
#. placeholder {0}: alertData.unit
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "Average drops below <0>{value}{0}</0>"
|
||||
msgstr "Средната стойност пада под <0>{value}{0}</0>"
|
||||
|
||||
#. placeholder {0}: alertData.unit
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "Average exceeds <0>{value}{0}</0>"
|
||||
@@ -208,7 +227,13 @@ msgstr "Архиви"
|
||||
msgid "Bandwidth"
|
||||
msgstr "Bandwidth на мрежата"
|
||||
|
||||
#. Battery label in systems table header
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Bat"
|
||||
msgstr "Bat"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Battery"
|
||||
msgstr "Батерия"
|
||||
|
||||
@@ -224,6 +249,13 @@ msgstr "Стана неактивен"
|
||||
msgid "Before"
|
||||
msgstr "Преди"
|
||||
|
||||
#. placeholder {0}: alert.value
|
||||
#. placeholder {1}: info.unit
|
||||
#. placeholder {2}: alert.min
|
||||
#: src/components/active-alerts.tsx
|
||||
msgid "Below {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
|
||||
msgstr "Под {0}{1} в последните {2, plural, one {# минута} other {# минути}}"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Beszel supports OpenID Connect and many OAuth2 authentication providers."
|
||||
msgstr "Beszel поддържа OpenID Connect и много други OAuth2 доставчици за удостоверяване."
|
||||
@@ -267,6 +299,7 @@ msgid "Can stop"
|
||||
msgstr "Може да се спре"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Cancel"
|
||||
msgstr "Откажи"
|
||||
@@ -320,6 +353,12 @@ msgstr "Провери log-овете за повече информация."
|
||||
msgid "Check your notification service"
|
||||
msgstr "Провери услугата си за удостоверяване"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Clear"
|
||||
msgstr "Изчисти"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
msgid "Click on a container to view more information."
|
||||
msgstr "Кликнете върху контейнер, за да видите повече информация."
|
||||
@@ -442,6 +481,10 @@ msgstr "Разбивка на времето на CPU"
|
||||
msgid "CPU Usage"
|
||||
msgstr "Употреба на процесор"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Create"
|
||||
msgstr "Създай"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Create account"
|
||||
msgstr "Създай акаунт"
|
||||
@@ -473,15 +516,18 @@ msgstr "Текущо състояние"
|
||||
msgid "Cycles"
|
||||
msgstr "Цикли"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Dashboard"
|
||||
msgstr "Табло"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Daily"
|
||||
msgstr "Дневно"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Default time period"
|
||||
msgstr "Времеви диапазон по подразбиране"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Delete"
|
||||
msgstr "Изтрий"
|
||||
@@ -548,7 +594,7 @@ msgstr "Документация"
|
||||
|
||||
#. Context: System is down
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Down"
|
||||
@@ -566,11 +612,16 @@ msgstr "Изтегляне"
|
||||
msgid "Duration"
|
||||
msgstr "Продължителност"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Edit"
|
||||
msgstr "Редактирай"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Edit {foo}"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
#: src/components/login/otp-forms.tsx
|
||||
@@ -586,6 +637,11 @@ msgstr "Имейл нотификации"
|
||||
msgid "Empty"
|
||||
msgstr "Празна"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "End Time"
|
||||
msgstr "Крайно време"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Enter email address to reset password"
|
||||
msgstr "Въведи имейл адрес за да нулираш паролата"
|
||||
@@ -598,10 +654,16 @@ msgstr "Въведи имейл адрес..."
|
||||
msgid "Enter your one-time password."
|
||||
msgstr "Въведете Вашата еднократна парола."
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Ephemeral"
|
||||
msgstr "Ефимерен"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Error"
|
||||
@@ -626,6 +688,10 @@ msgstr "Съществуващи системи които не са дефин
|
||||
msgid "Exited active"
|
||||
msgstr "Излезе активно"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Expires after one hour or on hub restart."
|
||||
msgstr "Изтича след един час или при рестартиране на хъба."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr "Експортиране"
|
||||
@@ -656,6 +722,7 @@ msgstr "Неуспешно удостоверяване"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Failed to save settings"
|
||||
msgstr "Неуспешно запазване на настройки"
|
||||
|
||||
@@ -714,6 +781,10 @@ msgstr "Пълна"
|
||||
msgid "General"
|
||||
msgstr "Общо"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Global"
|
||||
msgstr "Глобален"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "GPU Engines"
|
||||
msgstr "GPU двигатели"
|
||||
@@ -758,15 +829,14 @@ msgctxt "Docker image"
|
||||
msgid "Image"
|
||||
msgstr "Образ"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Inactive"
|
||||
msgstr "Неактивен"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Invalid email address."
|
||||
msgstr "Невалиден имейл адрес."
|
||||
|
||||
#. Linux kernel
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Kernel"
|
||||
msgstr "Linux Kernel"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "Език"
|
||||
@@ -859,6 +929,7 @@ msgid "Max 1 min"
|
||||
msgstr "Максимум 1 минута"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
@@ -959,12 +1030,19 @@ msgstr "Поддръжка на OAuth 2 / OIDC"
|
||||
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
|
||||
msgstr "На всеки рестарт, системите в датабазата ще бъдат обновени да съвпадат със системите зададени във файла."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "One-time"
|
||||
msgstr "Еднократен"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "One-time password"
|
||||
msgstr "Еднократна парола"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Open menu"
|
||||
msgstr "Отвори менюто"
|
||||
@@ -981,6 +1059,7 @@ msgstr "Други"
|
||||
msgid "Overwrite existing alerts"
|
||||
msgstr "Презапиши съществуващи тревоги"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Page"
|
||||
@@ -1013,6 +1092,10 @@ msgstr "Паролата трябва да е по-малка от 72 байта
|
||||
msgid "Password reset request received"
|
||||
msgstr "Получено е искането за нулиране на паролата"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Past"
|
||||
msgstr "Минал"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Pause"
|
||||
msgstr "Пауза"
|
||||
@@ -1034,6 +1117,14 @@ msgstr "Средно използване на ядро"
|
||||
msgid "Percentage of time spent in each state"
|
||||
msgstr "Процент време, прекарано във всяко състояние"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Permanent"
|
||||
msgstr "Постоянен"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Persistence"
|
||||
msgstr "Устойчивост"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
msgstr "Моля <0>конфигурурай SMTP сървър</0> за да се подсигуриш, че тревогите са доставени."
|
||||
@@ -1094,6 +1185,10 @@ msgstr "Процесът стартира"
|
||||
msgid "Public Key"
|
||||
msgstr "Публичен ключ"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Quiet Hours"
|
||||
msgstr "Тихи часове"
|
||||
|
||||
#. Disk read
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
@@ -1106,6 +1201,7 @@ msgstr "Получени"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Refresh"
|
||||
msgstr "Опресни"
|
||||
|
||||
@@ -1185,6 +1281,22 @@ msgstr "Запази настройките"
|
||||
msgid "Save system"
|
||||
msgstr "Запази система"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Saved in the database and does not expire until you disable it."
|
||||
msgstr "Запазен е в базата данни и не изтича, докато не го деактивирате."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule"
|
||||
msgstr "График"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule quiet hours where notifications will not be sent, such as during maintenance periods."
|
||||
msgstr "Планирай тихи часове, когато няма да се изпращат известия, като например по време на периоди на поддръжка."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule quiet hours where notifications will not be sent."
|
||||
msgstr "Планирай тихи часове, когато няма да се изпращат известия."
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Search"
|
||||
msgstr "Търси"
|
||||
@@ -1197,6 +1309,10 @@ msgstr "Търси за системи или настройки..."
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "Виж <0>настройките за нотификациите</0> за да конфигурираш как получаваш тревоги."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Select {foo}"
|
||||
msgstr "Избери {foo}"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "Изпратени"
|
||||
@@ -1240,8 +1356,14 @@ msgstr "Настройки за SMTP"
|
||||
msgid "Sort By"
|
||||
msgstr "Сортиране по"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Start Time"
|
||||
msgstr "Начален час"
|
||||
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "Състояние"
|
||||
@@ -1266,9 +1388,15 @@ msgstr "Изполван swap от системата"
|
||||
msgid "Swap Usage"
|
||||
msgstr "Използване на swap"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "System"
|
||||
@@ -1353,8 +1481,8 @@ msgstr "Формат на времето"
|
||||
msgid "To email(s)"
|
||||
msgstr "До имейл(ите)"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "Toggle grid"
|
||||
msgstr "Превключване на мрежа"
|
||||
|
||||
@@ -1423,6 +1551,10 @@ msgstr "Задейства се, когато употребата на паме
|
||||
msgid "Triggers when any sensor exceeds a threshold"
|
||||
msgstr "Задейства се, когато някой даден сензор надвиши зададен праг"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when battery charge drops below a threshold"
|
||||
msgstr "Задейства се, когато зарядът на батерията падне под зададен праг"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when combined up/down exceeds a threshold"
|
||||
msgstr "Задейства се, когато комбинираното качване/сваляне надвиши зададен праг"
|
||||
@@ -1433,7 +1565,7 @@ msgstr "Задейства се, когато употребата на проц
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when GPU usage exceeds a threshold"
|
||||
msgstr ""
|
||||
msgstr "Задейства се, когато използването на GPU надвиши праг"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when memory usage exceeds a threshold"
|
||||
@@ -1447,6 +1579,8 @@ msgstr "Задейства се, когато статуса превключв
|
||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||
msgstr "Задейства се, когато употребата на някой диск надивши зададен праг"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Type"
|
||||
msgstr "Тип"
|
||||
@@ -1476,7 +1610,7 @@ msgid "Unlimited"
|
||||
msgstr "Неограничено"
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Up"
|
||||
msgstr "Нагоре"
|
||||
@@ -1485,7 +1619,12 @@ msgstr "Нагоре"
|
||||
msgid "Up ({upSystemsLength})"
|
||||
msgstr "Нагоре ({upSystemsLength})"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Update"
|
||||
msgstr "Актуализирай"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Updated"
|
||||
msgstr "Актуализирано"
|
||||
@@ -1498,7 +1637,7 @@ msgstr "Актуализира се на всеки 10 минути."
|
||||
msgid "Upload"
|
||||
msgstr "Качване"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "Uptime"
|
||||
msgstr "Време на работа"
|
||||
|
||||
@@ -1570,8 +1709,8 @@ msgid "Webhook / Push notifications"
|
||||
msgstr "Webhook / Пуш нотификации"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation. Expires after one hour or on hub restart."
|
||||
msgstr "Когато е активиран, този символ позволява на агентите да се регистрират сами без предварително създаване на система. Изтича след един час или при рестартиране на хъба."
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation."
|
||||
msgstr "Когато е активиран, този символ позволява на агентите да се регистрират сами без предварително създаване на система."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: cs\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-10-28 23:00\n"
|
||||
"PO-Revision-Date: 2025-12-05 20:24\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Czech\n"
|
||||
"Plural-Forms: nplurals=4; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 3;\n"
|
||||
@@ -24,6 +24,10 @@ msgstr ""
|
||||
msgid "{0} of {1} row(s) selected."
|
||||
msgstr "{0} z {1} vybraných řádků."
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "{cores, plural, one {# core} other {# cores}}"
|
||||
msgstr "{cores, plural, one {# jádro} few {# jádra} many {# jader} other {# jader}}"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "{count, plural, one {{countString} day} other {{countString} days}}"
|
||||
msgstr "{count, plural, one {{countString} den} few {{countString} dny} other {{countString} dní}}"
|
||||
@@ -36,6 +40,10 @@ msgstr "{count, plural, one {{countString} Hodina} few {{countString} Hodiny} ma
|
||||
msgid "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
|
||||
msgstr "{count, plural, one {{countString} minuta} few {{countString} minuty} many {{countString} minut} other {{countString} minut}}"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "{threads, plural, one {# thread} other {# threads}}"
|
||||
msgstr "{threads, plural, one {# vlákno} few {# vlákna} many {# vláken} other {# vláken}}"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 hour"
|
||||
msgstr "1 hodina"
|
||||
@@ -43,7 +51,7 @@ msgstr "1 hodina"
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "1 min"
|
||||
msgstr ""
|
||||
msgstr "1 min"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 minute"
|
||||
@@ -60,7 +68,7 @@ msgstr "12 hodin"
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "15 min"
|
||||
msgstr ""
|
||||
msgstr "15 min"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "24 hours"
|
||||
@@ -73,16 +81,19 @@ msgstr "30 dní"
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "5 min"
|
||||
msgstr ""
|
||||
msgstr "5 min"
|
||||
|
||||
#. Table column
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Actions"
|
||||
msgstr "Akce"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Active"
|
||||
msgstr "Aktivní"
|
||||
|
||||
@@ -95,12 +106,14 @@ msgid "Active state"
|
||||
msgstr "Aktivní stav"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add <0>System</0>"
|
||||
msgstr "Přidat <0>Systém</0>"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Add {foo}"
|
||||
msgstr "Přidat {foo}"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add New System"
|
||||
msgstr "Přidat nový systém"
|
||||
msgid "Add <0>System</0>"
|
||||
msgstr "Přidat <0>Systém</0>"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add system"
|
||||
@@ -151,6 +164,7 @@ msgstr "Všechny kontejnery"
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/home.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "All Systems"
|
||||
@@ -176,6 +190,11 @@ msgstr "Průměr"
|
||||
msgid "Average CPU utilization of containers"
|
||||
msgstr "Průměrné využití CPU kontejnerů"
|
||||
|
||||
#. placeholder {0}: alertData.unit
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "Average drops below <0>{value}{0}</0>"
|
||||
msgstr "Průměr klesne pod <0>{value}{0}</0>"
|
||||
|
||||
#. placeholder {0}: alertData.unit
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "Average exceeds <0>{value}{0}</0>"
|
||||
@@ -208,7 +227,13 @@ msgstr "Zálohy"
|
||||
msgid "Bandwidth"
|
||||
msgstr "Přenos"
|
||||
|
||||
#. Battery label in systems table header
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Bat"
|
||||
msgstr "Bat"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Battery"
|
||||
msgstr "Baterie"
|
||||
|
||||
@@ -224,6 +249,13 @@ msgstr "Stal se neaktivním"
|
||||
msgid "Before"
|
||||
msgstr "Před"
|
||||
|
||||
#. placeholder {0}: alert.value
|
||||
#. placeholder {1}: info.unit
|
||||
#. placeholder {2}: alert.min
|
||||
#: src/components/active-alerts.tsx
|
||||
msgid "Below {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
|
||||
msgstr "Pod {0}{1} za {2, plural, one {poslední # minutu} few {poslední # minuty} other {posledních # minut}}"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Beszel supports OpenID Connect and many OAuth2 authentication providers."
|
||||
msgstr "Beszel podporuje OpenID Connect a mnoho poskytovatelů OAuth2 ověřování."
|
||||
@@ -267,6 +299,7 @@ msgid "Can stop"
|
||||
msgstr "Může zastavit"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Cancel"
|
||||
msgstr "Zrušit"
|
||||
@@ -320,6 +353,12 @@ msgstr "Pro více informací zkontrolujte logy."
|
||||
msgid "Check your notification service"
|
||||
msgstr "Zkontrolujte službu upozornění"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Clear"
|
||||
msgstr "Vymazat"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
msgid "Click on a container to view more information."
|
||||
msgstr "Klikněte na kontejner pro zobrazení dalších informací."
|
||||
@@ -442,6 +481,10 @@ msgstr "Rozdělení času CPU"
|
||||
msgid "CPU Usage"
|
||||
msgstr "Využití procesoru"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Create"
|
||||
msgstr "Vytvořit"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Create account"
|
||||
msgstr "Vytvořit účet"
|
||||
@@ -473,15 +516,18 @@ msgstr "Aktuální stav"
|
||||
msgid "Cycles"
|
||||
msgstr "Cykly"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Dashboard"
|
||||
msgstr "Přehled"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Daily"
|
||||
msgstr "Denně"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Default time period"
|
||||
msgstr "Výchozí doba"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Delete"
|
||||
msgstr "Odstranit"
|
||||
@@ -509,11 +555,11 @@ msgstr "Vybíjení"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Disk"
|
||||
msgstr "Disk"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Disk I/O"
|
||||
msgstr "Disk I/O"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Disk unit"
|
||||
@@ -548,7 +594,7 @@ msgstr "Dokumentace"
|
||||
|
||||
#. Context: System is down
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Down"
|
||||
@@ -566,11 +612,16 @@ msgstr "Stažení"
|
||||
msgid "Duration"
|
||||
msgstr "Doba trvání"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Edit"
|
||||
msgstr "Upravit"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Edit {foo}"
|
||||
msgstr "Upravit {foo}"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
#: src/components/login/otp-forms.tsx
|
||||
@@ -586,6 +637,11 @@ msgstr "Emailová upozornění"
|
||||
msgid "Empty"
|
||||
msgstr "Prázdná"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "End Time"
|
||||
msgstr "Čas ukončení"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Enter email address to reset password"
|
||||
msgstr "Zadejte e-mailovou adresu pro obnovu hesla"
|
||||
@@ -598,10 +654,16 @@ msgstr "Zadejte e-mailovou adresu..."
|
||||
msgid "Enter your one-time password."
|
||||
msgstr "Zadejte Vaše jednorázové heslo."
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Ephemeral"
|
||||
msgstr "Efemérní"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Error"
|
||||
@@ -626,6 +688,10 @@ msgstr "Stávající systémy, které nejsou definovány v <0>config.yml</0>, bu
|
||||
msgid "Exited active"
|
||||
msgstr "Ukončeno aktivně"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Expires after one hour or on hub restart."
|
||||
msgstr "Vyprší po jedné hodině nebo při restartu hubu."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr "Exportovat"
|
||||
@@ -656,6 +722,7 @@ msgstr "Ověření se nezdařilo"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Failed to save settings"
|
||||
msgstr "Nepodařilo se uložit nastavení"
|
||||
|
||||
@@ -687,7 +754,7 @@ msgstr "Otisk"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Firmware"
|
||||
msgstr "Firmware"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "For <0>{min}</0> {min, plural, one {minute} other {minutes}}"
|
||||
@@ -714,6 +781,10 @@ msgstr "Plná"
|
||||
msgid "General"
|
||||
msgstr "Obecné"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Global"
|
||||
msgstr "Globální"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "GPU Engines"
|
||||
msgstr "GPU enginy"
|
||||
@@ -758,15 +829,14 @@ msgctxt "Docker image"
|
||||
msgid "Image"
|
||||
msgstr "Obraz"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Inactive"
|
||||
msgstr "Neaktivní"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Invalid email address."
|
||||
msgstr "Neplatná e-mailová adresa."
|
||||
|
||||
#. Linux kernel
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Kernel"
|
||||
msgstr "Jádro"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "Jazyk"
|
||||
@@ -786,7 +856,7 @@ msgstr "Životní cyklus"
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "limit"
|
||||
msgstr "limit"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Load Average"
|
||||
@@ -859,6 +929,7 @@ msgid "Max 1 min"
|
||||
msgstr "Max. 1 min"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
@@ -885,7 +956,7 @@ msgstr "Využití paměti docker kontejnerů"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Model"
|
||||
msgstr "Model"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
@@ -959,12 +1030,19 @@ msgstr "Podpora OAuth 2 / OIDC"
|
||||
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
|
||||
msgstr "Při každém restartu budou systémy v databázi aktualizovány tak, aby odpovídaly systémům definovaným v souboru."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "One-time"
|
||||
msgstr "Jednorázové"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "One-time password"
|
||||
msgstr "Jednorázové heslo"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Open menu"
|
||||
msgstr "Otevřít menu"
|
||||
@@ -981,6 +1059,7 @@ msgstr "Jiné"
|
||||
msgid "Overwrite existing alerts"
|
||||
msgstr "Přepsat existující upozornění"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Page"
|
||||
@@ -1013,6 +1092,10 @@ msgstr "Heslo musí být menší než 72 bytů."
|
||||
msgid "Password reset request received"
|
||||
msgstr "Žádost o obnovu hesla byla přijata"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Past"
|
||||
msgstr "Minulé"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Pause"
|
||||
msgstr "Pozastavit"
|
||||
@@ -1034,6 +1117,14 @@ msgstr "Průměrné využití na jádro"
|
||||
msgid "Percentage of time spent in each state"
|
||||
msgstr "Procento času strávěného v každém stavu"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Permanent"
|
||||
msgstr "Trvalý"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Persistence"
|
||||
msgstr "Trvalost"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
msgstr "<0>nakonfigurujte SMTP server</0> pro zajištění toho, aby byla upozornění doručena."
|
||||
@@ -1069,7 +1160,7 @@ msgstr "Přihlaste se prosím k vašemu účtu"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Port"
|
||||
msgstr "Port"
|
||||
msgstr ""
|
||||
|
||||
#. Power On Time
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
@@ -1094,6 +1185,10 @@ msgstr "Proces spuštěn"
|
||||
msgid "Public Key"
|
||||
msgstr "Veřejný klíč"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Quiet Hours"
|
||||
msgstr "Tiché hodiny"
|
||||
|
||||
#. Disk read
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
@@ -1106,6 +1201,7 @@ msgstr "Přijato"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Refresh"
|
||||
msgstr "Aktualizovat"
|
||||
|
||||
@@ -1185,6 +1281,22 @@ msgstr "Uložit nastavení"
|
||||
msgid "Save system"
|
||||
msgstr "Uložit systém"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Saved in the database and does not expire until you disable it."
|
||||
msgstr "Uložen v databázi a nevyprší, dokud jej nezablokujete."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule"
|
||||
msgstr "Plán"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule quiet hours where notifications will not be sent, such as during maintenance periods."
|
||||
msgstr "Naplánujte tiché hodiny, kdy se nebudou odesílat oznámení, například během období údržby."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule quiet hours where notifications will not be sent."
|
||||
msgstr "Naplánujte tiché hodiny, kdy se nebudou odesílat oznámení."
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Search"
|
||||
msgstr "Hledat"
|
||||
@@ -1197,6 +1309,10 @@ msgstr "Hledat systémy nebo nastavení..."
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "Podívejte se na <0>nastavení upozornění</0> pro nastavení toho, jak přijímáte upozornění."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Select {foo}"
|
||||
msgstr "Vybrat {foo}"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "Odeslat"
|
||||
@@ -1240,8 +1356,14 @@ msgstr "Nastavení SMTP"
|
||||
msgid "Sort By"
|
||||
msgstr "Seřadit podle"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Start Time"
|
||||
msgstr "Čas začátku"
|
||||
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "Stav"
|
||||
@@ -1266,9 +1388,15 @@ msgstr "Swap prostor využívaný systémem"
|
||||
msgid "Swap Usage"
|
||||
msgstr "Swap využití"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "System"
|
||||
@@ -1353,8 +1481,8 @@ msgstr "Formát času"
|
||||
msgid "To email(s)"
|
||||
msgstr "Na email(y)"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "Toggle grid"
|
||||
msgstr "Přepnout mřížku"
|
||||
|
||||
@@ -1365,7 +1493,7 @@ msgstr "Přepnout motiv"
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Token"
|
||||
msgstr "Token"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
@@ -1423,6 +1551,10 @@ msgstr "Spustí se, když využití paměti během 5 minut překročí prahovou
|
||||
msgid "Triggers when any sensor exceeds a threshold"
|
||||
msgstr "Spustí se, když některý senzor překročí prahovou hodnotu"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when battery charge drops below a threshold"
|
||||
msgstr "Spustí se, když úroveň nabití baterie klesne pod prahovou hodnotu"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when combined up/down exceeds a threshold"
|
||||
msgstr "Spustí se, když kombinace up/down překročí prahovou hodnotu"
|
||||
@@ -1447,6 +1579,8 @@ msgstr "Spouští se, když se změní dostupnost"
|
||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||
msgstr "Spustí se, když využití disku překročí prahovou hodnotu"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Type"
|
||||
msgstr "Typ"
|
||||
@@ -1476,7 +1610,7 @@ msgid "Unlimited"
|
||||
msgstr "Neomezeno"
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Up"
|
||||
msgstr "Funkční"
|
||||
@@ -1485,7 +1619,12 @@ msgstr "Funkční"
|
||||
msgid "Up ({upSystemsLength})"
|
||||
msgstr "Funkční ({upSystemsLength})"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Update"
|
||||
msgstr "Aktualizovat"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Updated"
|
||||
msgstr "Aktualizováno"
|
||||
@@ -1498,7 +1637,7 @@ msgstr "Aktualizováno každých 10 minut."
|
||||
msgid "Upload"
|
||||
msgstr "Odeslání"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "Uptime"
|
||||
msgstr "Doba provozu"
|
||||
|
||||
@@ -1570,8 +1709,8 @@ msgid "Webhook / Push notifications"
|
||||
msgstr "Webhook / Push oznámení"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation. Expires after one hour or on hub restart."
|
||||
msgstr "Pokud je povoleno, tento token umožňuje agentům, aby se sami zaregistrovali bez předchozího vytvoření systému. Vyprší po jedné hodině nebo po restartu uzlu."
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation."
|
||||
msgstr "Pokud je povoleno, umožňuje tento token agentům samo-registraci bez předchozího vytvoření systému."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: da\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-10-28 23:00\n"
|
||||
"PO-Revision-Date: 2025-12-19 10:55\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Danish\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -24,6 +24,10 @@ msgstr ""
|
||||
msgid "{0} of {1} row(s) selected."
|
||||
msgstr "{0} af {1} række(r) valgt."
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "{cores, plural, one {# core} other {# cores}}"
|
||||
msgstr "{cores, plural, one {# kerne} other {# kerner}}"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "{count, plural, one {{countString} day} other {{countString} days}}"
|
||||
msgstr "{count, plural, one {{countString} dag} other {{countString} dage}}"
|
||||
@@ -36,6 +40,10 @@ msgstr "{count, plural, one {{countString} time} other {{countString} timer}}"
|
||||
msgid "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
|
||||
msgstr "{count, plural, one {{countString} minut} other {{countString} minutter}}"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "{threads, plural, one {# thread} other {# threads}}"
|
||||
msgstr "{threads, plural, one {# tråd} other {# tråde}}"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 hour"
|
||||
msgstr "1 time"
|
||||
@@ -76,13 +84,16 @@ msgid "5 min"
|
||||
msgstr "5 minutter"
|
||||
|
||||
#. Table column
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Actions"
|
||||
msgstr "Handlinger"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Active"
|
||||
msgstr "Aktiv"
|
||||
|
||||
@@ -95,12 +106,14 @@ msgid "Active state"
|
||||
msgstr "Aktiv tilstand"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add <0>System</0>"
|
||||
msgstr "Tilføj <0>System</0>"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Add {foo}"
|
||||
msgstr "Tilføj {foo}"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add New System"
|
||||
msgstr "Tilføj nyt system"
|
||||
msgid "Add <0>System</0>"
|
||||
msgstr "Tilføj <0>System</0>"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add system"
|
||||
@@ -151,6 +164,7 @@ msgstr "Alle containere"
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/home.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "All Systems"
|
||||
@@ -176,6 +190,11 @@ msgstr "Gennemsnitlig"
|
||||
msgid "Average CPU utilization of containers"
|
||||
msgstr "Gennemsnitlig CPU udnyttelse af containere"
|
||||
|
||||
#. placeholder {0}: alertData.unit
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "Average drops below <0>{value}{0}</0>"
|
||||
msgstr "Gennemsnit falder under <0>{value}{0}</0>"
|
||||
|
||||
#. placeholder {0}: alertData.unit
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "Average exceeds <0>{value}{0}</0>"
|
||||
@@ -208,7 +227,13 @@ msgstr "Sikkerhedskopier"
|
||||
msgid "Bandwidth"
|
||||
msgstr "Båndbredde"
|
||||
|
||||
#. Battery label in systems table header
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Bat"
|
||||
msgstr "Bat"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Battery"
|
||||
msgstr "Batteri"
|
||||
|
||||
@@ -224,6 +249,13 @@ msgstr "Blev inaktiv"
|
||||
msgid "Before"
|
||||
msgstr "Før"
|
||||
|
||||
#. placeholder {0}: alert.value
|
||||
#. placeholder {1}: info.unit
|
||||
#. placeholder {2}: alert.min
|
||||
#: src/components/active-alerts.tsx
|
||||
msgid "Below {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
|
||||
msgstr "Under {0}{1} i sidste {2, plural, one {# minut} other {# minutter}}"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Beszel supports OpenID Connect and many OAuth2 authentication providers."
|
||||
msgstr "Beszel understøtter OpenID Connect og mange OAuth2 godkendelsesudbydere."
|
||||
@@ -267,13 +299,14 @@ msgid "Can stop"
|
||||
msgstr "Kan stoppe"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Cancel"
|
||||
msgstr "Fortryd"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Capabilities"
|
||||
msgstr ""
|
||||
msgstr "Funktioner"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Capacity"
|
||||
@@ -320,6 +353,12 @@ msgstr "Tjek logfiler for flere detaljer."
|
||||
msgid "Check your notification service"
|
||||
msgstr "Tjek din notifikationstjeneste"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Clear"
|
||||
msgstr "Ryd"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
msgid "Click on a container to view more information."
|
||||
msgstr "Klik på en container for at se mere information."
|
||||
@@ -352,7 +391,7 @@ msgstr "Bekræft adgangskode"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Conflicts"
|
||||
msgstr ""
|
||||
msgstr "Konflikter"
|
||||
|
||||
#: src/components/active-alerts.tsx
|
||||
msgid "Connection is down"
|
||||
@@ -425,11 +464,11 @@ msgstr "CPU-kerner"
|
||||
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "CPU Peak"
|
||||
msgstr ""
|
||||
msgstr "CPU Peak"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "CPU time"
|
||||
msgstr ""
|
||||
msgstr "CPU tid"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "CPU Time Breakdown"
|
||||
@@ -442,6 +481,10 @@ msgstr "CPU-tidsfordeling"
|
||||
msgid "CPU Usage"
|
||||
msgstr "CPU forbrug"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Create"
|
||||
msgstr "Opret"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Create account"
|
||||
msgstr "Opret konto"
|
||||
@@ -473,15 +516,18 @@ msgstr "Nuværende tilstand"
|
||||
msgid "Cycles"
|
||||
msgstr "Cykler"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Dashboard"
|
||||
msgstr "Oversigtspanel"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Daily"
|
||||
msgstr "Dagligt"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Default time period"
|
||||
msgstr "Standard tidsperiode"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Delete"
|
||||
msgstr "Slet"
|
||||
@@ -548,7 +594,7 @@ msgstr "Dokumentation"
|
||||
|
||||
#. Context: System is down
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Down"
|
||||
@@ -566,11 +612,16 @@ msgstr "Hent ned"
|
||||
msgid "Duration"
|
||||
msgstr "Varighed"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Edit"
|
||||
msgstr "Rediger"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Edit {foo}"
|
||||
msgstr "Rediger {foo}"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
#: src/components/login/otp-forms.tsx
|
||||
@@ -586,6 +637,11 @@ msgstr "Email-notifikationer"
|
||||
msgid "Empty"
|
||||
msgstr "Tom"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "End Time"
|
||||
msgstr "Sluttid"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Enter email address to reset password"
|
||||
msgstr "Indtast e-mailadresse for at nulstille adgangskoden"
|
||||
@@ -598,10 +654,16 @@ msgstr "Indtast e-mailadresse..."
|
||||
msgid "Enter your one-time password."
|
||||
msgstr "Indtast din engangsadgangskode."
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Ephemeral"
|
||||
msgstr "Efemer"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Error"
|
||||
@@ -616,7 +678,7 @@ msgstr "Overskrider {0}{1} i sidste {2, plural, one {# minut} other {# minutter}
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Exec main PID"
|
||||
msgstr ""
|
||||
msgstr "Exec vigtigste PID"
|
||||
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
|
||||
@@ -626,6 +688,10 @@ msgstr "Eksisterende systemer ikke defineret i <0>config.yml</0> vil blive slett
|
||||
msgid "Exited active"
|
||||
msgstr "Afsluttet aktiv"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Expires after one hour or on hub restart."
|
||||
msgstr "Udløber efter en time eller ved hub-genstart."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr "Eksporter"
|
||||
@@ -656,6 +722,7 @@ msgstr "Kunne ikke godkende"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Failed to save settings"
|
||||
msgstr "Kunne ikke gemme indstillinger"
|
||||
|
||||
@@ -714,6 +781,10 @@ msgstr "Fuldt opladt"
|
||||
msgid "General"
|
||||
msgstr "Generelt"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Global"
|
||||
msgstr "Global"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "GPU Engines"
|
||||
msgstr "GPU-enheder"
|
||||
@@ -758,15 +829,14 @@ msgctxt "Docker image"
|
||||
msgid "Image"
|
||||
msgstr "Billede"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Inactive"
|
||||
msgstr "Inaktiv"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Invalid email address."
|
||||
msgstr "Ugyldig email adresse."
|
||||
|
||||
#. Linux kernel
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Kernel"
|
||||
msgstr "Kerne"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "Sprog"
|
||||
@@ -786,7 +856,7 @@ msgstr "Livscyklus"
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "limit"
|
||||
msgstr ""
|
||||
msgstr "grænse"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Load Average"
|
||||
@@ -842,7 +912,7 @@ msgstr "Leder du i stedet for efter hvor du kan oprette alarmer? Klik på klokke
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Main PID"
|
||||
msgstr ""
|
||||
msgstr "Primær PID"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Manage display and notification preferences."
|
||||
@@ -859,6 +929,7 @@ msgid "Max 1 min"
|
||||
msgstr "Maks. 1 min"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
@@ -872,7 +943,7 @@ msgstr "Hukommelsesgrænse"
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Memory Peak"
|
||||
msgstr ""
|
||||
msgstr "Hukommelsesspids"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/lib/alerts.ts
|
||||
@@ -898,7 +969,7 @@ msgstr "Navn"
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Net"
|
||||
msgstr ""
|
||||
msgstr "Net"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Network traffic of docker containers"
|
||||
@@ -959,12 +1030,19 @@ msgstr "OAuth 2 / OIDC understøttelse"
|
||||
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
|
||||
msgstr "Ved hver genstart vil systemer i databasen blive opdateret til at matche de systemer, der er defineret i filen."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "One-time"
|
||||
msgstr "Engangs"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "One-time password"
|
||||
msgstr "Engangsadgangskode"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Open menu"
|
||||
msgstr "Åbn menu"
|
||||
@@ -981,6 +1059,7 @@ msgstr "Andre"
|
||||
msgid "Overwrite existing alerts"
|
||||
msgstr "Overskriv eksisterende alarmer"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Page"
|
||||
@@ -1013,6 +1092,10 @@ msgstr "Adgangskoden skal være mindre end 72 bytes."
|
||||
msgid "Password reset request received"
|
||||
msgstr "Anmodning om nulstilling af adgangskode modtaget"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Past"
|
||||
msgstr "Tidligere"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Pause"
|
||||
msgstr "Pause"
|
||||
@@ -1034,6 +1117,14 @@ msgstr "Gennemsnitlig udnyttelse pr. kerne"
|
||||
msgid "Percentage of time spent in each state"
|
||||
msgstr "Procentdel af tid brugt i hver tilstand"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Permanent"
|
||||
msgstr "Permanent"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Persistence"
|
||||
msgstr "Vedholdenhed"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
msgstr "Konfigurer <0>en SMTP server</0> for at sikre at alarmer bliver leveret."
|
||||
@@ -1094,6 +1185,10 @@ msgstr "Proces startet"
|
||||
msgid "Public Key"
|
||||
msgstr "Offentlig nøgle"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Quiet Hours"
|
||||
msgstr "Stille timer"
|
||||
|
||||
#. Disk read
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
@@ -1106,6 +1201,7 @@ msgstr "Modtaget"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Refresh"
|
||||
msgstr "Opdater"
|
||||
|
||||
@@ -1150,7 +1246,7 @@ msgstr "Genoptag"
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgctxt "Root disk label"
|
||||
msgid "Root"
|
||||
msgstr ""
|
||||
msgstr "Root"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Rotate token"
|
||||
@@ -1185,6 +1281,22 @@ msgstr "Gem indstillinger"
|
||||
msgid "Save system"
|
||||
msgstr "Gem system"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Saved in the database and does not expire until you disable it."
|
||||
msgstr "Gemt i databasen og udløber ikke, før du deaktiverer det."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule"
|
||||
msgstr "Planlæg"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule quiet hours where notifications will not be sent, such as during maintenance periods."
|
||||
msgstr "Planlæg stille timer hvor meddelelser ikke vil blive sendt, såsom under vedligeholdelsesperioder."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule quiet hours where notifications will not be sent."
|
||||
msgstr "Planlæg stille timer hvor meddelelser ikke vil blive sendt."
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Search"
|
||||
msgstr "Søg"
|
||||
@@ -1197,6 +1309,10 @@ msgstr "Søg efter systemer eller indstillinger..."
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "Se <0>meddelelsesindstillinger</0> for at konfigurere, hvordan du modtager alarmer."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Select {foo}"
|
||||
msgstr "Vælg {foo}"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "Sendt"
|
||||
@@ -1240,8 +1356,14 @@ msgstr "SMTP-indstillinger"
|
||||
msgid "Sort By"
|
||||
msgstr "Sorter efter"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Start Time"
|
||||
msgstr "Starttid"
|
||||
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "Tilstand"
|
||||
@@ -1266,9 +1388,15 @@ msgstr "Swap plads brugt af systemet"
|
||||
msgid "Swap Usage"
|
||||
msgstr "Swap forbrug"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "System"
|
||||
@@ -1280,7 +1408,7 @@ msgstr "Gennemsnitlig system belastning over tid"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Systemd Services"
|
||||
msgstr ""
|
||||
msgstr "Systemd Services"
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Systems"
|
||||
@@ -1353,8 +1481,8 @@ msgstr "Tidsformat"
|
||||
msgid "To email(s)"
|
||||
msgstr "Til email(s)"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "Toggle grid"
|
||||
msgstr "Slå gitter til/fra"
|
||||
|
||||
@@ -1423,6 +1551,10 @@ msgstr "Udløser når 5 minut belastning gennemsnit overstiger en tærskel"
|
||||
msgid "Triggers when any sensor exceeds a threshold"
|
||||
msgstr "Udløser når en sensor overstiger en tærskel"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when battery charge drops below a threshold"
|
||||
msgstr "Udløses når batteriniveauet falder under en tærskel"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when combined up/down exceeds a threshold"
|
||||
msgstr "Udløses når de kombinerede op/ned overstiger en tærskel"
|
||||
@@ -1433,7 +1565,7 @@ msgstr "Udløser når CPU-forbrug overstiger en tærskel"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when GPU usage exceeds a threshold"
|
||||
msgstr ""
|
||||
msgstr "Udløses når GPU-brug overstiger en grænse"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when memory usage exceeds a threshold"
|
||||
@@ -1447,13 +1579,15 @@ msgstr "Udløser når status skifter mellem op og ned"
|
||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||
msgstr "Udløser når brugen af en disk overstiger en tærskel"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Type"
|
||||
msgstr "Type"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Unit file"
|
||||
msgstr ""
|
||||
msgstr "Enhed fil"
|
||||
|
||||
#. Temperature / network units
|
||||
#: src/components/routes/settings/general.tsx
|
||||
@@ -1473,10 +1607,10 @@ msgstr "Ukendt"
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Unlimited"
|
||||
msgstr ""
|
||||
msgstr "Ubegrænset"
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Up"
|
||||
msgstr "Oppe"
|
||||
@@ -1485,7 +1619,12 @@ msgstr "Oppe"
|
||||
msgid "Up ({upSystemsLength})"
|
||||
msgstr "Oppe ({upSystemsLength})"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Update"
|
||||
msgstr "Opdater"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Updated"
|
||||
msgstr "Opdateret"
|
||||
@@ -1498,7 +1637,7 @@ msgstr "Opdateret hver 10. minut."
|
||||
msgid "Upload"
|
||||
msgstr "Overfør"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "Uptime"
|
||||
msgstr "Oppetid"
|
||||
|
||||
@@ -1570,8 +1709,8 @@ msgid "Webhook / Push notifications"
|
||||
msgstr "Webhook / Push notifikationer"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation. Expires after one hour or on hub restart."
|
||||
msgstr "Når aktiveret tillader denne nøgle agenter at selvregistrere uden forudgående systemoprettelse. Udløber efter en time eller ved hub-genstart."
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation."
|
||||
msgstr "Når aktiveret, tillader denne token agenter at registrere sig selv uden forudgående systemoprettelse."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: de\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-10-28 22:59\n"
|
||||
"PO-Revision-Date: 2025-12-05 20:24\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: German\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -24,6 +24,10 @@ msgstr ""
|
||||
msgid "{0} of {1} row(s) selected."
|
||||
msgstr "{0} von {1} Zeile(n) ausgewählt."
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "{cores, plural, one {# core} other {# cores}}"
|
||||
msgstr "{cores, plural, one {# Kern} other {# Kerne}}"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "{count, plural, one {{countString} day} other {{countString} days}}"
|
||||
msgstr "{count, plural, one {{countString} Tag} other {{countString} Tage}}"
|
||||
@@ -36,6 +40,10 @@ msgstr "{count, plural, one {{countString} Stunde} other {{countString} Stunden}
|
||||
msgid "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
|
||||
msgstr "{count, plural, one {{countString} Minute} other {{countString} Minuten}}"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "{threads, plural, one {# thread} other {# threads}}"
|
||||
msgstr "{threads, plural, one {# Thread} other {# Threads}}"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 hour"
|
||||
msgstr "1 Stunde"
|
||||
@@ -76,13 +84,16 @@ msgid "5 min"
|
||||
msgstr "5 Min"
|
||||
|
||||
#. Table column
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Actions"
|
||||
msgstr "Aktionen"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Active"
|
||||
msgstr "Aktiv"
|
||||
|
||||
@@ -95,12 +106,14 @@ msgid "Active state"
|
||||
msgstr "Aktiver Zustand"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add <0>System</0>"
|
||||
msgstr "<0>System</0> hinzufügen"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Add {foo}"
|
||||
msgstr "{foo} hinzufügen"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add New System"
|
||||
msgstr "Neues System hinzufügen"
|
||||
msgid "Add <0>System</0>"
|
||||
msgstr "<0>System</0> hinzufügen"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add system"
|
||||
@@ -151,6 +164,7 @@ msgstr "Alle Container"
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/home.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "All Systems"
|
||||
@@ -176,6 +190,11 @@ msgstr "Durchschnitt"
|
||||
msgid "Average CPU utilization of containers"
|
||||
msgstr "Durchschnittliche CPU-Auslastung der Container"
|
||||
|
||||
#. placeholder {0}: alertData.unit
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "Average drops below <0>{value}{0}</0>"
|
||||
msgstr "Durchschnitt unterschreitet <0>{value}{0}</0>"
|
||||
|
||||
#. placeholder {0}: alertData.unit
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "Average exceeds <0>{value}{0}</0>"
|
||||
@@ -208,7 +227,13 @@ msgstr "Backups"
|
||||
msgid "Bandwidth"
|
||||
msgstr "Bandbreite"
|
||||
|
||||
#. Battery label in systems table header
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Bat"
|
||||
msgstr "Bat"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Battery"
|
||||
msgstr "Batterie"
|
||||
|
||||
@@ -224,6 +249,13 @@ msgstr "Wurde inaktiv"
|
||||
msgid "Before"
|
||||
msgstr "Vor"
|
||||
|
||||
#. placeholder {0}: alert.value
|
||||
#. placeholder {1}: info.unit
|
||||
#. placeholder {2}: alert.min
|
||||
#: src/components/active-alerts.tsx
|
||||
msgid "Below {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
|
||||
msgstr "Unterschreitet {0}{1} in den letzten {2, plural, one {# Minute} other {# Minuten}}"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Beszel supports OpenID Connect and many OAuth2 authentication providers."
|
||||
msgstr "Beszel unterstützt OpenID Connect und viele OAuth2-Authentifizierungsanbieter."
|
||||
@@ -267,6 +299,7 @@ msgid "Can stop"
|
||||
msgstr "Kann stoppen"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Cancel"
|
||||
msgstr "Abbrechen"
|
||||
@@ -320,6 +353,12 @@ msgstr "Überprüfe die Protokolle für weitere Details."
|
||||
msgid "Check your notification service"
|
||||
msgstr "Überprüfe deinen Benachrichtigungsdienst"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Clear"
|
||||
msgstr "Löschen"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
msgid "Click on a container to view more information."
|
||||
msgstr "Klicke auf einen Container, um weitere Informationen zu sehen."
|
||||
@@ -442,6 +481,10 @@ msgstr "CPU-Zeit-Aufschlüsselung"
|
||||
msgid "CPU Usage"
|
||||
msgstr "CPU-Auslastung"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Create"
|
||||
msgstr "Erstellen"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Create account"
|
||||
msgstr "Konto erstellen"
|
||||
@@ -473,15 +516,18 @@ msgstr "Aktueller Zustand"
|
||||
msgid "Cycles"
|
||||
msgstr "Zyklen"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Dashboard"
|
||||
msgstr "Dashboard"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Daily"
|
||||
msgstr "Täglich"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Default time period"
|
||||
msgstr "Standardzeitraum"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Delete"
|
||||
msgstr "Löschen"
|
||||
@@ -548,7 +594,7 @@ msgstr "Dokumentation"
|
||||
|
||||
#. Context: System is down
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Down"
|
||||
@@ -566,11 +612,16 @@ msgstr "Herunterladen"
|
||||
msgid "Duration"
|
||||
msgstr "Dauer"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Edit"
|
||||
msgstr "Bearbeiten"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Edit {foo}"
|
||||
msgstr "{foo} bearbeiten"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
#: src/components/login/otp-forms.tsx
|
||||
@@ -586,6 +637,11 @@ msgstr "E-Mail-Benachrichtigungen"
|
||||
msgid "Empty"
|
||||
msgstr "Leer"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "End Time"
|
||||
msgstr "Endzeit"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Enter email address to reset password"
|
||||
msgstr "E-Mail-Adresse eingeben, um das Passwort zurückzusetzen"
|
||||
@@ -598,10 +654,16 @@ msgstr "E-Mail-Adresse eingeben..."
|
||||
msgid "Enter your one-time password."
|
||||
msgstr "Geben Sie Ihr Einmalpasswort ein."
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Ephemeral"
|
||||
msgstr "Flüchtig"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Error"
|
||||
@@ -626,6 +688,10 @@ msgstr "Bestehende Systeme, die nicht in der <0>config.yml</0> definiert sind, w
|
||||
msgid "Exited active"
|
||||
msgstr "Beendet aktiv"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Expires after one hour or on hub restart."
|
||||
msgstr "Läuft nach einer Stunde oder bei Hub-Neustart ab."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr "Exportieren"
|
||||
@@ -656,6 +722,7 @@ msgstr "Authentifizierung fehlgeschlagen"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Failed to save settings"
|
||||
msgstr "Einstellungen konnten nicht gespeichert werden"
|
||||
|
||||
@@ -714,6 +781,10 @@ msgstr "Voll"
|
||||
msgid "General"
|
||||
msgstr "Allgemein"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Global"
|
||||
msgstr "Global"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "GPU Engines"
|
||||
msgstr "GPU-Engines"
|
||||
@@ -758,15 +829,14 @@ msgctxt "Docker image"
|
||||
msgid "Image"
|
||||
msgstr "Image"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Inactive"
|
||||
msgstr "Inaktiv"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Invalid email address."
|
||||
msgstr "Ungültige E-Mail-Adresse."
|
||||
|
||||
#. Linux kernel
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Kernel"
|
||||
msgstr "Kernel"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "Sprache"
|
||||
@@ -859,6 +929,7 @@ msgid "Max 1 min"
|
||||
msgstr "Max 1 Min"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
@@ -959,12 +1030,19 @@ msgstr "OAuth 2 / OIDC-Unterstützung"
|
||||
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
|
||||
msgstr "Bei jedem Neustart werden die Systeme in der Datenbank aktualisiert, um den in der Datei definierten Systemen zu entsprechen."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "One-time"
|
||||
msgstr "Einmalig"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "One-time password"
|
||||
msgstr "Einmalpasswort"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Open menu"
|
||||
msgstr "Menü öffnen"
|
||||
@@ -981,6 +1059,7 @@ msgstr "Andere"
|
||||
msgid "Overwrite existing alerts"
|
||||
msgstr "Bestehende Warnungen überschreiben"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Page"
|
||||
@@ -1013,6 +1092,10 @@ msgstr "Das Passwort muss weniger als 72 Bytes lang sein."
|
||||
msgid "Password reset request received"
|
||||
msgstr "Anfrage zum Zurücksetzen des Passworts erhalten"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Past"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Pause"
|
||||
msgstr "Pause"
|
||||
@@ -1034,6 +1117,14 @@ msgstr "Durchschnittliche Auslastung pro Kern"
|
||||
msgid "Percentage of time spent in each state"
|
||||
msgstr "Prozentsatz der Zeit in jedem Zustand"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Permanent"
|
||||
msgstr "Permanent"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Persistence"
|
||||
msgstr "Persistenz"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
msgstr "Bitte <0>konfiguriere einen SMTP-Server</0>, um sicherzustellen, dass Warnungen zugestellt werden."
|
||||
@@ -1094,6 +1185,10 @@ msgstr "Prozess gestartet"
|
||||
msgid "Public Key"
|
||||
msgstr "Öffentlicher Schlüssel"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Quiet Hours"
|
||||
msgstr "Ruhezeiten"
|
||||
|
||||
#. Disk read
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
@@ -1106,6 +1201,7 @@ msgstr "Empfangen"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Refresh"
|
||||
msgstr "Aktualisieren"
|
||||
|
||||
@@ -1150,7 +1246,7 @@ msgstr "Fortsetzen"
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgctxt "Root disk label"
|
||||
msgid "Root"
|
||||
msgstr "Root"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Rotate token"
|
||||
@@ -1185,6 +1281,22 @@ msgstr "Einstellungen speichern"
|
||||
msgid "Save system"
|
||||
msgstr "System speichern"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Saved in the database and does not expire until you disable it."
|
||||
msgstr "In der Datenbank gespeichert und läuft nicht ab, bis Sie es deaktivieren."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule"
|
||||
msgstr "Zeitplan"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule quiet hours where notifications will not be sent, such as during maintenance periods."
|
||||
msgstr "Plane Ruhezeiten, in denen keine Benachrichtigungen gesendet werden, z. B. während Wartungszeiten."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule quiet hours where notifications will not be sent."
|
||||
msgstr "Plane Ruhezeiten, in denen keine Benachrichtigungen gesendet werden."
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Search"
|
||||
msgstr "Suche"
|
||||
@@ -1197,6 +1309,10 @@ msgstr "Nach Systemen oder Einstellungen suchen..."
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "Siehe <0>Benachrichtigungseinstellungen</0>, um zu konfigurieren, wie du Warnungen erhältst."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Select {foo}"
|
||||
msgstr "Auswählen {foo}"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "Gesendet"
|
||||
@@ -1240,8 +1356,14 @@ msgstr "SMTP-Einstellungen"
|
||||
msgid "Sort By"
|
||||
msgstr "Sortieren nach"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Start Time"
|
||||
msgstr "Startzeit"
|
||||
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "Status"
|
||||
@@ -1266,9 +1388,15 @@ msgstr "Vom System genutzter Swap-Speicher"
|
||||
msgid "Swap Usage"
|
||||
msgstr "Swap-Nutzung"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "System"
|
||||
@@ -1353,8 +1481,8 @@ msgstr "Zeitformat"
|
||||
msgid "To email(s)"
|
||||
msgstr "An E-Mail(s)"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "Toggle grid"
|
||||
msgstr "Raster umschalten"
|
||||
|
||||
@@ -1423,6 +1551,10 @@ msgstr "Löst aus, wenn der Lastdurchschnitt der letzten 5 Minuten einen Schwell
|
||||
msgid "Triggers when any sensor exceeds a threshold"
|
||||
msgstr "Löst aus, wenn ein Sensor einen Schwellenwert überschreitet"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when battery charge drops below a threshold"
|
||||
msgstr "Löst aus, wenn der Batterieladestand unter einen Schwellenwert fällt"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when combined up/down exceeds a threshold"
|
||||
msgstr "Löst aus, wenn die kombinierte Up- und Downloadrate einen Schwellenwert überschreitet"
|
||||
@@ -1447,6 +1579,8 @@ msgstr "Löst aus, wenn der Status zwischen online und offline wechselt"
|
||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||
msgstr "Löst aus, wenn die Nutzung einer Festplatte einen Schwellenwert überschreitet"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Type"
|
||||
msgstr "Typ"
|
||||
@@ -1476,7 +1610,7 @@ msgid "Unlimited"
|
||||
msgstr "Unbegrenzt"
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Up"
|
||||
msgstr "aktiv"
|
||||
@@ -1485,7 +1619,12 @@ msgstr "aktiv"
|
||||
msgid "Up ({upSystemsLength})"
|
||||
msgstr "aktiv ({upSystemsLength})"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Update"
|
||||
msgstr "Aktualisieren"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Updated"
|
||||
msgstr "Aktualisiert"
|
||||
@@ -1498,7 +1637,7 @@ msgstr "Alle 10 Minuten aktualisiert."
|
||||
msgid "Upload"
|
||||
msgstr "Hochladen"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "Uptime"
|
||||
msgstr "Betriebszeit"
|
||||
|
||||
@@ -1570,8 +1709,8 @@ msgid "Webhook / Push notifications"
|
||||
msgstr "Webhook / Push-Benachrichtigungen"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation. Expires after one hour or on hub restart."
|
||||
msgstr "Wenn aktiviert, ermöglicht dieser Token Agents, sich selbst zu registrieren, ohne vorherige Systemerstellung. Läuft nach einer Stunde oder beim Hub-Neustart ab."
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation."
|
||||
msgstr "Wenn aktiviert, ermöglicht dieser Token Agenten die Selbstregistrierung ohne vorherige Systemerstellung."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -19,6 +19,10 @@ msgstr ""
|
||||
msgid "{0} of {1} row(s) selected."
|
||||
msgstr "{0} of {1} row(s) selected."
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "{cores, plural, one {# core} other {# cores}}"
|
||||
msgstr "{cores, plural, one {# core} other {# cores}}"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "{count, plural, one {{countString} day} other {{countString} days}}"
|
||||
msgstr "{count, plural, one {{countString} day} other {{countString} days}}"
|
||||
@@ -31,6 +35,10 @@ msgstr "{count, plural, one {{countString} hour} other {{countString} hours}}"
|
||||
msgid "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
|
||||
msgstr "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "{threads, plural, one {# thread} other {# threads}}"
|
||||
msgstr "{threads, plural, one {# thread} other {# threads}}"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 hour"
|
||||
msgstr "1 hour"
|
||||
@@ -71,13 +79,16 @@ msgid "5 min"
|
||||
msgstr "5 min"
|
||||
|
||||
#. Table column
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Actions"
|
||||
msgstr "Actions"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Active"
|
||||
msgstr "Active"
|
||||
|
||||
@@ -90,12 +101,14 @@ msgid "Active state"
|
||||
msgstr "Active state"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add <0>System</0>"
|
||||
msgstr "Add <0>System</0>"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Add {foo}"
|
||||
msgstr "Add {foo}"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add New System"
|
||||
msgstr "Add New System"
|
||||
msgid "Add <0>System</0>"
|
||||
msgstr "Add <0>System</0>"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add system"
|
||||
@@ -146,6 +159,7 @@ msgstr "All Containers"
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/home.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "All Systems"
|
||||
@@ -171,6 +185,11 @@ msgstr "Average"
|
||||
msgid "Average CPU utilization of containers"
|
||||
msgstr "Average CPU utilization of containers"
|
||||
|
||||
#. placeholder {0}: alertData.unit
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "Average drops below <0>{value}{0}</0>"
|
||||
msgstr "Average drops below <0>{value}{0}</0>"
|
||||
|
||||
#. placeholder {0}: alertData.unit
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "Average exceeds <0>{value}{0}</0>"
|
||||
@@ -203,7 +222,13 @@ msgstr "Backups"
|
||||
msgid "Bandwidth"
|
||||
msgstr "Bandwidth"
|
||||
|
||||
#. Battery label in systems table header
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Bat"
|
||||
msgstr "Bat"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Battery"
|
||||
msgstr "Battery"
|
||||
|
||||
@@ -219,6 +244,13 @@ msgstr "Became inactive"
|
||||
msgid "Before"
|
||||
msgstr "Before"
|
||||
|
||||
#. placeholder {0}: alert.value
|
||||
#. placeholder {1}: info.unit
|
||||
#. placeholder {2}: alert.min
|
||||
#: src/components/active-alerts.tsx
|
||||
msgid "Below {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
|
||||
msgstr "Below {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Beszel supports OpenID Connect and many OAuth2 authentication providers."
|
||||
msgstr "Beszel supports OpenID Connect and many OAuth2 authentication providers."
|
||||
@@ -262,6 +294,7 @@ msgid "Can stop"
|
||||
msgstr "Can stop"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Cancel"
|
||||
msgstr "Cancel"
|
||||
@@ -315,6 +348,12 @@ msgstr "Check logs for more details."
|
||||
msgid "Check your notification service"
|
||||
msgstr "Check your notification service"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Clear"
|
||||
msgstr "Clear"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
msgid "Click on a container to view more information."
|
||||
msgstr "Click on a container to view more information."
|
||||
@@ -437,6 +476,10 @@ msgstr "CPU Time Breakdown"
|
||||
msgid "CPU Usage"
|
||||
msgstr "CPU Usage"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Create"
|
||||
msgstr "Create"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Create account"
|
||||
msgstr "Create account"
|
||||
@@ -468,15 +511,18 @@ msgstr "Current state"
|
||||
msgid "Cycles"
|
||||
msgstr "Cycles"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Dashboard"
|
||||
msgstr "Dashboard"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Daily"
|
||||
msgstr "Daily"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Default time period"
|
||||
msgstr "Default time period"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Delete"
|
||||
msgstr "Delete"
|
||||
@@ -543,7 +589,7 @@ msgstr "Documentation"
|
||||
|
||||
#. Context: System is down
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Down"
|
||||
@@ -561,11 +607,16 @@ msgstr "Download"
|
||||
msgid "Duration"
|
||||
msgstr "Duration"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Edit"
|
||||
msgstr "Edit"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Edit {foo}"
|
||||
msgstr "Edit {foo}"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
#: src/components/login/otp-forms.tsx
|
||||
@@ -581,6 +632,11 @@ msgstr "Email notifications"
|
||||
msgid "Empty"
|
||||
msgstr "Empty"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "End Time"
|
||||
msgstr "End Time"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Enter email address to reset password"
|
||||
msgstr "Enter email address to reset password"
|
||||
@@ -593,10 +649,16 @@ msgstr "Enter email address..."
|
||||
msgid "Enter your one-time password."
|
||||
msgstr "Enter your one-time password."
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Ephemeral"
|
||||
msgstr "Ephemeral"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Error"
|
||||
@@ -621,6 +683,10 @@ msgstr "Existing systems not defined in <0>config.yml</0> will be deleted. Pleas
|
||||
msgid "Exited active"
|
||||
msgstr "Exited active"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Expires after one hour or on hub restart."
|
||||
msgstr "Expires after one hour or on hub restart."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr "Export"
|
||||
@@ -651,6 +717,7 @@ msgstr "Failed to authenticate"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Failed to save settings"
|
||||
msgstr "Failed to save settings"
|
||||
|
||||
@@ -709,6 +776,10 @@ msgstr "Full"
|
||||
msgid "General"
|
||||
msgstr "General"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Global"
|
||||
msgstr "Global"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "GPU Engines"
|
||||
msgstr "GPU Engines"
|
||||
@@ -753,15 +824,14 @@ msgctxt "Docker image"
|
||||
msgid "Image"
|
||||
msgstr "Image"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Inactive"
|
||||
msgstr "Inactive"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Invalid email address."
|
||||
msgstr "Invalid email address."
|
||||
|
||||
#. Linux kernel
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Kernel"
|
||||
msgstr "Kernel"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "Language"
|
||||
@@ -854,6 +924,7 @@ msgid "Max 1 min"
|
||||
msgstr "Max 1 min"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
@@ -954,12 +1025,19 @@ msgstr "OAuth 2 / OIDC support"
|
||||
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."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "One-time"
|
||||
msgstr "One-time"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "One-time password"
|
||||
msgstr "One-time password"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Open menu"
|
||||
msgstr "Open menu"
|
||||
@@ -976,6 +1054,7 @@ msgstr "Other"
|
||||
msgid "Overwrite existing alerts"
|
||||
msgstr "Overwrite existing alerts"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Page"
|
||||
@@ -1008,6 +1087,10 @@ msgstr "Password must be less than 72 bytes."
|
||||
msgid "Password reset request received"
|
||||
msgstr "Password reset request received"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Past"
|
||||
msgstr "Past"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Pause"
|
||||
msgstr "Pause"
|
||||
@@ -1029,6 +1112,14 @@ msgstr "Per-core average utilization"
|
||||
msgid "Percentage of time spent in each state"
|
||||
msgstr "Percentage of time spent in each state"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Permanent"
|
||||
msgstr "Permanent"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Persistence"
|
||||
msgstr "Persistence"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
msgstr "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
@@ -1089,6 +1180,10 @@ msgstr "Process started"
|
||||
msgid "Public Key"
|
||||
msgstr "Public Key"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Quiet Hours"
|
||||
msgstr "Quiet Hours"
|
||||
|
||||
#. Disk read
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
@@ -1101,6 +1196,7 @@ msgstr "Received"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Refresh"
|
||||
msgstr "Refresh"
|
||||
|
||||
@@ -1180,6 +1276,22 @@ msgstr "Save Settings"
|
||||
msgid "Save system"
|
||||
msgstr "Save system"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Saved in the database and does not expire until you disable it."
|
||||
msgstr "Saved in the database and does not expire until you disable it."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule"
|
||||
msgstr "Schedule"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule quiet hours where notifications will not be sent, such as during maintenance periods."
|
||||
msgstr "Schedule quiet hours where notifications will not be sent, such as during maintenance periods."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule quiet hours where notifications will not be sent."
|
||||
msgstr "Schedule quiet hours where notifications will not be sent."
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Search"
|
||||
msgstr "Search"
|
||||
@@ -1192,6 +1304,10 @@ msgstr "Search for systems or settings..."
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "See <0>notification settings</0> to configure how you receive alerts."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Select {foo}"
|
||||
msgstr "Select {foo}"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "Sent"
|
||||
@@ -1235,8 +1351,14 @@ msgstr "SMTP settings"
|
||||
msgid "Sort By"
|
||||
msgstr "Sort By"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Start Time"
|
||||
msgstr "Start Time"
|
||||
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "State"
|
||||
@@ -1261,9 +1383,15 @@ msgstr "Swap space used by the system"
|
||||
msgid "Swap Usage"
|
||||
msgstr "Swap Usage"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "System"
|
||||
@@ -1348,8 +1476,8 @@ msgstr "Time format"
|
||||
msgid "To email(s)"
|
||||
msgstr "To email(s)"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "Toggle grid"
|
||||
msgstr "Toggle grid"
|
||||
|
||||
@@ -1418,6 +1546,10 @@ msgstr "Triggers when 5 minute load average exceeds a threshold"
|
||||
msgid "Triggers when any sensor exceeds a threshold"
|
||||
msgstr "Triggers when any sensor exceeds a threshold"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when battery charge drops below a threshold"
|
||||
msgstr "Triggers when battery charge drops below a threshold"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when combined up/down exceeds a threshold"
|
||||
msgstr "Triggers when combined up/down exceeds a threshold"
|
||||
@@ -1442,6 +1574,8 @@ msgstr "Triggers when status switches between up and down"
|
||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||
msgstr "Triggers when usage of any disk exceeds a threshold"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Type"
|
||||
msgstr "Type"
|
||||
@@ -1471,7 +1605,7 @@ msgid "Unlimited"
|
||||
msgstr "Unlimited"
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Up"
|
||||
msgstr "Up"
|
||||
@@ -1480,7 +1614,12 @@ msgstr "Up"
|
||||
msgid "Up ({upSystemsLength})"
|
||||
msgstr "Up ({upSystemsLength})"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Update"
|
||||
msgstr "Update"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Updated"
|
||||
msgstr "Updated"
|
||||
@@ -1493,7 +1632,7 @@ msgstr "Updated every 10 minutes."
|
||||
msgid "Upload"
|
||||
msgstr "Upload"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "Uptime"
|
||||
msgstr "Uptime"
|
||||
|
||||
@@ -1565,8 +1704,8 @@ msgid "Webhook / Push notifications"
|
||||
msgstr "Webhook / Push notifications"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation. Expires after one hour or on hub restart."
|
||||
msgstr "When enabled, this token allows agents to self-register without prior system creation. Expires after one hour or on hub restart."
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation."
|
||||
msgstr "When enabled, this token allows agents to self-register without prior system creation."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: es\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-11-04 22:13\n"
|
||||
"PO-Revision-Date: 2025-12-14 09:39\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Spanish\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -24,6 +24,10 @@ msgstr ""
|
||||
msgid "{0} of {1} row(s) selected."
|
||||
msgstr "{0} de {1} fila(s) seleccionada(s)."
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "{cores, plural, one {# core} other {# cores}}"
|
||||
msgstr "{cores, plural, one {# núcleo} other {# núcleos}}"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "{count, plural, one {{countString} day} other {{countString} days}}"
|
||||
msgstr "{count, plural, one {{countString} día} other {{countString} días}}"
|
||||
@@ -36,6 +40,10 @@ msgstr "{count, plural, one {{countString} hora} other {{countString} horas}}"
|
||||
msgid "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
|
||||
msgstr "{count, plural, one {{countString} minuto} other {{countString} minutos}}"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "{threads, plural, one {# thread} other {# threads}}"
|
||||
msgstr "{threads, plural, one {# hilo} other {# hilos}}"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 hour"
|
||||
msgstr "1 hora"
|
||||
@@ -76,13 +84,16 @@ msgid "5 min"
|
||||
msgstr "5 min"
|
||||
|
||||
#. Table column
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Actions"
|
||||
msgstr "Acciones"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Active"
|
||||
msgstr "Activo"
|
||||
|
||||
@@ -95,12 +106,14 @@ msgid "Active state"
|
||||
msgstr "Estado activo"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add <0>System</0>"
|
||||
msgstr "Agregar <0>sistema</0>"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Add {foo}"
|
||||
msgstr "Agregar {foo}"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add New System"
|
||||
msgstr "Agregar nuevo sistema"
|
||||
msgid "Add <0>System</0>"
|
||||
msgstr "Agregar <0>sistema</0>"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add system"
|
||||
@@ -151,6 +164,7 @@ msgstr "Todos los contenedores"
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/home.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "All Systems"
|
||||
@@ -176,6 +190,11 @@ msgstr "Promedio"
|
||||
msgid "Average CPU utilization of containers"
|
||||
msgstr "Utilización promedio de CPU de los contenedores"
|
||||
|
||||
#. placeholder {0}: alertData.unit
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "Average drops below <0>{value}{0}</0>"
|
||||
msgstr "El promedio cae por debajo de <0>{value}{0}</0>"
|
||||
|
||||
#. placeholder {0}: alertData.unit
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "Average exceeds <0>{value}{0}</0>"
|
||||
@@ -208,7 +227,13 @@ msgstr "Copias de seguridad"
|
||||
msgid "Bandwidth"
|
||||
msgstr "Ancho de banda"
|
||||
|
||||
#. Battery label in systems table header
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Bat"
|
||||
msgstr "Bat"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Battery"
|
||||
msgstr "Batería"
|
||||
|
||||
@@ -224,6 +249,13 @@ msgstr "Se desactivó"
|
||||
msgid "Before"
|
||||
msgstr "Antes"
|
||||
|
||||
#. placeholder {0}: alert.value
|
||||
#. placeholder {1}: info.unit
|
||||
#. placeholder {2}: alert.min
|
||||
#: src/components/active-alerts.tsx
|
||||
msgid "Below {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
|
||||
msgstr "Por debajo de {0}{1} en el último {2, plural, one {# minuto} other {# minutos}}"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Beszel supports OpenID Connect and many OAuth2 authentication providers."
|
||||
msgstr "Beszel admite OpenID Connect y muchos proveedores de autenticación OAuth2."
|
||||
@@ -256,17 +288,18 @@ msgstr "Caché / Buffers"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Can reload"
|
||||
msgstr "Puede recargar"
|
||||
msgstr "Puede recargarse"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Can start"
|
||||
msgstr "Puede iniciar"
|
||||
msgstr "Puede iniciarse"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Can stop"
|
||||
msgstr "Puede detener"
|
||||
msgstr "Puede detenerse"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Cancel"
|
||||
msgstr "Cancelar"
|
||||
@@ -320,9 +353,15 @@ msgstr "Revisa los registros para más detalles."
|
||||
msgid "Check your notification service"
|
||||
msgstr "Verifica tu servicio de notificaciones"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Clear"
|
||||
msgstr "Limpiar"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
msgid "Click on a container to view more information."
|
||||
msgstr "Haga clic en un contenedor para ver más información."
|
||||
msgstr "Haz clic en un contenedor para ver más información."
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Click on a device to view more information."
|
||||
@@ -442,6 +481,10 @@ msgstr "Desglose de tiempo de CPU"
|
||||
msgid "CPU Usage"
|
||||
msgstr "Uso de CPU"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Create"
|
||||
msgstr "Crear"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Create account"
|
||||
msgstr "Crear cuenta"
|
||||
@@ -473,15 +516,18 @@ msgstr "Estado actual"
|
||||
msgid "Cycles"
|
||||
msgstr "Ciclos"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Dashboard"
|
||||
msgstr "Tablero"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Daily"
|
||||
msgstr "Diariamente"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Default time period"
|
||||
msgstr "Periodo de tiempo predeterminado"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Delete"
|
||||
msgstr "Eliminar"
|
||||
@@ -548,7 +594,7 @@ msgstr "Documentación"
|
||||
|
||||
#. Context: System is down
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Down"
|
||||
@@ -566,11 +612,16 @@ msgstr "Descargar"
|
||||
msgid "Duration"
|
||||
msgstr "Duración"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Edit"
|
||||
msgstr "Editar"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Edit {foo}"
|
||||
msgstr "Editar {foo}"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
#: src/components/login/otp-forms.tsx
|
||||
@@ -586,6 +637,11 @@ msgstr "Notificaciones por correo"
|
||||
msgid "Empty"
|
||||
msgstr "Vacía"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "End Time"
|
||||
msgstr "Hora de finalización"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Enter email address to reset password"
|
||||
msgstr "Ingresa la dirección de correo electrónico para restablecer la contraseña"
|
||||
@@ -598,10 +654,16 @@ msgstr "Ingresa dirección de correo..."
|
||||
msgid "Enter your one-time password."
|
||||
msgstr "Ingrese su contraseña de un solo uso."
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Ephemeral"
|
||||
msgstr "Efímero"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Error"
|
||||
@@ -626,6 +688,10 @@ msgstr "Los sistemas existentes no definidos en <0>config.yml</0> serán elimina
|
||||
msgid "Exited active"
|
||||
msgstr "Salió activo"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Expires after one hour or on hub restart."
|
||||
msgstr "Expira después de una hora o al reiniciar el hub."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr "Exportar"
|
||||
@@ -656,6 +722,7 @@ msgstr "Error al autenticar"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Failed to save settings"
|
||||
msgstr "Error al guardar la configuración"
|
||||
|
||||
@@ -714,6 +781,10 @@ msgstr "Llena"
|
||||
msgid "General"
|
||||
msgstr "General"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Global"
|
||||
msgstr "Global"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "GPU Engines"
|
||||
msgstr "Motores GPU"
|
||||
@@ -758,15 +829,14 @@ msgctxt "Docker image"
|
||||
msgid "Image"
|
||||
msgstr "Imagen"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Inactive"
|
||||
msgstr "Inactivo"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Invalid email address."
|
||||
msgstr "Dirección de correo electrónico no válida."
|
||||
|
||||
#. Linux kernel
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Kernel"
|
||||
msgstr "Kernel"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "Idioma"
|
||||
@@ -859,6 +929,7 @@ msgid "Max 1 min"
|
||||
msgstr "Máx. 1 min"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
@@ -959,12 +1030,19 @@ msgstr "Soporte para OAuth 2 / OIDC"
|
||||
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
|
||||
msgstr "En cada reinicio, los sistemas en la base de datos se actualizarán para coincidir con los sistemas definidos en el archivo."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "One-time"
|
||||
msgstr "Una vez"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "One-time password"
|
||||
msgstr "Contraseña de un solo uso"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Open menu"
|
||||
msgstr "Abrir menú"
|
||||
@@ -981,6 +1059,7 @@ msgstr "Otro"
|
||||
msgid "Overwrite existing alerts"
|
||||
msgstr "Sobrescribir alertas existentes"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Page"
|
||||
@@ -1013,6 +1092,10 @@ msgstr "La contraseña debe ser menor de 72 bytes."
|
||||
msgid "Password reset request received"
|
||||
msgstr "Solicitud de restablecimiento de contraseña recibida"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Past"
|
||||
msgstr "Pasado"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Pause"
|
||||
msgstr "Pausar"
|
||||
@@ -1034,6 +1117,14 @@ msgstr "Uso promedio por núcleo"
|
||||
msgid "Percentage of time spent in each state"
|
||||
msgstr "Porcentaje de tiempo dedicado a cada estado"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Permanent"
|
||||
msgstr "Permanente"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Persistence"
|
||||
msgstr "Persistencia"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
msgstr "Por favor, <0>configura un servidor SMTP</0> para asegurar que las alertas sean entregadas."
|
||||
@@ -1094,6 +1185,10 @@ msgstr "Proceso iniciado"
|
||||
msgid "Public Key"
|
||||
msgstr "Clave pública"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Quiet Hours"
|
||||
msgstr "Horas de silencio"
|
||||
|
||||
#. Disk read
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
@@ -1106,6 +1201,7 @@ msgstr "Recibido"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Refresh"
|
||||
msgstr "Actualizar"
|
||||
|
||||
@@ -1185,6 +1281,22 @@ msgstr "Guardar configuración"
|
||||
msgid "Save system"
|
||||
msgstr "Guardar sistema"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Saved in the database and does not expire until you disable it."
|
||||
msgstr "Guardado en la base de datos y no expira hasta que lo desactives."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule"
|
||||
msgstr "Programar"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule quiet hours where notifications will not be sent, such as during maintenance periods."
|
||||
msgstr "Programe horas de silencio donde no se enviarán notificaciones, como durante períodos de mantenimiento."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule quiet hours where notifications will not be sent."
|
||||
msgstr "Programe horas de silencio donde no se enviarán notificaciones."
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Search"
|
||||
msgstr "Buscar"
|
||||
@@ -1197,6 +1309,10 @@ msgstr "Buscar sistemas o configuraciones..."
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "Consulta <0>configuración de notificaciones</0> para configurar cómo recibe alertas."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Select {foo}"
|
||||
msgstr "Seleccionar {foo}"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "Enviado"
|
||||
@@ -1240,8 +1356,14 @@ msgstr "Configuración SMTP"
|
||||
msgid "Sort By"
|
||||
msgstr "Ordenar por"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Start Time"
|
||||
msgstr "Hora de inicio"
|
||||
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "Estado"
|
||||
@@ -1266,9 +1388,15 @@ msgstr "Espacio de swap utilizado por el sistema"
|
||||
msgid "Swap Usage"
|
||||
msgstr "Uso de swap"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "System"
|
||||
@@ -1353,8 +1481,8 @@ msgstr "Formato de hora"
|
||||
msgid "To email(s)"
|
||||
msgstr "A correo(s)"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "Toggle grid"
|
||||
msgstr "Alternar cuadrícula"
|
||||
|
||||
@@ -1423,6 +1551,10 @@ msgstr "Se activa cuando la carga media de 5 minutos supera un umbral"
|
||||
msgid "Triggers when any sensor exceeds a threshold"
|
||||
msgstr "Se activa cuando cualquier sensor supera un umbral"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when battery charge drops below a threshold"
|
||||
msgstr "Se activa cuando la carga de la batería baja de un umbral"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when combined up/down exceeds a threshold"
|
||||
msgstr "Se activa cuando la suma de subida/bajada supera un umbral"
|
||||
@@ -1447,6 +1579,8 @@ msgstr "Se activa cuando el estado cambia entre activo e inactivo"
|
||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||
msgstr "Se activa cuando el uso de cualquier disco supera un umbral"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Type"
|
||||
msgstr "Tipo"
|
||||
@@ -1476,7 +1610,7 @@ msgid "Unlimited"
|
||||
msgstr "Ilimitado"
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Up"
|
||||
msgstr "Activo"
|
||||
@@ -1485,7 +1619,12 @@ msgstr "Activo"
|
||||
msgid "Up ({upSystemsLength})"
|
||||
msgstr "Activo ({upSystemsLength})"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Update"
|
||||
msgstr "Actualizar"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Updated"
|
||||
msgstr "Actualizado"
|
||||
@@ -1498,7 +1637,7 @@ msgstr "Actualizado cada 10 minutos."
|
||||
msgid "Upload"
|
||||
msgstr "Cargar"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "Uptime"
|
||||
msgstr "Tiempo de actividad"
|
||||
|
||||
@@ -1570,8 +1709,8 @@ msgid "Webhook / Push notifications"
|
||||
msgstr "Notificaciones Webhook / Push"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation. Expires after one hour or on hub restart."
|
||||
msgstr "Cuando está habilitado, este token permite que los agentes se auto-registren sin crear previamente el sistema. Expira después de una hora o al reiniciar el hub."
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation."
|
||||
msgstr "Cuando está habilitado, este token permite a los agentes registrarse automáticamente sin creación previa del sistema."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: fa\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-10-28 23:00\n"
|
||||
"PO-Revision-Date: 2025-12-02 23:18\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Persian\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -24,6 +24,10 @@ msgstr ""
|
||||
msgid "{0} of {1} row(s) selected."
|
||||
msgstr "{0} از {1} ردیف انتخاب شده است."
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "{cores, plural, one {# core} other {# cores}}"
|
||||
msgstr "{cores, plural, one {# هسته} other {# هسته}}"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "{count, plural, one {{countString} day} other {{countString} days}}"
|
||||
msgstr "{count, plural, one {{countString} روز} other {{countString} روز}}"
|
||||
@@ -36,6 +40,10 @@ msgstr "{count, plural, one {{countString} ساعت} other {{countString} ساع
|
||||
msgid "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
|
||||
msgstr "{count, plural, one {{countString} دقیقه} few {{countString} دقیقه} many {{countString} دقیقه} other {{countString} دقیقه}}"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "{threads, plural, one {# thread} other {# threads}}"
|
||||
msgstr "{threads, plural, one {# رشته} other {# رشته}}"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 hour"
|
||||
msgstr "۱ ساعت"
|
||||
@@ -76,13 +84,16 @@ msgid "5 min"
|
||||
msgstr "۵ دقیقه"
|
||||
|
||||
#. Table column
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Actions"
|
||||
msgstr "عملیات"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Active"
|
||||
msgstr "فعال"
|
||||
|
||||
@@ -95,12 +106,14 @@ msgid "Active state"
|
||||
msgstr "وضعیت فعال"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add <0>System</0>"
|
||||
msgstr "افزودن <0>سیستم</0>"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Add {foo}"
|
||||
msgstr "افزودن {foo}"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add New System"
|
||||
msgstr "افزودن سیستم جدید"
|
||||
msgid "Add <0>System</0>"
|
||||
msgstr "افزودن <0>سیستم</0>"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add system"
|
||||
@@ -151,6 +164,7 @@ msgstr "همه کانتینرها"
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/home.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "All Systems"
|
||||
@@ -176,6 +190,11 @@ msgstr "میانگین"
|
||||
msgid "Average CPU utilization of containers"
|
||||
msgstr "میانگین استفاده از CPU کانتینرها"
|
||||
|
||||
#. placeholder {0}: alertData.unit
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "Average drops below <0>{value}{0}</0>"
|
||||
msgstr "میانگین به زیر <0>{value}{0}</0> میافتد"
|
||||
|
||||
#. placeholder {0}: alertData.unit
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "Average exceeds <0>{value}{0}</0>"
|
||||
@@ -208,7 +227,13 @@ msgstr "پشتیبانگیریها"
|
||||
msgid "Bandwidth"
|
||||
msgstr "پهنای باند"
|
||||
|
||||
#. Battery label in systems table header
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Bat"
|
||||
msgstr "باتری"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Battery"
|
||||
msgstr "باتری"
|
||||
|
||||
@@ -224,6 +249,13 @@ msgstr "غیرفعال شد"
|
||||
msgid "Before"
|
||||
msgstr "قبل از"
|
||||
|
||||
#. placeholder {0}: alert.value
|
||||
#. placeholder {1}: info.unit
|
||||
#. placeholder {2}: alert.min
|
||||
#: src/components/active-alerts.tsx
|
||||
msgid "Below {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
|
||||
msgstr "زیر {0}{1} در آخرین {2, plural, one {# دقیقه} other {# دقیقه}}"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Beszel supports OpenID Connect and many OAuth2 authentication providers."
|
||||
msgstr "بِزل از OpenID Connect و بسیاری از ارائهدهندگان احراز هویت OAuth2 پشتیبانی میکند."
|
||||
@@ -267,6 +299,7 @@ msgid "Can stop"
|
||||
msgstr "میتواند متوقف شود"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Cancel"
|
||||
msgstr "لغو"
|
||||
@@ -320,6 +353,12 @@ msgstr "برای جزئیات بیشتر، لاگها را بررسی کنی
|
||||
msgid "Check your notification service"
|
||||
msgstr "سرویس اطلاعرسانی خود را بررسی کنید"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Clear"
|
||||
msgstr "پاک کردن"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
msgid "Click on a container to view more information."
|
||||
msgstr "برای مشاهده اطلاعات بیشتر روی کانتینر کلیک کنید."
|
||||
@@ -442,6 +481,10 @@ msgstr "تجزیه زمان CPU"
|
||||
msgid "CPU Usage"
|
||||
msgstr "میزان استفاده از پردازنده"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Create"
|
||||
msgstr "ایجاد"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Create account"
|
||||
msgstr "ایجاد حساب کاربری"
|
||||
@@ -473,15 +516,18 @@ msgstr "وضعیت فعلی"
|
||||
msgid "Cycles"
|
||||
msgstr "چرخهها"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Dashboard"
|
||||
msgstr "داشبورد"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Daily"
|
||||
msgstr "روزانه"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Default time period"
|
||||
msgstr "بازه زمانی پیشفرض"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Delete"
|
||||
msgstr "حذف"
|
||||
@@ -548,7 +594,7 @@ msgstr "مستندات"
|
||||
|
||||
#. Context: System is down
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Down"
|
||||
@@ -566,11 +612,16 @@ msgstr "دانلود"
|
||||
msgid "Duration"
|
||||
msgstr "مدت زمان"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Edit"
|
||||
msgstr "ویرایش"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Edit {foo}"
|
||||
msgstr "ویرایش {foo}"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
#: src/components/login/otp-forms.tsx
|
||||
@@ -586,6 +637,11 @@ msgstr "اعلانهای ایمیلی"
|
||||
msgid "Empty"
|
||||
msgstr "خالی"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "End Time"
|
||||
msgstr "زمان پایان"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Enter email address to reset password"
|
||||
msgstr "آدرس ایمیل را برای بازنشانی رمز عبور وارد کنید"
|
||||
@@ -598,10 +654,16 @@ msgstr "آدرس ایمیل را وارد کنید..."
|
||||
msgid "Enter your one-time password."
|
||||
msgstr "رمز عبور یکبار مصرف خود را وارد کنید."
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Ephemeral"
|
||||
msgstr "گذرا"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Error"
|
||||
@@ -616,7 +678,7 @@ msgstr "در {2, plural, one {# دقیقه} other {# دقیقه}} گذشته ا
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Exec main PID"
|
||||
msgstr ""
|
||||
msgstr "PID اصلی اجرایی"
|
||||
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
|
||||
@@ -626,6 +688,10 @@ msgstr "سیستمهای موجود که در <0>config.yml</0> تعریف ن
|
||||
msgid "Exited active"
|
||||
msgstr "خروج فعال"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Expires after one hour or on hub restart."
|
||||
msgstr "پس از یک ساعت یا راهاندازی مجدد هاب منقضی میشود."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr "خروجی گرفتن"
|
||||
@@ -656,6 +722,7 @@ msgstr "احراز هویت ناموفق بود"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Failed to save settings"
|
||||
msgstr "ذخیره تنظیمات ناموفق بود"
|
||||
|
||||
@@ -714,6 +781,10 @@ msgstr "پر"
|
||||
msgid "General"
|
||||
msgstr "عمومی"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Global"
|
||||
msgstr "جهانی"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "GPU Engines"
|
||||
msgstr "موتورهای GPU"
|
||||
@@ -758,15 +829,14 @@ msgctxt "Docker image"
|
||||
msgid "Image"
|
||||
msgstr "تصویر"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Inactive"
|
||||
msgstr "غیرفعال"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Invalid email address."
|
||||
msgstr "آدرس ایمیل نامعتبر است."
|
||||
|
||||
#. Linux kernel
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Kernel"
|
||||
msgstr "هسته"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "زبان"
|
||||
@@ -786,7 +856,7 @@ msgstr "چرخه حیات"
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "limit"
|
||||
msgstr ""
|
||||
msgstr "محدودیت"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Load Average"
|
||||
@@ -842,7 +912,7 @@ msgstr "به دنبال جایی برای ایجاد هشدار هستید؟ ر
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Main PID"
|
||||
msgstr ""
|
||||
msgstr "PID اصلی"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Manage display and notification preferences."
|
||||
@@ -859,6 +929,7 @@ msgid "Max 1 min"
|
||||
msgstr "حداکثر ۱ دقیقه"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
@@ -959,12 +1030,19 @@ msgstr "پشتیبانی از OAuth 2 / OIDC"
|
||||
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
|
||||
msgstr "در هر بار راهاندازی مجدد، سیستمهای موجود در پایگاه داده با سیستمهای تعریف شده در فایل مطابقت داده میشوند."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "One-time"
|
||||
msgstr "یکبار مصرف"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "One-time password"
|
||||
msgstr "رمز عبور یکبار مصرف"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Open menu"
|
||||
msgstr "باز کردن منو"
|
||||
@@ -981,6 +1059,7 @@ msgstr "سایر"
|
||||
msgid "Overwrite existing alerts"
|
||||
msgstr "بازنویسی هشدارهای موجود"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Page"
|
||||
@@ -1013,6 +1092,10 @@ msgstr "رمز عبور باید کمتر از ۷۲ بایت باشد."
|
||||
msgid "Password reset request received"
|
||||
msgstr "درخواست بازنشانی رمز عبور دریافت شد"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Past"
|
||||
msgstr "گذشته"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Pause"
|
||||
msgstr "توقف"
|
||||
@@ -1034,6 +1117,14 @@ msgstr "میانگین استفاده در هر هسته"
|
||||
msgid "Percentage of time spent in each state"
|
||||
msgstr "درصد زمان صرف شده در هر حالت"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Permanent"
|
||||
msgstr "دائمی"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Persistence"
|
||||
msgstr "ماندگاری"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
msgstr "لطفاً برای اطمینان از تحویل هشدارها، یک <0>سرور SMTP پیکربندی کنید</0>."
|
||||
@@ -1094,6 +1185,10 @@ msgstr "فرآیند شروع شد"
|
||||
msgid "Public Key"
|
||||
msgstr "کلید عمومی"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Quiet Hours"
|
||||
msgstr "ساعات آرام"
|
||||
|
||||
#. Disk read
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
@@ -1106,6 +1201,7 @@ msgstr "دریافت شد"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Refresh"
|
||||
msgstr "تازهسازی"
|
||||
|
||||
@@ -1150,7 +1246,7 @@ msgstr "ادامه"
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgctxt "Root disk label"
|
||||
msgid "Root"
|
||||
msgstr ""
|
||||
msgstr "ریشه"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Rotate token"
|
||||
@@ -1185,6 +1281,22 @@ msgstr "ذخیره تنظیمات"
|
||||
msgid "Save system"
|
||||
msgstr "ذخیره سیستم"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Saved in the database and does not expire until you disable it."
|
||||
msgstr "در پایگاه داده ذخیره شده و تا زمانی که آن را غیرفعال نکنید، منقضی نمیشود."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule"
|
||||
msgstr "برنامهریزی"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule quiet hours where notifications will not be sent, such as during maintenance periods."
|
||||
msgstr "برنامهریزی ساعات آرام که در آن اعلانها ارسال نخواهند شد، مانند در طول دورههای تعمیر و نگهداری."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule quiet hours where notifications will not be sent."
|
||||
msgstr "برنامهریزی ساعات آرام که در آن اعلانها ارسال نخواهند شد."
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Search"
|
||||
msgstr "جستجو"
|
||||
@@ -1197,6 +1309,10 @@ msgstr "جستجو برای سیستمها یا تنظیمات..."
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "برای پیکربندی نحوه دریافت هشدارها، به <0>تنظیمات اعلان</0> مراجعه کنید."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Select {foo}"
|
||||
msgstr "انتخاب {foo}"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "ارسال شد"
|
||||
@@ -1240,8 +1356,14 @@ msgstr "تنظیمات SMTP"
|
||||
msgid "Sort By"
|
||||
msgstr "مرتبسازی بر اساس"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Start Time"
|
||||
msgstr "زمان شروع"
|
||||
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "وضعیت"
|
||||
@@ -1266,9 +1388,15 @@ msgstr "فضای Swap استفاده شده توسط سیستم"
|
||||
msgid "Swap Usage"
|
||||
msgstr "میزان استفاده از Swap"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "System"
|
||||
@@ -1280,7 +1408,7 @@ msgstr "میانگین بار سیستم در طول زمان"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Systemd Services"
|
||||
msgstr ""
|
||||
msgstr "خدمات Systemd"
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Systems"
|
||||
@@ -1353,8 +1481,8 @@ msgstr "فرمت زمان"
|
||||
msgid "To email(s)"
|
||||
msgstr "به ایمیل(ها)"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "Toggle grid"
|
||||
msgstr "تغییر نمایش جدول"
|
||||
|
||||
@@ -1423,6 +1551,10 @@ msgstr "هنگامی که میانگین بار ۵ دقیقهای از یک
|
||||
msgid "Triggers when any sensor exceeds a threshold"
|
||||
msgstr "هنگامی که هر حسگری از یک آستانه فراتر رود، فعال میشود"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when battery charge drops below a threshold"
|
||||
msgstr "زمانی که شارژ باتری زیر آستانه قرار میگیرد، فعال میشود"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when combined up/down exceeds a threshold"
|
||||
msgstr "هنگامی که مجموع بالا/پایین از یک آستانه فراتر رود، فعال میشود"
|
||||
@@ -1447,13 +1579,15 @@ msgstr "هنگامی که وضعیت بین بالا و پایین تغییر م
|
||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||
msgstr "هنگامی که استفاده از هر دیسکی از یک آستانه فراتر رود، فعال میشود"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Type"
|
||||
msgstr "نوع"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Unit file"
|
||||
msgstr ""
|
||||
msgstr "فایل واحد"
|
||||
|
||||
#. Temperature / network units
|
||||
#: src/components/routes/settings/general.tsx
|
||||
@@ -1476,7 +1610,7 @@ msgid "Unlimited"
|
||||
msgstr "نامحدود"
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Up"
|
||||
msgstr "فعال"
|
||||
@@ -1485,7 +1619,12 @@ msgstr "فعال"
|
||||
msgid "Up ({upSystemsLength})"
|
||||
msgstr "فعال ({upSystemsLength})"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Update"
|
||||
msgstr "بهروزرسانی"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Updated"
|
||||
msgstr "بهروزرسانی شد"
|
||||
@@ -1498,7 +1637,7 @@ msgstr "هر ۱۰ دقیقه بهروزرسانی میشود."
|
||||
msgid "Upload"
|
||||
msgstr "آپلود"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "Uptime"
|
||||
msgstr "آپتایم"
|
||||
|
||||
@@ -1570,8 +1709,8 @@ msgid "Webhook / Push notifications"
|
||||
msgstr "اعلانهای Webhook / Push"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation. Expires after one hour or on hub restart."
|
||||
msgstr "هنگامی که فعال است، این توکن به عاملها اجازه خودثبتنامی بدون ایجاد سیستم قبلی میدهد. پس از یک ساعت یا در راهاندازی مجدد هاب منقضی میشود."
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation."
|
||||
msgstr "هنگامی که فعال باشد، این توکن به عوامل اجازه میدهد بدون ایجاد سیستم قبلی، خود را ثبت کنند."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: fr\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-11-11 19:25\n"
|
||||
"PO-Revision-Date: 2026-01-09 21:08\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: French\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
@@ -24,6 +24,10 @@ msgstr ""
|
||||
msgid "{0} of {1} row(s) selected."
|
||||
msgstr "{0} sur {1} ligne(s) sélectionnée(s)."
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "{cores, plural, one {# core} other {# cores}}"
|
||||
msgstr "{cores, plural, one {# cœur} other {# cœurs}}"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "{count, plural, one {{countString} day} other {{countString} days}}"
|
||||
msgstr "{count, plural, one {{countString} jour} other {{countString} jours}}"
|
||||
@@ -34,7 +38,11 @@ msgstr "{count, plural, one {{countString} heure} other {{countString} heures}}"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
|
||||
msgstr "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
|
||||
msgstr "{count, plural, one {{countString} minute} other {{countString} minutes}}"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "{threads, plural, one {# thread} other {# threads}}"
|
||||
msgstr "{threads, plural, one {# fil} other {# fils}}"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 hour"
|
||||
@@ -76,13 +84,16 @@ msgid "5 min"
|
||||
msgstr "5 min"
|
||||
|
||||
#. Table column
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Actions"
|
||||
msgstr "Actions"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Active"
|
||||
msgstr "Active"
|
||||
|
||||
@@ -95,12 +106,14 @@ msgid "Active state"
|
||||
msgstr "État actif"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add <0>System</0>"
|
||||
msgstr "Ajouter <0>un Système</0>"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Add {foo}"
|
||||
msgstr "Ajouter {foo}"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add New System"
|
||||
msgstr "Ajouter un nouveau système"
|
||||
msgid "Add <0>System</0>"
|
||||
msgstr "Ajouter <0>un Système</0>"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add system"
|
||||
@@ -151,6 +164,7 @@ msgstr "Tous les conteneurs"
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/home.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "All Systems"
|
||||
@@ -176,6 +190,11 @@ msgstr "Moyenne"
|
||||
msgid "Average CPU utilization of containers"
|
||||
msgstr "Utilisation moyenne du CPU des conteneurs"
|
||||
|
||||
#. placeholder {0}: alertData.unit
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "Average drops below <0>{value}{0}</0>"
|
||||
msgstr "La moyenne descend en dessous de <0>{value}{0}</0>"
|
||||
|
||||
#. placeholder {0}: alertData.unit
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "Average exceeds <0>{value}{0}</0>"
|
||||
@@ -208,7 +227,13 @@ msgstr "Sauvegardes"
|
||||
msgid "Bandwidth"
|
||||
msgstr "Bande passante"
|
||||
|
||||
#. Battery label in systems table header
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Bat"
|
||||
msgstr "Bat"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Battery"
|
||||
msgstr "Batterie"
|
||||
|
||||
@@ -224,6 +249,13 @@ msgstr "Devenu inactif"
|
||||
msgid "Before"
|
||||
msgstr "Avant"
|
||||
|
||||
#. placeholder {0}: alert.value
|
||||
#. placeholder {1}: info.unit
|
||||
#. placeholder {2}: alert.min
|
||||
#: src/components/active-alerts.tsx
|
||||
msgid "Below {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
|
||||
msgstr "Inférieur à {0}{1} dans {2, plural, one {la dernière # minute} other {les dernières # minutes}}"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Beszel supports OpenID Connect and many OAuth2 authentication providers."
|
||||
msgstr "Beszel prend en charge OpenID Connect et de nombreux fournisseurs d'authentification OAuth2."
|
||||
@@ -267,6 +299,7 @@ msgid "Can stop"
|
||||
msgstr "Peut arrêter"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Cancel"
|
||||
msgstr "Annuler"
|
||||
@@ -320,6 +353,12 @@ msgstr "Vérifiez les journaux pour plus de détails."
|
||||
msgid "Check your notification service"
|
||||
msgstr "Vérifiez votre service de notification"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Clear"
|
||||
msgstr "Effacer"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
msgid "Click on a container to view more information."
|
||||
msgstr "Cliquez sur un conteneur pour voir plus d'informations."
|
||||
@@ -442,6 +481,10 @@ msgstr "Répartition du temps CPU"
|
||||
msgid "CPU Usage"
|
||||
msgstr "Utilisation du CPU"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Create"
|
||||
msgstr "Créer"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Create account"
|
||||
msgstr "Créer un compte"
|
||||
@@ -473,15 +516,18 @@ msgstr "État actuel"
|
||||
msgid "Cycles"
|
||||
msgstr "Cycles"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Dashboard"
|
||||
msgstr "Tableau de bord"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Daily"
|
||||
msgstr "Quotidien"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Default time period"
|
||||
msgstr "Période par défaut"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Delete"
|
||||
msgstr "Supprimer"
|
||||
@@ -548,11 +594,11 @@ msgstr "Documentation"
|
||||
|
||||
#. Context: System is down
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Down"
|
||||
msgstr "Injoignable"
|
||||
msgstr "Hors ligne"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Down ({downSystemsLength})"
|
||||
@@ -566,11 +612,16 @@ msgstr "Télécharger"
|
||||
msgid "Duration"
|
||||
msgstr "Durée"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Edit"
|
||||
msgstr "Éditer"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Edit {foo}"
|
||||
msgstr "Modifier {foo}"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
#: src/components/login/otp-forms.tsx
|
||||
@@ -586,6 +637,11 @@ msgstr "Notifications par email"
|
||||
msgid "Empty"
|
||||
msgstr "Vide"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "End Time"
|
||||
msgstr "Heure de fin"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Enter email address to reset password"
|
||||
msgstr "Entrez l'adresse email pour réinitialiser le mot de passe"
|
||||
@@ -598,10 +654,16 @@ msgstr "Entrez l'adresse email..."
|
||||
msgid "Enter your one-time password."
|
||||
msgstr "Entrez votre mot de passe à usage unique."
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Ephemeral"
|
||||
msgstr "Éphémère"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Error"
|
||||
@@ -626,6 +688,10 @@ msgstr "Les systèmes existants non définis dans <0>config.yml</0> seront suppr
|
||||
msgid "Exited active"
|
||||
msgstr "Sorti actif"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Expires after one hour or on hub restart."
|
||||
msgstr "Expire après une heure ou au redémarrage du hub."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr "Exporter"
|
||||
@@ -656,6 +722,7 @@ msgstr "Échec de l'authentification"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Failed to save settings"
|
||||
msgstr "Échec de l'enregistrement des paramètres"
|
||||
|
||||
@@ -691,7 +758,7 @@ msgstr "Micrologiciel"
|
||||
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "For <0>{min}</0> {min, plural, one {minute} other {minutes}}"
|
||||
msgstr "Pour <0>{min}</0> {min, plural, one {minute} other {minutes}}"
|
||||
msgstr "Pendant <0>{min}</0> {min, plural, one {minute} other {minutes}}"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Forgot password?"
|
||||
@@ -714,6 +781,10 @@ msgstr "Pleine"
|
||||
msgid "General"
|
||||
msgstr "Général"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Global"
|
||||
msgstr "Global"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "GPU Engines"
|
||||
msgstr "Moteurs GPU"
|
||||
@@ -758,15 +829,14 @@ msgctxt "Docker image"
|
||||
msgid "Image"
|
||||
msgstr "Image"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Inactive"
|
||||
msgstr "Inactif"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Invalid email address."
|
||||
msgstr "Adresse email invalide."
|
||||
|
||||
#. Linux kernel
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Kernel"
|
||||
msgstr "Noyau"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "Langue"
|
||||
@@ -859,6 +929,7 @@ msgid "Max 1 min"
|
||||
msgstr "Max 1 min"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
@@ -898,7 +969,7 @@ msgstr "Nom"
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Net"
|
||||
msgstr "Net"
|
||||
msgstr "Rés"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Network traffic of docker containers"
|
||||
@@ -959,12 +1030,19 @@ msgstr "Support OAuth 2 / OIDC"
|
||||
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
|
||||
msgstr "À chaque redémarrage, les systèmes dans la base de données seront mis à jour pour correspondre aux systèmes définis dans le fichier."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "One-time"
|
||||
msgstr "Unique"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "One-time password"
|
||||
msgstr "Mot de passe à usage unique"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Open menu"
|
||||
msgstr "Ouvrir le menu"
|
||||
@@ -981,6 +1059,7 @@ msgstr "Autre"
|
||||
msgid "Overwrite existing alerts"
|
||||
msgstr "Écraser les alertes existantes"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Page"
|
||||
@@ -1013,6 +1092,10 @@ msgstr "Le mot de passe doit être inférieur à 72 Octets."
|
||||
msgid "Password reset request received"
|
||||
msgstr "Demande de réinitialisation du mot de passe reçue"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Past"
|
||||
msgstr "Passé"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Pause"
|
||||
msgstr "Pause"
|
||||
@@ -1034,6 +1117,14 @@ msgstr "Utilisation moyenne par cœur"
|
||||
msgid "Percentage of time spent in each state"
|
||||
msgstr "Pourcentage de temps passé dans chaque état"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Permanent"
|
||||
msgstr "Permanent"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Persistence"
|
||||
msgstr "Persistance"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
msgstr "Veuillez <0>configurer un serveur SMTP</0> pour garantir la livraison des alertes."
|
||||
@@ -1094,6 +1185,10 @@ msgstr "Processus démarré"
|
||||
msgid "Public Key"
|
||||
msgstr "Clé publique"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Quiet Hours"
|
||||
msgstr "Heures calmes"
|
||||
|
||||
#. Disk read
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
@@ -1106,6 +1201,7 @@ msgstr "Reçu"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Refresh"
|
||||
msgstr "Actualiser"
|
||||
|
||||
@@ -1185,6 +1281,22 @@ msgstr "Enregistrer les paramètres"
|
||||
msgid "Save system"
|
||||
msgstr "Sauvegarder le système"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Saved in the database and does not expire until you disable it."
|
||||
msgstr "Enregistré dans la base de données et n'expire pas tant que vous ne le désactivez pas."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule"
|
||||
msgstr "Programmer"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule quiet hours where notifications will not be sent, such as during maintenance periods."
|
||||
msgstr "Programmez des heures calmes où les notifications ne seront pas envoyées, par exemple pendant les périodes de maintenance."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule quiet hours where notifications will not be sent."
|
||||
msgstr "Programmez des heures calmes où les notifications ne seront pas envoyées."
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Search"
|
||||
msgstr "Recherche"
|
||||
@@ -1197,6 +1309,10 @@ msgstr "Rechercher des systèmes ou des paramètres..."
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "Voir les <0>paramètres de notification</0> pour configurer comment vous recevez les alertes."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Select {foo}"
|
||||
msgstr "Sélectionner {foo}"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "Envoyé"
|
||||
@@ -1240,8 +1356,14 @@ msgstr "Paramètres SMTP"
|
||||
msgid "Sort By"
|
||||
msgstr "Trier par"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Start Time"
|
||||
msgstr "Heure de début"
|
||||
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "État"
|
||||
@@ -1266,9 +1388,15 @@ msgstr "Espace Swap utilisé par le système"
|
||||
msgid "Swap Usage"
|
||||
msgstr "Utilisation du swap"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "System"
|
||||
@@ -1353,8 +1481,8 @@ msgstr "Format d'heure"
|
||||
msgid "To email(s)"
|
||||
msgstr "Aux email(s)"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "Toggle grid"
|
||||
msgstr "Basculer la grille"
|
||||
|
||||
@@ -1413,16 +1541,20 @@ msgstr "Se déclenche lorsque la charge moyenne sur 1 minute dépasse un seuil"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when 15 minute load average exceeds a threshold"
|
||||
msgstr "Se déclenche lorsque la charge moyenne sur 15 minute dépasse un seuil"
|
||||
msgstr "Se déclenche lorsque la charge moyenne sur 15 minutes dépasse un seuil"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when 5 minute load average exceeds a threshold"
|
||||
msgstr "Se déclenche lorsque la charge moyenne sur 5 minute dépasse un seuil"
|
||||
msgstr "Se déclenche lorsque la charge moyenne sur 5 minutes dépasse un seuil"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when any sensor exceeds a threshold"
|
||||
msgstr "Déclenchement lorsque tout capteur dépasse un seuil"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when battery charge drops below a threshold"
|
||||
msgstr "Déclenchement lorsque la charge de la batterie descend en dessous d'un seuil"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when combined up/down exceeds a threshold"
|
||||
msgstr "Déclenchement lorsque le montant/descendant combinée dépasse un seuil"
|
||||
@@ -1447,6 +1579,8 @@ msgstr "Se déclenche lorsque le statut passe de \"Joignable\" à \"Injoignable\
|
||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||
msgstr "Déclenchement lorsque l'utilisation de tout disque dépasse un seuil"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Type"
|
||||
msgstr "Type"
|
||||
@@ -1476,7 +1610,7 @@ msgid "Unlimited"
|
||||
msgstr "Illimité"
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Up"
|
||||
msgstr "Joignable"
|
||||
@@ -1485,7 +1619,12 @@ msgstr "Joignable"
|
||||
msgid "Up ({upSystemsLength})"
|
||||
msgstr "Joignable ({upSystemsLength})"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Update"
|
||||
msgstr "Mettre à jour"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Updated"
|
||||
msgstr "Mis à jour"
|
||||
@@ -1498,7 +1637,7 @@ msgstr "Mis à jour toutes les 10 minutes."
|
||||
msgid "Upload"
|
||||
msgstr "Téléverser"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "Uptime"
|
||||
msgstr "Temps de fonctionnement"
|
||||
|
||||
@@ -1570,8 +1709,8 @@ msgid "Webhook / Push notifications"
|
||||
msgstr "Notifications Webhook / Push"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation. Expires after one hour or on hub restart."
|
||||
msgstr "Lorsqu'il est activé, ce token permet aux agents de s'auto-enregistrer sans création préalable du système. Expire après une heure ou au redémarrage du hub."
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation."
|
||||
msgstr "Lorsqu'il est activé, ce jeton permet aux agents de s'enregistrer automatiquement sans création préalable du système."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: he\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-10-30 21:53\n"
|
||||
"PO-Revision-Date: 2025-12-02 23:18\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Hebrew\n"
|
||||
"Plural-Forms: nplurals=4; plural=n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3;\n"
|
||||
@@ -24,6 +24,10 @@ msgstr ""
|
||||
msgid "{0} of {1} row(s) selected."
|
||||
msgstr "{0} מתוך {1} שורה(ות) נבחרו."
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "{cores, plural, one {# core} other {# cores}}"
|
||||
msgstr "{cores, plural, one {# ליבה} other {# ליבות}}"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "{count, plural, one {{countString} day} other {{countString} days}}"
|
||||
msgstr "{count, plural, one {{countString} יום} two {{countString} ימים} other {{countString} ימים}}"
|
||||
@@ -36,6 +40,10 @@ msgstr "{count, plural, one {{countString} שעה} two {{countString} שעות}
|
||||
msgid "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
|
||||
msgstr "{count, plural, one {{countString} דקה} two {{countString} דקות} other {{countString} דקות}}"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "{threads, plural, one {# thread} other {# threads}}"
|
||||
msgstr "{threads, plural, one {# תהליכון} other {# תהליכונים}}"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 hour"
|
||||
msgstr "שעה"
|
||||
@@ -76,13 +84,16 @@ msgid "5 min"
|
||||
msgstr "5 דק'"
|
||||
|
||||
#. Table column
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Actions"
|
||||
msgstr "פעולות"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Active"
|
||||
msgstr "פעיל"
|
||||
|
||||
@@ -95,12 +106,14 @@ msgid "Active state"
|
||||
msgstr "מצב פעיל"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add <0>System</0>"
|
||||
msgstr "הוסף <0>מערכת</0>"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Add {foo}"
|
||||
msgstr "הוסף {foo}"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add New System"
|
||||
msgstr "הוסף מערכת חדשה"
|
||||
msgid "Add <0>System</0>"
|
||||
msgstr "הוסף <0>מערכת</0>"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add system"
|
||||
@@ -151,6 +164,7 @@ msgstr "כל הקונטיינרים"
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/home.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "All Systems"
|
||||
@@ -176,6 +190,11 @@ msgstr "ממוצע"
|
||||
msgid "Average CPU utilization of containers"
|
||||
msgstr "ניצול ממוצע של CPU בקונטיינרים"
|
||||
|
||||
#. placeholder {0}: alertData.unit
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "Average drops below <0>{value}{0}</0>"
|
||||
msgstr "הממוצע יורד מתחת ל-<0>{value}{0}</0>"
|
||||
|
||||
#. placeholder {0}: alertData.unit
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "Average exceeds <0>{value}{0}</0>"
|
||||
@@ -208,7 +227,13 @@ msgstr "גיבויים"
|
||||
msgid "Bandwidth"
|
||||
msgstr "רוחב פס"
|
||||
|
||||
#. Battery label in systems table header
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Bat"
|
||||
msgstr "סוללה"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Battery"
|
||||
msgstr "סוללה"
|
||||
|
||||
@@ -224,6 +249,13 @@ msgstr "הפך ללא פעיל"
|
||||
msgid "Before"
|
||||
msgstr "לפני"
|
||||
|
||||
#. placeholder {0}: alert.value
|
||||
#. placeholder {1}: info.unit
|
||||
#. placeholder {2}: alert.min
|
||||
#: src/components/active-alerts.tsx
|
||||
msgid "Below {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
|
||||
msgstr "מתחת ל-{0}{1} ב-{2, plural, one {דקה האחרונה} other {-# הדקות האחרונות}}"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Beszel supports OpenID Connect and many OAuth2 authentication providers."
|
||||
msgstr "Beszel תומך ב-OpenID Connect ובספקי אימות רבים של OAuth2."
|
||||
@@ -267,6 +299,7 @@ msgid "Can stop"
|
||||
msgstr "יכול לעצור"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Cancel"
|
||||
msgstr "ביטול"
|
||||
@@ -320,6 +353,12 @@ msgstr "בדוק לוגים לפרטים נוספים"
|
||||
msgid "Check your notification service"
|
||||
msgstr "בדוק את שירות ההתראות שלך"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Clear"
|
||||
msgstr "נקה"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
msgid "Click on a container to view more information."
|
||||
msgstr "לחץ על קונטיינר כדי לצפות במידע נוסף."
|
||||
@@ -442,6 +481,10 @@ msgstr "פירוט זמן CPU"
|
||||
msgid "CPU Usage"
|
||||
msgstr "שימוש CPU"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Create"
|
||||
msgstr "צור"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Create account"
|
||||
msgstr "צור חשבון"
|
||||
@@ -473,15 +516,18 @@ msgstr "מצב נוכחי"
|
||||
msgid "Cycles"
|
||||
msgstr "מחזורים"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Dashboard"
|
||||
msgstr "לוח בקרה"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Daily"
|
||||
msgstr "יומי"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Default time period"
|
||||
msgstr "תקופת זמן ברירת מחדל"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Delete"
|
||||
msgstr "מחק"
|
||||
@@ -548,7 +594,7 @@ msgstr "תיעוד"
|
||||
|
||||
#. Context: System is down
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Down"
|
||||
@@ -566,11 +612,16 @@ msgstr "הורדה"
|
||||
msgid "Duration"
|
||||
msgstr "משך זמן"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Edit"
|
||||
msgstr "ערוך"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Edit {foo}"
|
||||
msgstr "ערוך {foo}"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
#: src/components/login/otp-forms.tsx
|
||||
@@ -586,6 +637,11 @@ msgstr "התראות אימייל"
|
||||
msgid "Empty"
|
||||
msgstr "ריק"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "End Time"
|
||||
msgstr "זמן סיום"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Enter email address to reset password"
|
||||
msgstr "הכנס כתובת אימייל לאיפוס סיסמה"
|
||||
@@ -598,10 +654,16 @@ msgstr "הכנס כתובת אימייל..."
|
||||
msgid "Enter your one-time password."
|
||||
msgstr "הכנס את הסיסמה החד-פעמית שלך."
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Ephemeral"
|
||||
msgstr "זמני"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Error"
|
||||
@@ -626,6 +688,10 @@ msgstr "מערכות קיימות שלא מוגדרות ב-<0>config.yml</0> י
|
||||
msgid "Exited active"
|
||||
msgstr "יצא פעיל"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Expires after one hour or on hub restart."
|
||||
msgstr "פג תוקף לאחר שעה או בהפעלה מחדש של ה-hub."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr "ייצא"
|
||||
@@ -656,6 +722,7 @@ msgstr "אימות נכשל"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Failed to save settings"
|
||||
msgstr "שמירת הגדרות נכשלה"
|
||||
|
||||
@@ -714,6 +781,10 @@ msgstr "מלא"
|
||||
msgid "General"
|
||||
msgstr "כללי"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Global"
|
||||
msgstr "גלובלי"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "GPU Engines"
|
||||
msgstr "מנועי GPU"
|
||||
@@ -758,15 +829,14 @@ msgctxt "Docker image"
|
||||
msgid "Image"
|
||||
msgstr "תמונה"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Inactive"
|
||||
msgstr "לא פעיל"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Invalid email address."
|
||||
msgstr "כתובת אימייל לא תקינה."
|
||||
|
||||
#. Linux kernel
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Kernel"
|
||||
msgstr "קרנל"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "שפה"
|
||||
@@ -859,6 +929,7 @@ msgid "Max 1 min"
|
||||
msgstr "מקס 1 דק'"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
@@ -959,12 +1030,19 @@ msgstr "תמיכה ב-OAuth 2 / OIDC"
|
||||
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
|
||||
msgstr "בכל הפעלה מחדש, המערכות במסד הנתונים יעודכנו כדי להתאים למערכות המוגדרות בקובץ."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "One-time"
|
||||
msgstr "חד-פעמי"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "One-time password"
|
||||
msgstr "סיסמה חד-פעמית"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Open menu"
|
||||
msgstr "פתח תפריט"
|
||||
@@ -981,6 +1059,7 @@ msgstr "אחר"
|
||||
msgid "Overwrite existing alerts"
|
||||
msgstr "דרוס התראות קיימות"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Page"
|
||||
@@ -1013,6 +1092,10 @@ msgstr "הסיסמה חייבת להיות פחות מ-72 בתים."
|
||||
msgid "Password reset request received"
|
||||
msgstr "בקשת איפוס סיסמה התקבלה"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Past"
|
||||
msgstr "עבר"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Pause"
|
||||
msgstr "השהה"
|
||||
@@ -1034,6 +1117,14 @@ msgstr "ניצול ממוצע לליבה"
|
||||
msgid "Percentage of time spent in each state"
|
||||
msgstr "אחוז הזמן המוקדש לכל מצב"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Permanent"
|
||||
msgstr "קבוע"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Persistence"
|
||||
msgstr "עקביות"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
msgstr "אנא <0>הגדר שרת SMTP</0> כדי להבטיח שהתראות יישלחו."
|
||||
@@ -1094,6 +1185,10 @@ msgstr "תהליך התחיל"
|
||||
msgid "Public Key"
|
||||
msgstr "מפתח ציבורי"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Quiet Hours"
|
||||
msgstr "שעות שקט"
|
||||
|
||||
#. Disk read
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
@@ -1106,6 +1201,7 @@ msgstr "התקבל"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Refresh"
|
||||
msgstr "רענן"
|
||||
|
||||
@@ -1150,7 +1246,7 @@ msgstr "המשך"
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgctxt "Root disk label"
|
||||
msgid "Root"
|
||||
msgstr ""
|
||||
msgstr "שורש"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Rotate token"
|
||||
@@ -1185,6 +1281,22 @@ msgstr "שמור הגדרות"
|
||||
msgid "Save system"
|
||||
msgstr "שמור מערכת"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Saved in the database and does not expire until you disable it."
|
||||
msgstr "נשמר במסד הנתונים ולא פג תוקף עד שתבטל אותו."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule"
|
||||
msgstr "לוח זמנים"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule quiet hours where notifications will not be sent, such as during maintenance periods."
|
||||
msgstr "קבע שעות שקט שבהן לא יישלחו התראות, כמו במהלך תקופות תחזוקה."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule quiet hours where notifications will not be sent."
|
||||
msgstr "קבע שעות שקט שבהן לא יישלחו התראות."
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Search"
|
||||
msgstr "חיפוש"
|
||||
@@ -1197,6 +1309,10 @@ msgstr "חפש מערכות או הגדרות..."
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "ראה <0>הגדרות התראות</0> כדי להגדיר כיצד אתה מקבל התראות."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Select {foo}"
|
||||
msgstr "בחר {foo}"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "נשלח"
|
||||
@@ -1240,8 +1356,14 @@ msgstr "הגדרות SMTP"
|
||||
msgid "Sort By"
|
||||
msgstr "מיין לפי"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Start Time"
|
||||
msgstr "זמן התחלה"
|
||||
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "מצב"
|
||||
@@ -1266,9 +1388,15 @@ msgstr "שטח swap בשימוש על ידי המערכת"
|
||||
msgid "Swap Usage"
|
||||
msgstr "שימוש ב-Swap"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "System"
|
||||
@@ -1353,8 +1481,8 @@ msgstr "פורמט זמן"
|
||||
msgid "To email(s)"
|
||||
msgstr "לאימייל(ים)"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "Toggle grid"
|
||||
msgstr "החלף רשת"
|
||||
|
||||
@@ -1423,6 +1551,10 @@ msgstr "מופעל כאשר ממוצע העומס ל-5 דקות עולה על ס
|
||||
msgid "Triggers when any sensor exceeds a threshold"
|
||||
msgstr "מופעל כאשר כל חיישן עולה על סף"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when battery charge drops below a threshold"
|
||||
msgstr "מופעל כאשר טעינת הסוללה יורדת מתחת לסף"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when combined up/down exceeds a threshold"
|
||||
msgstr "מופעל כאשר השילוב של למעלה/למטה עולה על סף"
|
||||
@@ -1447,6 +1579,8 @@ msgstr "מופעל כאשר הסטטוס מתחלף בין למעלה ולמטה
|
||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||
msgstr "מופעל כאשר שימוש בכל דיסק עולה על סף"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Type"
|
||||
msgstr "סוג"
|
||||
@@ -1476,7 +1610,7 @@ msgid "Unlimited"
|
||||
msgstr "ללא הגבלה"
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Up"
|
||||
msgstr "למעלה"
|
||||
@@ -1485,7 +1619,12 @@ msgstr "למעלה"
|
||||
msgid "Up ({upSystemsLength})"
|
||||
msgstr "למעלה ({upSystemsLength})"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Update"
|
||||
msgstr "עדכן"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Updated"
|
||||
msgstr "עודכן"
|
||||
@@ -1498,7 +1637,7 @@ msgstr "מתעדכן כל 10 דקות."
|
||||
msgid "Upload"
|
||||
msgstr "העלאה"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "Uptime"
|
||||
msgstr "זמן פעילות"
|
||||
|
||||
@@ -1570,8 +1709,8 @@ msgid "Webhook / Push notifications"
|
||||
msgstr "Webhook / התראות דחיפה"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation. Expires after one hour or on hub restart."
|
||||
msgstr "כאשר מופעל, token זה מאפשר לסוכנים להירשם עצמאית ללא יצירת מערכת מוקדמת. פג לאחר שעה אחת או בהפעלה מחדש של hub."
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation."
|
||||
msgstr "כאשר מופעל, אסימון זה מאפשר לסוכנים להירשם באופן עצמי ללא יצירת מערכת מוקדמת."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: hr\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-10-28 23:00\n"
|
||||
"PO-Revision-Date: 2025-12-02 23:18\n"
|
||||
"Last-Translator: \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"
|
||||
@@ -24,6 +24,10 @@ msgstr ""
|
||||
msgid "{0} of {1} row(s) selected."
|
||||
msgstr "{0} od {1} redaka izabrano."
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "{cores, plural, one {# core} other {# cores}}"
|
||||
msgstr "{cores, plural, one {# jezgra} few {# jezgre} other {# jezgri}}"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "{count, plural, one {{countString} day} other {{countString} days}}"
|
||||
msgstr "{count, plural, one {{countString} dan} other {{countString} dani}}"
|
||||
@@ -36,6 +40,10 @@ msgstr "{count, plural, one {{countString} sat} other {{countString} sati}}"
|
||||
msgid "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
|
||||
msgstr "{count, plural, one {{countString} minuta} few {{countString} minuta} many {{countString} minuta} other {{countString} minute}}"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "{threads, plural, one {# thread} other {# threads}}"
|
||||
msgstr "{threads, plural, one {# nit} few {# niti} other {# niti}}"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 hour"
|
||||
msgstr "1 sat"
|
||||
@@ -76,13 +84,16 @@ msgid "5 min"
|
||||
msgstr "5 minuta"
|
||||
|
||||
#. Table column
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Actions"
|
||||
msgstr "Akcije"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Active"
|
||||
msgstr "Aktivan"
|
||||
|
||||
@@ -95,12 +106,14 @@ msgid "Active state"
|
||||
msgstr "Aktivno stanje"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add <0>System</0>"
|
||||
msgstr "Dodaj <0>Sistem</0>"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Add {foo}"
|
||||
msgstr "Dodaj {foo}"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add New System"
|
||||
msgstr "Dodaj Novi Sistem"
|
||||
msgid "Add <0>System</0>"
|
||||
msgstr "Dodaj <0>Sistem</0>"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add system"
|
||||
@@ -151,6 +164,7 @@ msgstr "Svi spremnici"
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/home.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "All Systems"
|
||||
@@ -176,6 +190,11 @@ msgstr "Prosjek"
|
||||
msgid "Average CPU utilization of containers"
|
||||
msgstr "Prosječna iskorištenost procesora u spremnicima"
|
||||
|
||||
#. placeholder {0}: alertData.unit
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "Average drops below <0>{value}{0}</0>"
|
||||
msgstr "Prosjek pada ispod <0>{value}{0}</0>"
|
||||
|
||||
#. placeholder {0}: alertData.unit
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "Average exceeds <0>{value}{0}</0>"
|
||||
@@ -208,7 +227,13 @@ msgstr "Sigurnosne kopije"
|
||||
msgid "Bandwidth"
|
||||
msgstr "Propusnost"
|
||||
|
||||
#. Battery label in systems table header
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Bat"
|
||||
msgstr "Bat"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Battery"
|
||||
msgstr "Baterija"
|
||||
|
||||
@@ -224,6 +249,13 @@ msgstr "Postalo neaktivno"
|
||||
msgid "Before"
|
||||
msgstr "Prije"
|
||||
|
||||
#. placeholder {0}: alert.value
|
||||
#. placeholder {1}: info.unit
|
||||
#. placeholder {2}: alert.min
|
||||
#: src/components/active-alerts.tsx
|
||||
msgid "Below {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
|
||||
msgstr "Ispod {0}{1} u posljednjih {2, plural, one {# minuti} few {# minute} other {# minuta}}"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Beszel supports OpenID Connect and many OAuth2 authentication providers."
|
||||
msgstr "Beszel podržava OpenID Connect i mnoge druge OAuth2 davatalje autentifikacije."
|
||||
@@ -267,13 +299,14 @@ msgid "Can stop"
|
||||
msgstr "Može se zaustaviti"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Cancel"
|
||||
msgstr "Otkaži"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Capabilities"
|
||||
msgstr ""
|
||||
msgstr "Mogućnosti"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Capacity"
|
||||
@@ -320,6 +353,12 @@ msgstr "Provjerite logove za više detalja."
|
||||
msgid "Check your notification service"
|
||||
msgstr "Provjerite Vaš servis notifikacija"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Clear"
|
||||
msgstr "Očisti"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
msgid "Click on a container to view more information."
|
||||
msgstr "Kliknite na spremnik za prikaz više informacija."
|
||||
@@ -442,6 +481,10 @@ msgstr "Raspodjela CPU vremena"
|
||||
msgid "CPU Usage"
|
||||
msgstr "Iskorištenost procesora"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Create"
|
||||
msgstr "Stvori"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Create account"
|
||||
msgstr "Napravite račun"
|
||||
@@ -473,15 +516,18 @@ msgstr "Trenutno stanje"
|
||||
msgid "Cycles"
|
||||
msgstr "Ciklusi"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Dashboard"
|
||||
msgstr "Nadzorna ploča"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Daily"
|
||||
msgstr "Dnevno"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Default time period"
|
||||
msgstr "Zadano vremensko razdoblje"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Delete"
|
||||
msgstr "Izbriši"
|
||||
@@ -548,7 +594,7 @@ msgstr "Dokumentacija"
|
||||
|
||||
#. Context: System is down
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Down"
|
||||
@@ -566,11 +612,16 @@ msgstr "Preuzmi"
|
||||
msgid "Duration"
|
||||
msgstr "Trajanje"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Edit"
|
||||
msgstr "Uredi"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Edit {foo}"
|
||||
msgstr "Uredi {foo}"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
#: src/components/login/otp-forms.tsx
|
||||
@@ -586,6 +637,11 @@ msgstr "Email notifikacije"
|
||||
msgid "Empty"
|
||||
msgstr "Prazna"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "End Time"
|
||||
msgstr "Vrijeme završetka"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Enter email address to reset password"
|
||||
msgstr "Unesite email adresu za resetiranje lozinke"
|
||||
@@ -598,10 +654,16 @@ msgstr "Unesite email adresu..."
|
||||
msgid "Enter your one-time password."
|
||||
msgstr "Unesite Vašu jednokratnu lozinku."
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Ephemeral"
|
||||
msgstr "Efemeran"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Error"
|
||||
@@ -626,6 +688,10 @@ msgstr "Postojeći sistemi koji nisu definirani u <0>config.yml</0> će biti izb
|
||||
msgid "Exited active"
|
||||
msgstr "Izašlo aktivno"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Expires after one hour or on hub restart."
|
||||
msgstr "Istječe nakon jednog sata ili ponovnog pokretanja huba."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr "Izvezi"
|
||||
@@ -656,6 +722,7 @@ msgstr "Provjera autentičnosti nije uspjela"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Failed to save settings"
|
||||
msgstr "Neuspješno snimanje postavki"
|
||||
|
||||
@@ -687,7 +754,7 @@ msgstr "Otisak prsta"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Firmware"
|
||||
msgstr ""
|
||||
msgstr "Firmver"
|
||||
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "For <0>{min}</0> {min, plural, one {minute} other {minutes}}"
|
||||
@@ -714,6 +781,10 @@ msgstr "Puna"
|
||||
msgid "General"
|
||||
msgstr "Općenito"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Global"
|
||||
msgstr "Globalno"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "GPU Engines"
|
||||
msgstr "GPU motori"
|
||||
@@ -758,15 +829,14 @@ msgctxt "Docker image"
|
||||
msgid "Image"
|
||||
msgstr "Slika"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Inactive"
|
||||
msgstr "Neaktivno"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Invalid email address."
|
||||
msgstr "Nevažeća adresa e-pošte."
|
||||
|
||||
#. Linux kernel
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Kernel"
|
||||
msgstr "Jezgra"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "Jezik"
|
||||
@@ -859,6 +929,7 @@ msgid "Max 1 min"
|
||||
msgstr "Maksimalno 1 minuta"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
@@ -885,7 +956,7 @@ msgstr "Upotreba memorije Docker spremnika"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Model"
|
||||
msgstr ""
|
||||
msgstr "Model"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
@@ -959,12 +1030,19 @@ msgstr "Podrška za OAuth 2 / OIDC"
|
||||
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
|
||||
msgstr "Prilikom svakog ponovnog pokretanja, sustavi u bazi podataka biti će ažurirani kako bi odgovarali sustavima definiranim u datoteci."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "One-time"
|
||||
msgstr "Jednokratno"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "One-time password"
|
||||
msgstr "Jednokratna lozinka"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Open menu"
|
||||
msgstr "Otvori menu"
|
||||
@@ -981,6 +1059,7 @@ msgstr "Ostalo"
|
||||
msgid "Overwrite existing alerts"
|
||||
msgstr "Prebrišite postojeća upozorenja"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Page"
|
||||
@@ -1013,6 +1092,10 @@ msgstr "Lozinka mora biti kraća od 72 bajta."
|
||||
msgid "Password reset request received"
|
||||
msgstr "Zahtjev za ponovno postavljanje lozinke primljen"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Past"
|
||||
msgstr "Prošlost"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Pause"
|
||||
msgstr "Pauza"
|
||||
@@ -1034,6 +1117,14 @@ msgstr "Prosječna iskorištenost po jezgri"
|
||||
msgid "Percentage of time spent in each state"
|
||||
msgstr "Postotak vremena provedenog u svakom stanju"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Permanent"
|
||||
msgstr "Trajan"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Persistence"
|
||||
msgstr "Postojanost"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
msgstr "Molimo <0>konfigurirajte SMTP server</0> kako biste osigurali isporuku upozorenja."
|
||||
@@ -1094,6 +1185,10 @@ msgstr "Proces pokrenut"
|
||||
msgid "Public Key"
|
||||
msgstr "Javni Ključ"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Quiet Hours"
|
||||
msgstr "Tihi sati"
|
||||
|
||||
#. Disk read
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
@@ -1106,6 +1201,7 @@ msgstr "Primljeno"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Refresh"
|
||||
msgstr "Osvježi"
|
||||
|
||||
@@ -1150,7 +1246,7 @@ msgstr "Nastavi"
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgctxt "Root disk label"
|
||||
msgid "Root"
|
||||
msgstr ""
|
||||
msgstr "Korijen"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Rotate token"
|
||||
@@ -1185,6 +1281,22 @@ msgstr "Spremi Postavke"
|
||||
msgid "Save system"
|
||||
msgstr "Spremi sustav"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Saved in the database and does not expire until you disable it."
|
||||
msgstr "Spremljeno u bazi podataka i ne istječe dok ga ne onemogućite."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule"
|
||||
msgstr "Raspored"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule quiet hours where notifications will not be sent, such as during maintenance periods."
|
||||
msgstr "Rasporedi tihe sate kada se obavijesti neće slati, na primjer tijekom razdoblja održavanja."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule quiet hours where notifications will not be sent."
|
||||
msgstr "Rasporedi tihe sate kada se obavijesti neće slati."
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Search"
|
||||
msgstr "Pretraži"
|
||||
@@ -1197,6 +1309,10 @@ msgstr "Pretraži za sisteme ili postavke..."
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "Pogledajte <0>postavke obavijesti</0> da biste konfigurirali način primanja upozorenja."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Select {foo}"
|
||||
msgstr "Odaberi {foo}"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "Poslano"
|
||||
@@ -1240,8 +1356,14 @@ msgstr "SMTP postavke"
|
||||
msgid "Sort By"
|
||||
msgstr "Sortiraj po"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Start Time"
|
||||
msgstr "Vrijeme početka"
|
||||
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "Stanje"
|
||||
@@ -1266,9 +1388,15 @@ msgstr "Swap prostor uzet od strane sistema"
|
||||
msgid "Swap Usage"
|
||||
msgstr "Swap Iskorištenost"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "System"
|
||||
@@ -1280,7 +1408,7 @@ msgstr "Prosječno opterećenje sustava kroz vrijeme"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Systemd Services"
|
||||
msgstr ""
|
||||
msgstr "Systemd servisi"
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Systems"
|
||||
@@ -1353,8 +1481,8 @@ msgstr "Format vremena"
|
||||
msgid "To email(s)"
|
||||
msgstr "Primaoci e-pošte"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "Toggle grid"
|
||||
msgstr "Uključi/isključi rešetku"
|
||||
|
||||
@@ -1423,6 +1551,10 @@ msgstr "Pokreće se kada prosječna opterećenost sustava unutar 5 minuta prije
|
||||
msgid "Triggers when any sensor exceeds a threshold"
|
||||
msgstr "Pokreće se kada bilo koji senzor prijeđe prag"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when battery charge drops below a threshold"
|
||||
msgstr "Pokreće se kada razina baterije padne ispod praga"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when combined up/down exceeds a threshold"
|
||||
msgstr "Pokreće se kada kombinacija gore/dolje premaši prag"
|
||||
@@ -1447,6 +1579,8 @@ msgstr "Pokreće se kada se status sistema promijeni"
|
||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||
msgstr "Pokreće se kada iskorištenost bilo kojeg diska premaši prag"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Type"
|
||||
msgstr "Vrsta"
|
||||
@@ -1476,7 +1610,7 @@ msgid "Unlimited"
|
||||
msgstr "Neograničeno"
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Up"
|
||||
msgstr "Sustav je podignut"
|
||||
@@ -1485,7 +1619,12 @@ msgstr "Sustav je podignut"
|
||||
msgid "Up ({upSystemsLength})"
|
||||
msgstr "Sustav je podignut ({upSystemsLength})"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Update"
|
||||
msgstr "Ažuriraj"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Updated"
|
||||
msgstr "Ažurirano"
|
||||
@@ -1498,7 +1637,7 @@ msgstr "Ažurirano svakih 10 minuta."
|
||||
msgid "Upload"
|
||||
msgstr "Otpremi"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "Uptime"
|
||||
msgstr "Vrijeme rada"
|
||||
|
||||
@@ -1570,8 +1709,8 @@ msgid "Webhook / Push notifications"
|
||||
msgstr "Webhook / Push obavijest"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation. Expires after one hour or on hub restart."
|
||||
msgstr "Kada je podešen, ovaj token dopušta agentima da se prijave bez prvobitnog stvaranja sustava. Ističe nakon jednog sata ili ponovnog pokretanja središnje kontrole."
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation."
|
||||
msgstr "Kada je omogućen, ovaj token omogućuje agentima da se sami registriraju bez prethodnog stvaranja sustava."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: hu\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-10-28 22:59\n"
|
||||
"PO-Revision-Date: 2025-12-02 23:18\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Hungarian\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -24,6 +24,10 @@ msgstr ""
|
||||
msgid "{0} of {1} row(s) selected."
|
||||
msgstr "{0} a(z) {1} sorból kiválasztva."
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "{cores, plural, one {# core} other {# cores}}"
|
||||
msgstr "{cores, plural, one {# mag} other {# mag}}"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "{count, plural, one {{countString} day} other {{countString} days}}"
|
||||
msgstr "{count, plural, one {{countString} nap} other {{countString} nap}}"
|
||||
@@ -36,6 +40,10 @@ msgstr "{count, plural, one {{countString} óra} other {{countString} óra}}"
|
||||
msgid "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
|
||||
msgstr "{count, plural, one {{countString} perc} few {{countString} perc} many {{countString} perc} other {{countString} perc}}"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "{threads, plural, one {# thread} other {# threads}}"
|
||||
msgstr "{threads, plural, one {# szál} other {# szál}}"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 hour"
|
||||
msgstr "1 óra"
|
||||
@@ -76,13 +84,16 @@ msgid "5 min"
|
||||
msgstr "5 perc"
|
||||
|
||||
#. Table column
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Actions"
|
||||
msgstr "Műveletek"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Active"
|
||||
msgstr "Aktív"
|
||||
|
||||
@@ -95,12 +106,14 @@ msgid "Active state"
|
||||
msgstr "Aktív állapot"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add <0>System</0>"
|
||||
msgstr "Hozzáadás <0>System</0>"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Add {foo}"
|
||||
msgstr "Hozzáadás {foo}"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add New System"
|
||||
msgstr "Új rendszer hozzáadása"
|
||||
msgid "Add <0>System</0>"
|
||||
msgstr "Hozzáadás <0>System</0>"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add system"
|
||||
@@ -151,6 +164,7 @@ msgstr "Összes konténer"
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/home.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "All Systems"
|
||||
@@ -176,6 +190,11 @@ msgstr "Átlag"
|
||||
msgid "Average CPU utilization of containers"
|
||||
msgstr "Konténerek átlagos CPU kihasználtsága"
|
||||
|
||||
#. placeholder {0}: alertData.unit
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "Average drops below <0>{value}{0}</0>"
|
||||
msgstr "Az átlag esik <0>{value}{0}</0> alá"
|
||||
|
||||
#. placeholder {0}: alertData.unit
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "Average exceeds <0>{value}{0}</0>"
|
||||
@@ -208,7 +227,13 @@ msgstr "Biztonsági mentések"
|
||||
msgid "Bandwidth"
|
||||
msgstr "Sávszélesség"
|
||||
|
||||
#. Battery label in systems table header
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Bat"
|
||||
msgstr "Akk"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Battery"
|
||||
msgstr "Akkumulátor"
|
||||
|
||||
@@ -224,6 +249,13 @@ msgstr "Inaktívvá vált"
|
||||
msgid "Before"
|
||||
msgstr "Előtte"
|
||||
|
||||
#. placeholder {0}: alert.value
|
||||
#. placeholder {1}: info.unit
|
||||
#. placeholder {2}: alert.min
|
||||
#: src/components/active-alerts.tsx
|
||||
msgid "Below {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
|
||||
msgstr "{0}{1} alatt az elmúlt {2, plural, one {# percben} other {# percben}}"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Beszel supports OpenID Connect and many OAuth2 authentication providers."
|
||||
msgstr "A Beszel támogatja az OpenID Connect-et és számos OAuth2 hitelesítési szolgáltatót."
|
||||
@@ -267,6 +299,7 @@ msgid "Can stop"
|
||||
msgstr "Leállítható"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Cancel"
|
||||
msgstr "Mégsem"
|
||||
@@ -320,6 +353,12 @@ msgstr "Ellenőrizd a naplót a további részletekért."
|
||||
msgid "Check your notification service"
|
||||
msgstr "Ellenőrizd az értesítési szolgáltatásodat"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Clear"
|
||||
msgstr "Törlés"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
msgid "Click on a container to view more information."
|
||||
msgstr "Kattintson egy konténerre a további információk megtekintéséhez."
|
||||
@@ -442,6 +481,10 @@ msgstr "CPU idő felbontása"
|
||||
msgid "CPU Usage"
|
||||
msgstr "CPU használat"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Create"
|
||||
msgstr "Létrehozás"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Create account"
|
||||
msgstr "Fiók létrehozása"
|
||||
@@ -473,15 +516,18 @@ msgstr "Jelenlegi állapot"
|
||||
msgid "Cycles"
|
||||
msgstr "Ciklusok"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Dashboard"
|
||||
msgstr "Áttekintés"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Daily"
|
||||
msgstr "Napi"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Default time period"
|
||||
msgstr "Alapértelmezett időszak"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Delete"
|
||||
msgstr "Törlés"
|
||||
@@ -548,7 +594,7 @@ msgstr "Dokumentáció"
|
||||
|
||||
#. Context: System is down
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Down"
|
||||
@@ -566,11 +612,16 @@ msgstr "Letöltés"
|
||||
msgid "Duration"
|
||||
msgstr "Időtartam"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Edit"
|
||||
msgstr "Szerkesztés"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Edit {foo}"
|
||||
msgstr "Szerkesztés {foo}"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
#: src/components/login/otp-forms.tsx
|
||||
@@ -586,6 +637,11 @@ msgstr "E-mail értesítések"
|
||||
msgid "Empty"
|
||||
msgstr "Üres"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "End Time"
|
||||
msgstr "Befejezés ideje"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Enter email address to reset password"
|
||||
msgstr "E-mail cím megadása a jelszó visszaállításához"
|
||||
@@ -598,10 +654,16 @@ msgstr "Adja meg az e-mail címet..."
|
||||
msgid "Enter your one-time password."
|
||||
msgstr "Adja meg az egyszeri jelszavát."
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Ephemeral"
|
||||
msgstr "Átmeneti"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Error"
|
||||
@@ -616,7 +678,7 @@ msgstr "Túllépi a {0}{1} értéket az elmúlt {2, plural, one {# percben} othe
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Exec main PID"
|
||||
msgstr ""
|
||||
msgstr "Fő folyamat PID"
|
||||
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
|
||||
@@ -626,6 +688,10 @@ msgstr "A <0>config.yml</0> fájlban nem definiált meglévő rendszerek törlé
|
||||
msgid "Exited active"
|
||||
msgstr "Aktívként kilépett"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Expires after one hour or on hub restart."
|
||||
msgstr "Lejár egy óra után vagy a hub újraindításakor."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr "Exportálás"
|
||||
@@ -656,6 +722,7 @@ msgstr "Hitelesítés sikertelen"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Failed to save settings"
|
||||
msgstr "Nem sikerült menteni a beállításokat"
|
||||
|
||||
@@ -687,7 +754,7 @@ msgstr "Ujjlenyomat"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Firmware"
|
||||
msgstr ""
|
||||
msgstr "Firmware"
|
||||
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "For <0>{min}</0> {min, plural, one {minute} other {minutes}}"
|
||||
@@ -714,6 +781,10 @@ msgstr "Tele"
|
||||
msgid "General"
|
||||
msgstr "Általános"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Global"
|
||||
msgstr "Globális"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "GPU Engines"
|
||||
msgstr "GPU-k"
|
||||
@@ -758,15 +829,14 @@ msgctxt "Docker image"
|
||||
msgid "Image"
|
||||
msgstr "Kép"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Inactive"
|
||||
msgstr "Inaktív"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Invalid email address."
|
||||
msgstr "Érvénytelen e-mail cím."
|
||||
|
||||
#. Linux kernel
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Kernel"
|
||||
msgstr "Kernel"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "Nyelv"
|
||||
@@ -842,7 +912,7 @@ msgstr "Inkább azt keresi, hogy hol hozhat létre riasztásokat? Kattintson a c
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Main PID"
|
||||
msgstr ""
|
||||
msgstr "Fő PID"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Manage display and notification preferences."
|
||||
@@ -859,6 +929,7 @@ msgid "Max 1 min"
|
||||
msgstr "Maximum 1 perc"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
@@ -959,12 +1030,19 @@ msgstr "OAuth 2 / OIDC támogatás"
|
||||
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
|
||||
msgstr "Minden újraindításkor az adatbázisban lévő rendszerek frissítésre kerülnek, hogy megfeleljenek a fájlban meghatározott rendszereknek."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "One-time"
|
||||
msgstr "Egyszeri"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "One-time password"
|
||||
msgstr "Egyszeri jelszó"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Open menu"
|
||||
msgstr "Menü megnyitása"
|
||||
@@ -981,6 +1059,7 @@ msgstr "Egyéb"
|
||||
msgid "Overwrite existing alerts"
|
||||
msgstr "Felülírja a meglévő riasztásokat"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Page"
|
||||
@@ -1013,6 +1092,10 @@ msgstr "A jelszó legfeljebb 72 byte lehet."
|
||||
msgid "Password reset request received"
|
||||
msgstr "Jelszó-visszaállítási kérelmet kaptunk"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Past"
|
||||
msgstr "Múlt"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Pause"
|
||||
msgstr "Szüneteltetés"
|
||||
@@ -1034,6 +1117,14 @@ msgstr "Átlagos kihasználtság magonként"
|
||||
msgid "Percentage of time spent in each state"
|
||||
msgstr "Az idő százalékos aránya minden állapotban"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Permanent"
|
||||
msgstr "Állandó"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Persistence"
|
||||
msgstr "Kitartás"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
msgstr "Kérjük, <0>konfigurálj egy SMTP szervert</0> az értesítések kézbesítésének biztosítása érdekében."
|
||||
@@ -1094,6 +1185,10 @@ msgstr "Folyamat elindítva"
|
||||
msgid "Public Key"
|
||||
msgstr "Nyilvános kulcs"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Quiet Hours"
|
||||
msgstr "Csendes órák"
|
||||
|
||||
#. Disk read
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
@@ -1106,6 +1201,7 @@ msgstr "Fogadott"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Refresh"
|
||||
msgstr "Frissítés"
|
||||
|
||||
@@ -1185,6 +1281,22 @@ msgstr "Beállítások mentése"
|
||||
msgid "Save system"
|
||||
msgstr "Rendszer mentése"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Saved in the database and does not expire until you disable it."
|
||||
msgstr "Elmentve az adatbázisban és nem jár le, amíg ki nem kapcsolod."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule"
|
||||
msgstr "Ütemezés"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule quiet hours where notifications will not be sent, such as during maintenance periods."
|
||||
msgstr "Ütemezze a csendes órákat, amikor az értesítések nem kerülnek elküldésre, például karbantartási időszakokban."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule quiet hours where notifications will not be sent."
|
||||
msgstr "Ütemezze a csendes órákat, amikor az értesítések nem kerülnek elküldésre."
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Search"
|
||||
msgstr "Keresés"
|
||||
@@ -1197,6 +1309,10 @@ msgstr "Keresés rendszerek vagy beállítások után..."
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "Lásd <0>az értesítési beállításokat</0>, hogy konfigurálja, hogyan kap értesítéseket."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Select {foo}"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "Elküldve"
|
||||
@@ -1240,8 +1356,14 @@ msgstr "SMTP beállítások"
|
||||
msgid "Sort By"
|
||||
msgstr "Rendezés"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Start Time"
|
||||
msgstr "Kezdési idő"
|
||||
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "Állapot"
|
||||
@@ -1266,9 +1388,15 @@ msgstr "Rendszer által használt swap terület"
|
||||
msgid "Swap Usage"
|
||||
msgstr "Swap használat"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "System"
|
||||
@@ -1353,8 +1481,8 @@ msgstr "Időformátum"
|
||||
msgid "To email(s)"
|
||||
msgstr "E-mailben"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "Toggle grid"
|
||||
msgstr "Rács ki- és bekapcsolása"
|
||||
|
||||
@@ -1423,6 +1551,10 @@ msgstr "Riaszt, ha az 5 perces terhelési átlag túllép egy küszöbértéket"
|
||||
msgid "Triggers when any sensor exceeds a threshold"
|
||||
msgstr "Bekapcsol, ha bármelyik érzékelő túllép egy küszöbértéket"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when battery charge drops below a threshold"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when combined up/down exceeds a threshold"
|
||||
msgstr "Bekapcsol, ha bármelyik érzékelő túllép egy küszöbértéket"
|
||||
@@ -1447,6 +1579,8 @@ msgstr "Bekapcsol, amikor az állapot fel és le között változik"
|
||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||
msgstr "Bekapcsol, ha a lemez érzékelő túllép egy küszöbértéket"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Type"
|
||||
msgstr "Típus"
|
||||
@@ -1476,7 +1610,7 @@ msgid "Unlimited"
|
||||
msgstr "Korlátlan"
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Up"
|
||||
msgstr "Online"
|
||||
@@ -1485,7 +1619,12 @@ msgstr "Online"
|
||||
msgid "Up ({upSystemsLength})"
|
||||
msgstr "Online ({upSystemsLength})"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Update"
|
||||
msgstr "Frissítés"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Updated"
|
||||
msgstr "Frissítve"
|
||||
@@ -1498,7 +1637,7 @@ msgstr "10 percenként frissítve."
|
||||
msgid "Upload"
|
||||
msgstr "Feltöltés"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "Uptime"
|
||||
msgstr "Üzemidő"
|
||||
|
||||
@@ -1570,8 +1709,8 @@ msgid "Webhook / Push notifications"
|
||||
msgstr "Webhook / Push értesítések"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation. Expires after one hour or on hub restart."
|
||||
msgstr "Ha engedélyezve van, ez a token lehetővé teszi az ügynökök önregisztrációját előzetes rendszerlétrehozás nélkül. Egy óra után vagy a hub újraindításakor lejár."
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation."
|
||||
msgstr "Ha engedélyezve van, ez a token lehetővé teszi az ügynökök számára az önregisztrációt rendszer előzetes létrehozása nélkül."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: it\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-10-30 21:53\n"
|
||||
"PO-Revision-Date: 2025-12-02 23:17\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Italian\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -24,6 +24,10 @@ msgstr ""
|
||||
msgid "{0} of {1} row(s) selected."
|
||||
msgstr "{0} di {1} righe selezionate."
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "{cores, plural, one {# core} other {# cores}}"
|
||||
msgstr "{cores, plural, one {# core} other {# core}}"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "{count, plural, one {{countString} day} other {{countString} days}}"
|
||||
msgstr "{count, plural, one {{countString} giorno} other {{countString} giorni}}"
|
||||
@@ -36,6 +40,10 @@ msgstr "{count, plural, one {{countString} ora} other {{countString} ore}}"
|
||||
msgid "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
|
||||
msgstr "{count, plural, one {{countString} minuto} other {{countString} minuti}}"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "{threads, plural, one {# thread} other {# threads}}"
|
||||
msgstr "{threads, plural, one {# thread} other {# thread}}"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 hour"
|
||||
msgstr "1 ora"
|
||||
@@ -76,13 +84,16 @@ msgid "5 min"
|
||||
msgstr "5 min"
|
||||
|
||||
#. Table column
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Actions"
|
||||
msgstr "Azioni"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Active"
|
||||
msgstr "Attivo"
|
||||
|
||||
@@ -95,12 +106,14 @@ msgid "Active state"
|
||||
msgstr "Stato attivo"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add <0>System</0>"
|
||||
msgstr "Aggiungi <0>Sistema</0>"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Add {foo}"
|
||||
msgstr "Aggiungi {foo}"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add New System"
|
||||
msgstr "Aggiungi Nuovo Sistema"
|
||||
msgid "Add <0>System</0>"
|
||||
msgstr "Aggiungi <0>Sistema</0>"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add system"
|
||||
@@ -151,6 +164,7 @@ msgstr "Tutti i contenitori"
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/home.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "All Systems"
|
||||
@@ -176,6 +190,11 @@ msgstr "Media"
|
||||
msgid "Average CPU utilization of containers"
|
||||
msgstr "Utilizzo medio della CPU dei container"
|
||||
|
||||
#. placeholder {0}: alertData.unit
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "Average drops below <0>{value}{0}</0>"
|
||||
msgstr "La media scende sotto <0>{value}{0}</0>"
|
||||
|
||||
#. placeholder {0}: alertData.unit
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "Average exceeds <0>{value}{0}</0>"
|
||||
@@ -208,7 +227,13 @@ msgstr "Backup"
|
||||
msgid "Bandwidth"
|
||||
msgstr "Larghezza di banda"
|
||||
|
||||
#. Battery label in systems table header
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Bat"
|
||||
msgstr "Bat"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Battery"
|
||||
msgstr "Batteria"
|
||||
|
||||
@@ -224,6 +249,13 @@ msgstr "Diventato inattivo"
|
||||
msgid "Before"
|
||||
msgstr "Prima"
|
||||
|
||||
#. placeholder {0}: alert.value
|
||||
#. placeholder {1}: info.unit
|
||||
#. placeholder {2}: alert.min
|
||||
#: src/components/active-alerts.tsx
|
||||
msgid "Below {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
|
||||
msgstr "Sotto {0}{1} negli ultimi {2, plural, one {# minuto} other {# minuti}}"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Beszel supports OpenID Connect and many OAuth2 authentication providers."
|
||||
msgstr "Beszel supporta OpenID Connect e molti provider di autenticazione OAuth2."
|
||||
@@ -267,6 +299,7 @@ msgid "Can stop"
|
||||
msgstr "Può fermare"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Cancel"
|
||||
msgstr "Annulla"
|
||||
@@ -320,6 +353,12 @@ msgstr "Controlla i log per maggiori dettagli."
|
||||
msgid "Check your notification service"
|
||||
msgstr "Controlla il tuo servizio di notifica"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Clear"
|
||||
msgstr "Cancella"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
msgid "Click on a container to view more information."
|
||||
msgstr "Fare clic su un contenitore per visualizzare ulteriori informazioni."
|
||||
@@ -442,6 +481,10 @@ msgstr "Suddivisione tempo CPU"
|
||||
msgid "CPU Usage"
|
||||
msgstr "Utilizzo CPU"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Create"
|
||||
msgstr "Crea"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Create account"
|
||||
msgstr "Crea account"
|
||||
@@ -473,15 +516,18 @@ msgstr "Stato attuale"
|
||||
msgid "Cycles"
|
||||
msgstr "Cicli"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Dashboard"
|
||||
msgstr "Cruscotto"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Daily"
|
||||
msgstr "Giornaliero"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Default time period"
|
||||
msgstr "Periodo di tempo predefinito"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Delete"
|
||||
msgstr "Elimina"
|
||||
@@ -548,7 +594,7 @@ msgstr "Documentazione"
|
||||
|
||||
#. Context: System is down
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Down"
|
||||
@@ -566,11 +612,16 @@ msgstr "Scarica"
|
||||
msgid "Duration"
|
||||
msgstr "Durata"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Edit"
|
||||
msgstr "Modifica"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Edit {foo}"
|
||||
msgstr "Modifica {foo}"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
#: src/components/login/otp-forms.tsx
|
||||
@@ -586,6 +637,11 @@ msgstr "Notifiche email"
|
||||
msgid "Empty"
|
||||
msgstr "Vuota"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "End Time"
|
||||
msgstr "Ora di fine"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Enter email address to reset password"
|
||||
msgstr "Inserisci l'indirizzo email per reimpostare la password"
|
||||
@@ -598,10 +654,16 @@ msgstr "Inserisci l'indirizzo email..."
|
||||
msgid "Enter your one-time password."
|
||||
msgstr "Inserisci la tua password monouso."
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Ephemeral"
|
||||
msgstr "Effimero"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Error"
|
||||
@@ -626,6 +688,10 @@ msgstr "I sistemi esistenti non definiti in <0>config.yml</0> verranno eliminati
|
||||
msgid "Exited active"
|
||||
msgstr "Uscito attivo"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Expires after one hour or on hub restart."
|
||||
msgstr "Scade dopo un'ora o al riavvio dell'hub."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr "Esporta"
|
||||
@@ -656,6 +722,7 @@ msgstr "Autenticazione fallita"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Failed to save settings"
|
||||
msgstr "Salvataggio delle impostazioni fallito"
|
||||
|
||||
@@ -714,6 +781,10 @@ msgstr "Piena"
|
||||
msgid "General"
|
||||
msgstr "Generale"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Global"
|
||||
msgstr "Globale"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "GPU Engines"
|
||||
msgstr "Motori GPU"
|
||||
@@ -758,15 +829,14 @@ msgctxt "Docker image"
|
||||
msgid "Image"
|
||||
msgstr "Immagine"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Inactive"
|
||||
msgstr "Inattivo"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Invalid email address."
|
||||
msgstr "Indirizzo email non valido."
|
||||
|
||||
#. Linux kernel
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Kernel"
|
||||
msgstr "Kernel"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "Lingua"
|
||||
@@ -859,6 +929,7 @@ msgid "Max 1 min"
|
||||
msgstr "Max 1 min"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
@@ -920,7 +991,7 @@ msgstr "Unità rete"
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "No"
|
||||
msgstr "No"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
@@ -959,12 +1030,19 @@ msgstr "Supporto OAuth 2 / OIDC"
|
||||
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
|
||||
msgstr "Ad ogni riavvio, i sistemi nel database verranno aggiornati per corrispondere ai sistemi definiti nel file."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "One-time"
|
||||
msgstr "Una volta"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "One-time password"
|
||||
msgstr "Password monouso"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Open menu"
|
||||
msgstr "Apri menu"
|
||||
@@ -981,6 +1059,7 @@ msgstr "Altro"
|
||||
msgid "Overwrite existing alerts"
|
||||
msgstr "Sovrascrivi avvisi esistenti"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Page"
|
||||
@@ -1013,6 +1092,10 @@ msgstr "La password deve essere inferiore a 72 byte."
|
||||
msgid "Password reset request received"
|
||||
msgstr "Richiesta di reimpostazione password ricevuta"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Past"
|
||||
msgstr "Passato"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Pause"
|
||||
msgstr "Pausa"
|
||||
@@ -1034,6 +1117,14 @@ msgstr "Utilizzo medio per core"
|
||||
msgid "Percentage of time spent in each state"
|
||||
msgstr "Percentuale di tempo trascorso in ogni stato"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Permanent"
|
||||
msgstr "Permanente"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Persistence"
|
||||
msgstr "Persistenza"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
msgstr "Si prega di <0>configurare un server SMTP</0> per garantire la consegna degli avvisi."
|
||||
@@ -1094,6 +1185,10 @@ msgstr "Processo avviato"
|
||||
msgid "Public Key"
|
||||
msgstr "Chiave Pub"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Quiet Hours"
|
||||
msgstr "Ore silenziose"
|
||||
|
||||
#. Disk read
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
@@ -1106,6 +1201,7 @@ msgstr "Ricevuto"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Refresh"
|
||||
msgstr "Aggiorna"
|
||||
|
||||
@@ -1185,6 +1281,22 @@ msgstr "Salva Impostazioni"
|
||||
msgid "Save system"
|
||||
msgstr "Salva sistema"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Saved in the database and does not expire until you disable it."
|
||||
msgstr "Salvato nel database e non scade finché non lo disabiliti."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule"
|
||||
msgstr "Pianifica"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule quiet hours where notifications will not be sent, such as during maintenance periods."
|
||||
msgstr "Pianifica le ore silenziose in cui le notifiche non verranno inviate, ad esempio durante i periodi di manutenzione."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule quiet hours where notifications will not be sent."
|
||||
msgstr "Pianifica le ore silenziose in cui le notifiche non verranno inviate."
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Search"
|
||||
msgstr "Cerca"
|
||||
@@ -1197,6 +1309,10 @@ msgstr "Cerca sistemi o impostazioni..."
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "Vedi <0>impostazioni di notifica</0> per configurare come ricevere gli avvisi."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Select {foo}"
|
||||
msgstr "Seleziona {foo}"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "Inviato"
|
||||
@@ -1240,8 +1356,14 @@ msgstr "Impostazioni SMTP"
|
||||
msgid "Sort By"
|
||||
msgstr "Ordina per"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Start Time"
|
||||
msgstr "Ora di inizio"
|
||||
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "Stato"
|
||||
@@ -1266,9 +1388,15 @@ msgstr "Spazio di swap utilizzato dal sistema"
|
||||
msgid "Swap Usage"
|
||||
msgstr "Utilizzo Swap"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "System"
|
||||
@@ -1353,8 +1481,8 @@ msgstr "Formato orario"
|
||||
msgid "To email(s)"
|
||||
msgstr "A email(s)"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "Toggle grid"
|
||||
msgstr "Attiva/disattiva griglia"
|
||||
|
||||
@@ -1423,6 +1551,10 @@ msgstr "Si attiva quando la media di carico di 5 minuti supera una soglia"
|
||||
msgid "Triggers when any sensor exceeds a threshold"
|
||||
msgstr "Attiva quando un sensore supera una soglia"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when battery charge drops below a threshold"
|
||||
msgstr "Attiva quando la carica della batteria scende sotto una soglia"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when combined up/down exceeds a threshold"
|
||||
msgstr "Attiva quando il combinato up/down supera una soglia"
|
||||
@@ -1447,6 +1579,8 @@ msgstr "Attiva quando lo stato passa tra up e down"
|
||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||
msgstr "Attiva quando l'utilizzo di un disco supera una soglia"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Type"
|
||||
msgstr "Tipo"
|
||||
@@ -1476,7 +1610,7 @@ msgid "Unlimited"
|
||||
msgstr "Illimitato"
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Up"
|
||||
msgstr "Attivo"
|
||||
@@ -1485,7 +1619,12 @@ msgstr "Attivo"
|
||||
msgid "Up ({upSystemsLength})"
|
||||
msgstr "Attivo ({upSystemsLength})"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Update"
|
||||
msgstr "Aggiorna"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Updated"
|
||||
msgstr "Aggiornato"
|
||||
@@ -1498,7 +1637,7 @@ msgstr "Aggiornato ogni 10 minuti."
|
||||
msgid "Upload"
|
||||
msgstr "Carica"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "Uptime"
|
||||
msgstr "Tempo di attività"
|
||||
|
||||
@@ -1570,8 +1709,8 @@ msgid "Webhook / Push notifications"
|
||||
msgstr "Notifiche Webhook / Push"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation. Expires after one hour or on hub restart."
|
||||
msgstr "Quando abilitato, questo token consente agli agenti di auto-registrarsi senza creazione preventiva del sistema. Scade dopo un'ora o al riavvio dell'hub."
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation."
|
||||
msgstr "Quando abilitato, questo token consente agli agenti di registrarsi automaticamente senza creazione preventiva del sistema."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: ja\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-10-28 23:00\n"
|
||||
"PO-Revision-Date: 2025-12-02 23:18\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Japanese\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
@@ -24,6 +24,10 @@ msgstr ""
|
||||
msgid "{0} of {1} row(s) selected."
|
||||
msgstr "{1}行のうち{0}行が選択されました。"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "{cores, plural, one {# core} other {# cores}}"
|
||||
msgstr "{cores, plural, one {# コア} other {# コア}}"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "{count, plural, one {{countString} day} other {{countString} days}}"
|
||||
msgstr "{count, plural, one {{countString} 日} other {{countString} 日}}"
|
||||
@@ -36,6 +40,10 @@ msgstr "{count, plural, one {{countString} 時間} other {{countString} 時間}}
|
||||
msgid "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
|
||||
msgstr "{count, plural, one {{countString} 分} few {{countString} 分} many {{countString} 分} other {{countString} 分}}"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "{threads, plural, one {# thread} other {# threads}}"
|
||||
msgstr "{threads, plural, one {# スレッド} other {# スレッド}}"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 hour"
|
||||
msgstr "1時間"
|
||||
@@ -76,13 +84,16 @@ msgid "5 min"
|
||||
msgstr "5分"
|
||||
|
||||
#. Table column
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Actions"
|
||||
msgstr "アクション"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Active"
|
||||
msgstr "アクティブ"
|
||||
|
||||
@@ -95,12 +106,14 @@ msgid "Active state"
|
||||
msgstr "アクティブ状態"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add <0>System</0>"
|
||||
msgstr "<0>システム</0>を追加"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Add {foo}"
|
||||
msgstr "{foo}を追加"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add New System"
|
||||
msgstr "新しいシステムを追加"
|
||||
msgid "Add <0>System</0>"
|
||||
msgstr "<0>システム</0>を追加"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add system"
|
||||
@@ -151,6 +164,7 @@ msgstr "すべてのコンテナ"
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/home.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "All Systems"
|
||||
@@ -176,6 +190,11 @@ msgstr "平均"
|
||||
msgid "Average CPU utilization of containers"
|
||||
msgstr "コンテナの平均CPU使用率"
|
||||
|
||||
#. placeholder {0}: alertData.unit
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "Average drops below <0>{value}{0}</0>"
|
||||
msgstr "平均が<0>{value}{0}</0>を下回っています"
|
||||
|
||||
#. placeholder {0}: alertData.unit
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "Average exceeds <0>{value}{0}</0>"
|
||||
@@ -208,7 +227,13 @@ msgstr "バックアップ"
|
||||
msgid "Bandwidth"
|
||||
msgstr "帯域幅"
|
||||
|
||||
#. Battery label in systems table header
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Bat"
|
||||
msgstr "バッテリー"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Battery"
|
||||
msgstr "バッテリー"
|
||||
|
||||
@@ -224,6 +249,13 @@ msgstr "非アクティブになった"
|
||||
msgid "Before"
|
||||
msgstr "前"
|
||||
|
||||
#. placeholder {0}: alert.value
|
||||
#. placeholder {1}: info.unit
|
||||
#. placeholder {2}: alert.min
|
||||
#: src/components/active-alerts.tsx
|
||||
msgid "Below {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
|
||||
msgstr "過去{2, plural, one {# 分} other {# 分}}で{0}{1}を下回っています"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Beszel supports OpenID Connect and many OAuth2 authentication providers."
|
||||
msgstr "BeszelはOpenID Connectと多くのOAuth2認証プロバイダーをサポートしています。"
|
||||
@@ -267,6 +299,7 @@ msgid "Can stop"
|
||||
msgstr "停止可能"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Cancel"
|
||||
msgstr "キャンセル"
|
||||
@@ -320,6 +353,12 @@ msgstr "詳細についてはログを確認してください。"
|
||||
msgid "Check your notification service"
|
||||
msgstr "通知サービスを確認してください"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Clear"
|
||||
msgstr "クリア"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
msgid "Click on a container to view more information."
|
||||
msgstr "詳細情報を表示するにはコンテナをクリックしてください。"
|
||||
@@ -442,6 +481,10 @@ msgstr "CPU 時間の内訳"
|
||||
msgid "CPU Usage"
|
||||
msgstr "CPU使用率"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Create"
|
||||
msgstr "作成"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Create account"
|
||||
msgstr "アカウントを作成"
|
||||
@@ -473,15 +516,18 @@ msgstr "現在の状態"
|
||||
msgid "Cycles"
|
||||
msgstr "サイクル"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Dashboard"
|
||||
msgstr "ダッシュボード"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Daily"
|
||||
msgstr "毎日"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Default time period"
|
||||
msgstr "デフォルトの期間"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Delete"
|
||||
msgstr "削除"
|
||||
@@ -548,7 +594,7 @@ msgstr "ドキュメント"
|
||||
|
||||
#. Context: System is down
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Down"
|
||||
@@ -566,11 +612,16 @@ msgstr "ダウンロード"
|
||||
msgid "Duration"
|
||||
msgstr "期間"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Edit"
|
||||
msgstr "編集"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Edit {foo}"
|
||||
msgstr "{foo}を編集"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
#: src/components/login/otp-forms.tsx
|
||||
@@ -586,6 +637,11 @@ msgstr "メール通知"
|
||||
msgid "Empty"
|
||||
msgstr "空"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "End Time"
|
||||
msgstr "終了時間"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Enter email address to reset password"
|
||||
msgstr "パスワードをリセットするためにメールアドレスを入力してください"
|
||||
@@ -598,10 +654,16 @@ msgstr "メールアドレスを入力..."
|
||||
msgid "Enter your one-time password."
|
||||
msgstr "ワンタイムパスワードを入力してください。"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Ephemeral"
|
||||
msgstr "一時的"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Error"
|
||||
@@ -626,6 +688,10 @@ msgstr "<0>config.yml</0>に定義されていない既存のシステムは削
|
||||
msgid "Exited active"
|
||||
msgstr "アクティブ状態で終了"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Expires after one hour or on hub restart."
|
||||
msgstr "1時間後、またはハブの再起動時に有効期限が切れます。"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr "エクスポート"
|
||||
@@ -656,6 +722,7 @@ msgstr "認証に失敗しました"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Failed to save settings"
|
||||
msgstr "設定の保存に失敗しました"
|
||||
|
||||
@@ -714,6 +781,10 @@ msgstr "満充電"
|
||||
msgid "General"
|
||||
msgstr "一般"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Global"
|
||||
msgstr "グローバル"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "GPU Engines"
|
||||
msgstr "GPUエンジン"
|
||||
@@ -758,15 +829,14 @@ msgctxt "Docker image"
|
||||
msgid "Image"
|
||||
msgstr "イメージ"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Inactive"
|
||||
msgstr "非アクティブ"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Invalid email address."
|
||||
msgstr "無効なメールアドレスです。"
|
||||
|
||||
#. Linux kernel
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Kernel"
|
||||
msgstr "カーネル"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "言語"
|
||||
@@ -859,6 +929,7 @@ msgid "Max 1 min"
|
||||
msgstr "最大1分"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
@@ -959,12 +1030,19 @@ msgstr "OAuth 2 / OIDCサポート"
|
||||
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
|
||||
msgstr "再起動のたびに、データベース内のシステムはファイルに定義されたシステムに一致するように更新されます。"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "One-time"
|
||||
msgstr "1回限り"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "One-time password"
|
||||
msgstr "ワンタイムパスワード"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Open menu"
|
||||
msgstr "メニューを開く"
|
||||
@@ -981,6 +1059,7 @@ msgstr "その他"
|
||||
msgid "Overwrite existing alerts"
|
||||
msgstr "既存のアラートを上書き"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Page"
|
||||
@@ -1013,6 +1092,10 @@ msgstr "パスワードは72バイト未満でなければなりません。"
|
||||
msgid "Password reset request received"
|
||||
msgstr "パスワードリセットのリクエストを受け取りました"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Past"
|
||||
msgstr "過去"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Pause"
|
||||
msgstr "一時停止"
|
||||
@@ -1034,6 +1117,14 @@ msgstr "コアごとの平均使用率"
|
||||
msgid "Percentage of time spent in each state"
|
||||
msgstr "各状態で費やした時間の割合"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Permanent"
|
||||
msgstr "永久"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Persistence"
|
||||
msgstr "永続性"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
msgstr "アラートが配信されるように<0>SMTPサーバーを設定</0>してください。"
|
||||
@@ -1094,6 +1185,10 @@ msgstr "プロセス開始"
|
||||
msgid "Public Key"
|
||||
msgstr "公開鍵"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Quiet Hours"
|
||||
msgstr "サイレント時間"
|
||||
|
||||
#. Disk read
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
@@ -1106,6 +1201,7 @@ msgstr "受信"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Refresh"
|
||||
msgstr "更新"
|
||||
|
||||
@@ -1150,7 +1246,7 @@ msgstr "再開"
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgctxt "Root disk label"
|
||||
msgid "Root"
|
||||
msgstr ""
|
||||
msgstr "ルート"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Rotate token"
|
||||
@@ -1185,6 +1281,22 @@ msgstr "設定を保存"
|
||||
msgid "Save system"
|
||||
msgstr "システムを保存"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Saved in the database and does not expire until you disable it."
|
||||
msgstr "データベースに保存され、無効にするまで有効期限が切れません。"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule"
|
||||
msgstr "スケジュール"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule quiet hours where notifications will not be sent, such as during maintenance periods."
|
||||
msgstr "メンテナンス期間中などの通知が送信されないサイレント時間をスケジュールします。"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule quiet hours where notifications will not be sent."
|
||||
msgstr "通知が送信されないサイレント時間をスケジュールします。"
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Search"
|
||||
msgstr "検索"
|
||||
@@ -1197,6 +1309,10 @@ msgstr "システムまたは設定を検索..."
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "アラートの受信方法を設定するには<0>通知設定</0>を参照してください。"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Select {foo}"
|
||||
msgstr "{foo}を選択"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "送信"
|
||||
@@ -1240,8 +1356,14 @@ msgstr "SMTP設定"
|
||||
msgid "Sort By"
|
||||
msgstr "並び替え基準"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Start Time"
|
||||
msgstr "開始時間"
|
||||
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "状態"
|
||||
@@ -1266,9 +1388,15 @@ msgstr "システムが使用するスワップ領域"
|
||||
msgid "Swap Usage"
|
||||
msgstr "スワップ使用量"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "System"
|
||||
@@ -1353,8 +1481,8 @@ msgstr "時間形式"
|
||||
msgid "To email(s)"
|
||||
msgstr "宛先メールアドレス"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "Toggle grid"
|
||||
msgstr "グリッドを切り替え"
|
||||
|
||||
@@ -1423,6 +1551,10 @@ msgstr "5分間の負荷平均がしきい値を超えたときにトリガー
|
||||
msgid "Triggers when any sensor exceeds a threshold"
|
||||
msgstr "センサーがしきい値を超えたときにトリガーされます"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when battery charge drops below a threshold"
|
||||
msgstr "バッテリーの充電量がしきい値を下回ったときにトリガーされます"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when combined up/down exceeds a threshold"
|
||||
msgstr "上り/下りの合計がしきい値を超えたときにトリガーされます"
|
||||
@@ -1447,6 +1579,8 @@ msgstr "ステータスが上から下に切り替わるときにトリガーさ
|
||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||
msgstr "ディスクの使用量がしきい値を超えたときにトリガーされます"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Type"
|
||||
msgstr "タイプ"
|
||||
@@ -1476,7 +1610,7 @@ msgid "Unlimited"
|
||||
msgstr "無制限"
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Up"
|
||||
msgstr "正常"
|
||||
@@ -1485,7 +1619,12 @@ msgstr "正常"
|
||||
msgid "Up ({upSystemsLength})"
|
||||
msgstr "正常 ({upSystemsLength})"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Update"
|
||||
msgstr "更新"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Updated"
|
||||
msgstr "更新済み"
|
||||
@@ -1498,7 +1637,7 @@ msgstr "10分ごとに更新されます。"
|
||||
msgid "Upload"
|
||||
msgstr "アップロード"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "Uptime"
|
||||
msgstr "稼働時間"
|
||||
|
||||
@@ -1570,8 +1709,8 @@ msgid "Webhook / Push notifications"
|
||||
msgstr "Webhook / プッシュ通知"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation. Expires after one hour or on hub restart."
|
||||
msgstr "有効にすると、このトークンはエージェントが事前のシステム作成なしに自己登録することを可能にします。1時間後またはハブの再起動時に期限切れになります。"
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation."
|
||||
msgstr "有効にすると、このトークンによりエージェントは事前のシステム作成なしで自己登録できます。"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: ko\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-10-28 23:00\n"
|
||||
"PO-Revision-Date: 2025-12-02 23:18\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Korean\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
@@ -24,6 +24,10 @@ msgstr ""
|
||||
msgid "{0} of {1} row(s) selected."
|
||||
msgstr "{1}개의 행 중 {0}개가 선택되었습니다."
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "{cores, plural, one {# core} other {# cores}}"
|
||||
msgstr "{cores, plural, one {# 코어} other {# 코어}}"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "{count, plural, one {{countString} day} other {{countString} days}}"
|
||||
msgstr "{count, plural, one {{countString} 일} other {{countString} 일}}"
|
||||
@@ -36,6 +40,10 @@ msgstr "{count, plural, one {{countString} 시간} other {{countString} 시간}}
|
||||
msgid "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
|
||||
msgstr "{count, plural, one {{countString} 분} few {{countString} 분} many {{countString} 분} other {{countString} 분}}"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "{threads, plural, one {# thread} other {# threads}}"
|
||||
msgstr "{threads, plural, one {# 스레드} other {# 스레드}}"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 hour"
|
||||
msgstr "1시간"
|
||||
@@ -76,13 +84,16 @@ msgid "5 min"
|
||||
msgstr "5분"
|
||||
|
||||
#. Table column
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Actions"
|
||||
msgstr "작업"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Active"
|
||||
msgstr "활성"
|
||||
|
||||
@@ -95,12 +106,14 @@ msgid "Active state"
|
||||
msgstr "활성 상태"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add <0>System</0>"
|
||||
msgstr "<0>시스템</0> 추가"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Add {foo}"
|
||||
msgstr "{foo} 추가"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add New System"
|
||||
msgstr "새 시스템 추가"
|
||||
msgid "Add <0>System</0>"
|
||||
msgstr "<0>시스템</0> 추가"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add system"
|
||||
@@ -151,6 +164,7 @@ msgstr "모든 컨테이너"
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/home.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "All Systems"
|
||||
@@ -176,6 +190,11 @@ msgstr "평균"
|
||||
msgid "Average CPU utilization of containers"
|
||||
msgstr "Docker 컨테이너의 평균 CPU 사용량"
|
||||
|
||||
#. placeholder {0}: alertData.unit
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "Average drops below <0>{value}{0}</0>"
|
||||
msgstr "평균이 <0>{value}{0}</0> 아래로 떨어집니다"
|
||||
|
||||
#. placeholder {0}: alertData.unit
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "Average exceeds <0>{value}{0}</0>"
|
||||
@@ -208,7 +227,13 @@ msgstr "백업"
|
||||
msgid "Bandwidth"
|
||||
msgstr "대역폭"
|
||||
|
||||
#. Battery label in systems table header
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Bat"
|
||||
msgstr "배터리"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Battery"
|
||||
msgstr "배터리"
|
||||
|
||||
@@ -224,6 +249,13 @@ msgstr "비활성화됨"
|
||||
msgid "Before"
|
||||
msgstr "이전"
|
||||
|
||||
#. placeholder {0}: alert.value
|
||||
#. placeholder {1}: info.unit
|
||||
#. placeholder {2}: alert.min
|
||||
#: src/components/active-alerts.tsx
|
||||
msgid "Below {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
|
||||
msgstr "마지막 {2, plural, one {# 분} other {# 분}} 동안 {0}{1} 미만"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Beszel supports OpenID Connect and many OAuth2 authentication providers."
|
||||
msgstr "Beszel은 OpenID Connect 및 많은 OAuth2 인증 제공자를 지원합니다."
|
||||
@@ -267,6 +299,7 @@ msgid "Can stop"
|
||||
msgstr "중지 가능"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Cancel"
|
||||
msgstr "취소"
|
||||
@@ -320,6 +353,12 @@ msgstr "자세한 내용은 로그를 확인하세요."
|
||||
msgid "Check your notification service"
|
||||
msgstr "알림 서비스를 확인하세요."
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Clear"
|
||||
msgstr "지우기"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
msgid "Click on a container to view more information."
|
||||
msgstr "더 많은 정보를 보려면 컨테이너를 클릭하세요."
|
||||
@@ -442,6 +481,10 @@ msgstr "CPU 시간 분배"
|
||||
msgid "CPU Usage"
|
||||
msgstr "CPU 사용량"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Create"
|
||||
msgstr "생성"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Create account"
|
||||
msgstr "계정 생성"
|
||||
@@ -473,15 +516,18 @@ msgstr "현재 상태"
|
||||
msgid "Cycles"
|
||||
msgstr "사이클"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Dashboard"
|
||||
msgstr "대시보드"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Daily"
|
||||
msgstr "매일"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Default time period"
|
||||
msgstr "기본 기간"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Delete"
|
||||
msgstr "삭제"
|
||||
@@ -548,7 +594,7 @@ msgstr "문서"
|
||||
|
||||
#. Context: System is down
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Down"
|
||||
@@ -566,11 +612,16 @@ msgstr "다운로드"
|
||||
msgid "Duration"
|
||||
msgstr "기간"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Edit"
|
||||
msgstr "수정"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Edit {foo}"
|
||||
msgstr "{foo} 수정"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
#: src/components/login/otp-forms.tsx
|
||||
@@ -586,6 +637,11 @@ msgstr "이메일 알림"
|
||||
msgid "Empty"
|
||||
msgstr "빔"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "End Time"
|
||||
msgstr "종료 시간"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Enter email address to reset password"
|
||||
msgstr "비밀번호를 재설정하려면 이메일 주소를 입력하세요"
|
||||
@@ -598,10 +654,16 @@ msgstr "이메일 주소 입력..."
|
||||
msgid "Enter your one-time password."
|
||||
msgstr "OTP를 입력하세요."
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Ephemeral"
|
||||
msgstr "일시적"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Error"
|
||||
@@ -626,6 +688,10 @@ msgstr "<0>config.yml</0>에 정의되지 않은 기존 시스템은 삭제됩
|
||||
msgid "Exited active"
|
||||
msgstr "활성 종료됨"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Expires after one hour or on hub restart."
|
||||
msgstr "한 시간 후 또는 허브 재시작 시 만료됩니다."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr "내보내기"
|
||||
@@ -656,6 +722,7 @@ msgstr "인증 실패"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Failed to save settings"
|
||||
msgstr "설정 저장 실패"
|
||||
|
||||
@@ -714,6 +781,10 @@ msgstr "가득"
|
||||
msgid "General"
|
||||
msgstr "일반"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Global"
|
||||
msgstr "전역"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "GPU Engines"
|
||||
msgstr "GPU 엔진들"
|
||||
@@ -758,15 +829,14 @@ msgctxt "Docker image"
|
||||
msgid "Image"
|
||||
msgstr "이미지"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Inactive"
|
||||
msgstr "비활성"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Invalid email address."
|
||||
msgstr "잘못된 이메일 주소입니다."
|
||||
|
||||
#. Linux kernel
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Kernel"
|
||||
msgstr "커널"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "언어"
|
||||
@@ -859,6 +929,7 @@ msgid "Max 1 min"
|
||||
msgstr "1분간 최댓값"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
@@ -959,12 +1030,19 @@ msgstr "OAuth 2 / OIDC 지원"
|
||||
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
|
||||
msgstr "매 시작 시, 데이터베이스가 파일에 정의된 시스템과 일치하도록 업데이트됩니다."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "One-time"
|
||||
msgstr "일회성"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "One-time password"
|
||||
msgstr "OTP"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Open menu"
|
||||
msgstr "메뉴 열기"
|
||||
@@ -981,6 +1059,7 @@ msgstr "기타"
|
||||
msgid "Overwrite existing alerts"
|
||||
msgstr "기존 알림 덮어쓰기"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Page"
|
||||
@@ -1013,6 +1092,10 @@ msgstr "비밀번호는 72 바이트 이하여야 합니다."
|
||||
msgid "Password reset request received"
|
||||
msgstr "비밀번호 재설정 요청이 접수되었습니다"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Past"
|
||||
msgstr "과거"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Pause"
|
||||
msgstr "일시 중지"
|
||||
@@ -1034,6 +1117,14 @@ msgstr "코어별 평균 사용률"
|
||||
msgid "Percentage of time spent in each state"
|
||||
msgstr "각 상태에서 보낸 시간의 백분율"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Permanent"
|
||||
msgstr "영구적"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Persistence"
|
||||
msgstr "지속성"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
msgstr "알림이 전달되도록 <0>SMTP 서버를 구성</0>하세요."
|
||||
@@ -1094,6 +1185,10 @@ msgstr "프로세스 시작됨"
|
||||
msgid "Public Key"
|
||||
msgstr "공개 키"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Quiet Hours"
|
||||
msgstr "조용한 시간"
|
||||
|
||||
#. Disk read
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
@@ -1106,6 +1201,7 @@ msgstr "수신됨"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Refresh"
|
||||
msgstr "새로고침"
|
||||
|
||||
@@ -1185,6 +1281,22 @@ msgstr "설정 저장"
|
||||
msgid "Save system"
|
||||
msgstr "시스템 저장"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Saved in the database and does not expire until you disable it."
|
||||
msgstr "데이터베이스에 저장되며 비활성화할 때까지 만료되지 않습니다."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule"
|
||||
msgstr "일정"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule quiet hours where notifications will not be sent, such as during maintenance periods."
|
||||
msgstr "유지보수 기간 등 알림이 전송되지 않을 조용한 시간을 예약하세요."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule quiet hours where notifications will not be sent."
|
||||
msgstr "알림이 전송되지 않을 조용한 시간을 예약하세요."
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Search"
|
||||
msgstr "검색"
|
||||
@@ -1197,6 +1309,10 @@ msgstr "시스템 또는 설정 검색..."
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "알림을 받는 방법을 구성하려면 <0>알림 설정</0>을 참조하세요."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Select {foo}"
|
||||
msgstr "{foo} 선택"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "보냄"
|
||||
@@ -1240,8 +1356,14 @@ msgstr "SMTP 설정"
|
||||
msgid "Sort By"
|
||||
msgstr "정렬 기준"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Start Time"
|
||||
msgstr "시작 시간"
|
||||
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "상태"
|
||||
@@ -1266,9 +1388,15 @@ msgstr "시스템에서 사용된 스왑 공간"
|
||||
msgid "Swap Usage"
|
||||
msgstr "스왑 사용량"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "System"
|
||||
@@ -1280,7 +1408,7 @@ msgstr "시간에 따른 시스템 부하 평균"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Systemd Services"
|
||||
msgstr ""
|
||||
msgstr "Systemd 서비스"
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Systems"
|
||||
@@ -1353,8 +1481,8 @@ msgstr "시간 형식"
|
||||
msgid "To email(s)"
|
||||
msgstr "받는사람(들)"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "Toggle grid"
|
||||
msgstr "그리드 전환"
|
||||
|
||||
@@ -1423,6 +1551,10 @@ msgstr "5분 부하 평균이 임계값을 초과하면 트리거됩니다."
|
||||
msgid "Triggers when any sensor exceeds a threshold"
|
||||
msgstr "센서가 임계값을 초과할 때 트리거됩니다."
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when battery charge drops below a threshold"
|
||||
msgstr "배터리 충전량이 임계값 아래로 떨어질 때 트리거됩니다."
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when combined up/down exceeds a threshold"
|
||||
msgstr "업로드와 다운로드 대역폭의 합이 임계값을 초과할 때 트리거됩니다."
|
||||
@@ -1447,6 +1579,8 @@ msgstr "시스템의 전원이 켜지거나 꺼질때 트리거됩니다."
|
||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||
msgstr "디스크 사용량이 임계값을 초과할 때 트리거됩니다."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Type"
|
||||
msgstr "유형"
|
||||
@@ -1476,7 +1610,7 @@ msgid "Unlimited"
|
||||
msgstr "무제한"
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Up"
|
||||
msgstr "온라인"
|
||||
@@ -1485,7 +1619,12 @@ msgstr "온라인"
|
||||
msgid "Up ({upSystemsLength})"
|
||||
msgstr "온라인 ({upSystemsLength})"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Update"
|
||||
msgstr "업데이트"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Updated"
|
||||
msgstr "업데이트됨"
|
||||
@@ -1498,7 +1637,7 @@ msgstr "10분마다 업데이트됩니다."
|
||||
msgid "Upload"
|
||||
msgstr "업로드"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "Uptime"
|
||||
msgstr "가동 시간"
|
||||
|
||||
@@ -1570,8 +1709,8 @@ msgid "Webhook / Push notifications"
|
||||
msgstr "Webhook / 푸시 알림"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation. Expires after one hour or on hub restart."
|
||||
msgstr "활성화하면 이 토큰을 통해 에이전트가 사전 시스템 생성 없이 자체 등록할 수 있습니다. 1시간 후 또는 허브 재시작 시 만료됩니다."
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation."
|
||||
msgstr "활성화되면 이 토큰은 사전 시스템 생성 없이 에이전트가 자체 등록할 수 있도록 합니다."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: nl\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-10-28 22:59\n"
|
||||
"PO-Revision-Date: 2025-12-17 12:02\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Dutch\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -24,6 +24,10 @@ msgstr ""
|
||||
msgid "{0} of {1} row(s) selected."
|
||||
msgstr "{0} van de {1} rij(en) geselecteerd."
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "{cores, plural, one {# core} other {# cores}}"
|
||||
msgstr "{cores, plural, one {# kern} other {# kernen}}"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "{count, plural, one {{countString} day} other {{countString} days}}"
|
||||
msgstr "{count, plural, one {{countString} dag} other {{countString} dagen}}"
|
||||
@@ -36,6 +40,10 @@ msgstr "{count, plural, one {{countString} uur} other {{countString} uren}}"
|
||||
msgid "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
|
||||
msgstr "{count, plural, one {{countString} minuut} other {{countString} minuten}}"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "{threads, plural, one {# thread} other {# threads}}"
|
||||
msgstr "{threads, plural, one {# thread} other {# threads}}"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 hour"
|
||||
msgstr "1 uur"
|
||||
@@ -51,7 +59,7 @@ msgstr "1 minuut"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 week"
|
||||
msgstr "1 week"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "12 hours"
|
||||
@@ -76,13 +84,16 @@ msgid "5 min"
|
||||
msgstr "5 minuten"
|
||||
|
||||
#. Table column
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Actions"
|
||||
msgstr "Acties"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Active"
|
||||
msgstr "Actief"
|
||||
|
||||
@@ -95,12 +106,14 @@ msgid "Active state"
|
||||
msgstr "Actieve status"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add <0>System</0>"
|
||||
msgstr "Voeg <0>Systeem</0> toe"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Add {foo}"
|
||||
msgstr "Voeg {foo} toe"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add New System"
|
||||
msgstr "Nieuw systeem toevoegen"
|
||||
msgid "Add <0>System</0>"
|
||||
msgstr "Voeg <0>Systeem</0> toe"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add system"
|
||||
@@ -151,6 +164,7 @@ msgstr "Alle containers"
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/home.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "All Systems"
|
||||
@@ -176,6 +190,11 @@ msgstr "Gemiddelde"
|
||||
msgid "Average CPU utilization of containers"
|
||||
msgstr "Gemiddeld CPU-gebruik van containers"
|
||||
|
||||
#. placeholder {0}: alertData.unit
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "Average drops below <0>{value}{0}</0>"
|
||||
msgstr "Gemiddelde daalt onder <0>{value}{0}</0>"
|
||||
|
||||
#. placeholder {0}: alertData.unit
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "Average exceeds <0>{value}{0}</0>"
|
||||
@@ -208,7 +227,13 @@ msgstr "Back-ups"
|
||||
msgid "Bandwidth"
|
||||
msgstr "Bandbreedte"
|
||||
|
||||
#. Battery label in systems table header
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Bat"
|
||||
msgstr "Bat"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Battery"
|
||||
msgstr "Batterij"
|
||||
|
||||
@@ -224,6 +249,13 @@ msgstr "Inactief geworden"
|
||||
msgid "Before"
|
||||
msgstr "Voor"
|
||||
|
||||
#. placeholder {0}: alert.value
|
||||
#. placeholder {1}: info.unit
|
||||
#. placeholder {2}: alert.min
|
||||
#: src/components/active-alerts.tsx
|
||||
msgid "Below {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
|
||||
msgstr "Onder {0}{1} in de laatste {2, plural, one {# minuut} other {# minuten}}"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Beszel supports OpenID Connect and many OAuth2 authentication providers."
|
||||
msgstr "Beszel ondersteunt OpenID Connect en vele OAuth2 authenticatieaanbieders."
|
||||
@@ -267,6 +299,7 @@ msgid "Can stop"
|
||||
msgstr "Kan stoppen"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Cancel"
|
||||
msgstr "Annuleren"
|
||||
@@ -320,6 +353,12 @@ msgstr "Controleer de logs voor meer details."
|
||||
msgid "Check your notification service"
|
||||
msgstr "Controleer je meldingsservice"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Clear"
|
||||
msgstr "Wissen"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
msgid "Click on a container to view more information."
|
||||
msgstr "Klik op een container om meer informatie te zien."
|
||||
@@ -442,6 +481,10 @@ msgstr "CPU-tijdverdeling"
|
||||
msgid "CPU Usage"
|
||||
msgstr "Processorgebruik"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Create"
|
||||
msgstr "Aanmaken"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Create account"
|
||||
msgstr "Account aanmaken"
|
||||
@@ -473,15 +516,18 @@ msgstr "Huidige status"
|
||||
msgid "Cycles"
|
||||
msgstr "Cycli"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Dashboard"
|
||||
msgstr ""
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Daily"
|
||||
msgstr "Dagelijks"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Default time period"
|
||||
msgstr "Standaard tijdsduur"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Delete"
|
||||
msgstr "Verwijderen"
|
||||
@@ -496,7 +542,7 @@ msgstr "Beschrijving"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
msgid "Detail"
|
||||
msgstr "Detail"
|
||||
msgstr "Details"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Device"
|
||||
@@ -548,7 +594,7 @@ msgstr "Documentatie"
|
||||
|
||||
#. Context: System is down
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Down"
|
||||
@@ -566,11 +612,16 @@ msgstr "Downloaden"
|
||||
msgid "Duration"
|
||||
msgstr "Duur"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Edit"
|
||||
msgstr "Bewerken"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Edit {foo}"
|
||||
msgstr "Bewerk {foo}"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
#: src/components/login/otp-forms.tsx
|
||||
@@ -586,6 +637,11 @@ msgstr "E-mailnotificaties"
|
||||
msgid "Empty"
|
||||
msgstr "Leeg"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "End Time"
|
||||
msgstr "Eindtijd"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Enter email address to reset password"
|
||||
msgstr "Voer een e-mailadres in om het wachtwoord opnieuw in te stellen"
|
||||
@@ -598,10 +654,16 @@ msgstr "Voer een e-mailadres in..."
|
||||
msgid "Enter your one-time password."
|
||||
msgstr "Voer uw eenmalig wachtwoord in."
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Ephemeral"
|
||||
msgstr "Tijdelijk"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Error"
|
||||
@@ -626,6 +688,10 @@ msgstr "Bestaande systemen die niet gedefinieerd zijn in <0>config.yml</0> zulle
|
||||
msgid "Exited active"
|
||||
msgstr "Beëindigd actief"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Expires after one hour or on hub restart."
|
||||
msgstr "Verloopt na één uur of bij hub-herstart."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr "Exporteren"
|
||||
@@ -656,6 +722,7 @@ msgstr "Authenticatie mislukt"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Failed to save settings"
|
||||
msgstr "Instellingen opslaan mislukt"
|
||||
|
||||
@@ -687,7 +754,7 @@ msgstr "Vingerafdruk"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Firmware"
|
||||
msgstr ""
|
||||
msgstr "Firmware"
|
||||
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "For <0>{min}</0> {min, plural, one {minute} other {minutes}}"
|
||||
@@ -714,6 +781,10 @@ msgstr "Vol"
|
||||
msgid "General"
|
||||
msgstr "Algemeen"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Global"
|
||||
msgstr "Globaal"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "GPU Engines"
|
||||
msgstr "GPU-engines"
|
||||
@@ -756,17 +827,16 @@ msgstr "Als je het wachtwoord voor je beheerdersaccount bent kwijtgeraakt, kan j
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
msgctxt "Docker image"
|
||||
msgid "Image"
|
||||
msgstr ""
|
||||
msgstr "Afbeelding"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Inactive"
|
||||
msgstr "Inactief"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Invalid email address."
|
||||
msgstr "Ongeldig e-mailadres."
|
||||
|
||||
#. Linux kernel
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Kernel"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "Taal"
|
||||
@@ -781,12 +851,12 @@ msgstr "Layoutbreedte"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Lifecycle"
|
||||
msgstr ""
|
||||
msgstr "Levenscyclus"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "limit"
|
||||
msgstr ""
|
||||
msgstr "limiet"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Load Average"
|
||||
@@ -834,7 +904,7 @@ msgstr "Aanmelding mislukt"
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Logs"
|
||||
msgstr ""
|
||||
msgstr "Logboeken"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Looking instead for where to create alerts? Click the bell <0/> icons in the systems table."
|
||||
@@ -856,9 +926,10 @@ msgstr "Handmatige installatie-instructies"
|
||||
#. Chart select field. Please try to keep this short.
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Max 1 min"
|
||||
msgstr "Max 1 min"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
@@ -959,12 +1030,19 @@ msgstr "OAuth 2 / OIDC ondersteuning"
|
||||
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
|
||||
msgstr "Bij elke herstart zullen systemen in de database worden bijgewerkt om overeen te komen met de systemen die in het bestand zijn gedefinieerd."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "One-time"
|
||||
msgstr "Eenmalig"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "One-time password"
|
||||
msgstr "Eenmalig wachtwoord"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Open menu"
|
||||
msgstr "Menu openen"
|
||||
@@ -981,6 +1059,7 @@ msgstr "Overig"
|
||||
msgid "Overwrite existing alerts"
|
||||
msgstr "Overschrijf bestaande waarschuwingen"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Page"
|
||||
@@ -1013,6 +1092,10 @@ msgstr "Het wachtwoord moet minder zijn dat 72 bytes."
|
||||
msgid "Password reset request received"
|
||||
msgstr "Wachtwoord reset aanvraag ontvangen"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Past"
|
||||
msgstr "Verleden"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Pause"
|
||||
msgstr "Pauze"
|
||||
@@ -1034,6 +1117,14 @@ msgstr "Gemiddeld gebruik per kern"
|
||||
msgid "Percentage of time spent in each state"
|
||||
msgstr "Percentage tijd besteed in elke status"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Permanent"
|
||||
msgstr "Permanent"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Persistence"
|
||||
msgstr "Persistentie"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
msgstr "<0>Configureer een SMTP-server </0> om ervoor te zorgen dat waarschuwingen worden afgeleverd."
|
||||
@@ -1094,6 +1185,10 @@ msgstr "Proces gestart"
|
||||
msgid "Public Key"
|
||||
msgstr "Publieke sleutel"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Quiet Hours"
|
||||
msgstr "Stille uren"
|
||||
|
||||
#. Disk read
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
@@ -1106,6 +1201,7 @@ msgstr "Ontvangen"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Refresh"
|
||||
msgstr "Vernieuwen"
|
||||
|
||||
@@ -1150,7 +1246,7 @@ msgstr "Hervatten"
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgctxt "Root disk label"
|
||||
msgid "Root"
|
||||
msgstr ""
|
||||
msgstr "Root"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Rotate token"
|
||||
@@ -1185,6 +1281,22 @@ msgstr "Instellingen opslaan"
|
||||
msgid "Save system"
|
||||
msgstr "Systeem bewaren"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Saved in the database and does not expire until you disable it."
|
||||
msgstr "Opgeslagen in de database en verloopt niet totdat u het uitschakelt."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule"
|
||||
msgstr "Schema"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule quiet hours where notifications will not be sent, such as during maintenance periods."
|
||||
msgstr "Plan stille uren waarin meldingen niet worden verzonden, zoals tijdens onderhoudsperioden."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule quiet hours where notifications will not be sent."
|
||||
msgstr "Plan stille uren waarin meldingen niet worden verzonden."
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Search"
|
||||
msgstr "Zoeken"
|
||||
@@ -1197,6 +1309,10 @@ msgstr "Zoek naar systemen of instellingen..."
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "Zie <0>notificatie-instellingen</0> om te configureren hoe je meldingen ontvangt."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Select {foo}"
|
||||
msgstr "Selecteer {foo}"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "Verzonden"
|
||||
@@ -1240,8 +1356,14 @@ msgstr "SMTP-instellingen"
|
||||
msgid "Sort By"
|
||||
msgstr "Sorteren op"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Start Time"
|
||||
msgstr "Starttijd"
|
||||
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "Status"
|
||||
@@ -1252,7 +1374,7 @@ msgstr "Status"
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Status"
|
||||
msgstr "Status"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Sub State"
|
||||
@@ -1266,9 +1388,15 @@ msgstr "Swap ruimte gebruikt door het systeem"
|
||||
msgid "Swap Usage"
|
||||
msgstr "Swap gebruik"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "System"
|
||||
@@ -1319,7 +1447,7 @@ msgstr "Temperatuur van systeem sensoren"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Test <0>URL</0>"
|
||||
msgstr "Test <0>URL</0>"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Test notification sent"
|
||||
@@ -1353,8 +1481,8 @@ msgstr "Tijdnotatie"
|
||||
msgid "To email(s)"
|
||||
msgstr "Naar e-mail(s)"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "Toggle grid"
|
||||
msgstr "Schakel raster"
|
||||
|
||||
@@ -1423,6 +1551,10 @@ msgstr "Triggert wanneer de 5 minuten gemiddelde belasting een drempelwaarde ove
|
||||
msgid "Triggers when any sensor exceeds a threshold"
|
||||
msgstr "Triggert wanneer een sensor een drempelwaarde overschrijdt"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when battery charge drops below a threshold"
|
||||
msgstr "Triggert wanneer de batterijlading onder een drempelwaarde daalt"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when combined up/down exceeds a threshold"
|
||||
msgstr "Triggert wanneer de gecombineerde up/down een drempelwaarde overschrijdt"
|
||||
@@ -1447,6 +1579,8 @@ msgstr "Triggert wanneer de status schakelt tussen up en down"
|
||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||
msgstr "Triggert wanneer het gebruik van een schijf een drempelwaarde overschrijdt"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Type"
|
||||
msgstr "Type"
|
||||
@@ -1476,7 +1610,7 @@ msgid "Unlimited"
|
||||
msgstr "Onbeperkt"
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Up"
|
||||
msgstr "Online"
|
||||
@@ -1485,7 +1619,12 @@ msgstr "Online"
|
||||
msgid "Up ({upSystemsLength})"
|
||||
msgstr "Online ({upSystemsLength})"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Update"
|
||||
msgstr "Bijwerken"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Updated"
|
||||
msgstr "Bijgewerkt"
|
||||
@@ -1498,7 +1637,7 @@ msgstr "Elke 10 minuten bijgewerkt."
|
||||
msgid "Upload"
|
||||
msgstr "Uploaden"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "Uptime"
|
||||
msgstr "Actief"
|
||||
|
||||
@@ -1570,8 +1709,8 @@ msgid "Webhook / Push notifications"
|
||||
msgstr "Webhook / Pushmeldingen"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation. Expires after one hour or on hub restart."
|
||||
msgstr "Wanneer ingeschakeld kunnen agenten zich met dit token registreren zonder dat er vooraf een systeem aangemaakt hoeft te worden. Het token verloopt na één uur of bij herstart van de hub."
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation."
|
||||
msgstr "Indien ingeschakeld, stelt deze token agenten in staat zich zelf te registreren zonder voorafgaande systeemcreatie."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: no\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-10-28 23:00\n"
|
||||
"PO-Revision-Date: 2025-12-05 20:24\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Norwegian\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -24,6 +24,10 @@ msgstr ""
|
||||
msgid "{0} of {1} row(s) selected."
|
||||
msgstr "{0} av {1} rad(er) valgt."
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "{cores, plural, one {# core} other {# cores}}"
|
||||
msgstr "{cores, plural, one {# kjerne} other {# kjerner}}"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "{count, plural, one {{countString} day} other {{countString} days}}"
|
||||
msgstr "{count, plural, one {{countString} dag} other {{countString} dager}}"
|
||||
@@ -36,6 +40,10 @@ msgstr "{count, plural, one {{countString} time} other {{countString} timer}}"
|
||||
msgid "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
|
||||
msgstr "{count, plural, one {{countString} minutt} other {{countString} minutter}}"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "{threads, plural, one {# thread} other {# threads}}"
|
||||
msgstr "{threads, plural, one {# tråd} other {# tråder}}"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 hour"
|
||||
msgstr "1 time"
|
||||
@@ -76,13 +84,16 @@ msgid "5 min"
|
||||
msgstr "5 min"
|
||||
|
||||
#. Table column
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Actions"
|
||||
msgstr "Handlinger"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Active"
|
||||
msgstr "Aktiv"
|
||||
|
||||
@@ -95,12 +106,14 @@ msgid "Active state"
|
||||
msgstr "Aktiv tilstand"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add <0>System</0>"
|
||||
msgstr "Legg til <0>System</0>"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Add {foo}"
|
||||
msgstr "Legg til {foo}"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add New System"
|
||||
msgstr "Legg Til Nytt System"
|
||||
msgid "Add <0>System</0>"
|
||||
msgstr "Legg til <0>System</0>"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add system"
|
||||
@@ -151,6 +164,7 @@ msgstr "Alle containere"
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/home.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "All Systems"
|
||||
@@ -176,6 +190,11 @@ msgstr "Gjennomsnitt"
|
||||
msgid "Average CPU utilization of containers"
|
||||
msgstr "Gjennomsnittlig CPU-utnyttelse av konteinere"
|
||||
|
||||
#. placeholder {0}: alertData.unit
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "Average drops below <0>{value}{0}</0>"
|
||||
msgstr "Gjennomsnittet faller under <0>{value}{0}</0>"
|
||||
|
||||
#. placeholder {0}: alertData.unit
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "Average exceeds <0>{value}{0}</0>"
|
||||
@@ -208,7 +227,13 @@ msgstr "Sikkerhetskopier"
|
||||
msgid "Bandwidth"
|
||||
msgstr "Båndbredde"
|
||||
|
||||
#. Battery label in systems table header
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Bat"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Battery"
|
||||
msgstr "Batteri"
|
||||
|
||||
@@ -224,6 +249,13 @@ msgstr "Ble inaktiv"
|
||||
msgid "Before"
|
||||
msgstr "Før"
|
||||
|
||||
#. placeholder {0}: alert.value
|
||||
#. placeholder {1}: info.unit
|
||||
#. placeholder {2}: alert.min
|
||||
#: src/components/active-alerts.tsx
|
||||
msgid "Below {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
|
||||
msgstr "Under {0}{1} i siste {2, plural, one {# minutt} other {# minutter}}"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Beszel supports OpenID Connect and many OAuth2 authentication providers."
|
||||
msgstr "Beszel støtter OpenID Connect og mange OAuth2 autentiserings-tilbydere."
|
||||
@@ -267,6 +299,7 @@ msgid "Can stop"
|
||||
msgstr "Kan stoppe"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Cancel"
|
||||
msgstr "Avbryt"
|
||||
@@ -320,6 +353,12 @@ msgstr "Sjekk loggene for flere detaljer."
|
||||
msgid "Check your notification service"
|
||||
msgstr "Sjekk din meldingstjeneste"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Clear"
|
||||
msgstr "Tøm"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
msgid "Click on a container to view more information."
|
||||
msgstr "Klikk på en container for å se mer informasjon."
|
||||
@@ -442,6 +481,10 @@ msgstr "CPU-tidsoppdeling"
|
||||
msgid "CPU Usage"
|
||||
msgstr "CPU-bruk"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Create"
|
||||
msgstr "Opprett"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Create account"
|
||||
msgstr "Opprett konto"
|
||||
@@ -473,15 +516,18 @@ msgstr "Nåværende tilstand"
|
||||
msgid "Cycles"
|
||||
msgstr "Sykluser"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Dashboard"
|
||||
msgstr "Dashbord"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Daily"
|
||||
msgstr "Daglig"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Default time period"
|
||||
msgstr "Standard tidsperiode"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Delete"
|
||||
msgstr "Slett"
|
||||
@@ -548,7 +594,7 @@ msgstr "Dokumentasjon"
|
||||
|
||||
#. Context: System is down
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Down"
|
||||
@@ -566,11 +612,16 @@ msgstr "Last ned"
|
||||
msgid "Duration"
|
||||
msgstr "Varighet"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Edit"
|
||||
msgstr "Rediger"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Edit {foo}"
|
||||
msgstr "Rediger {foo}"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
#: src/components/login/otp-forms.tsx
|
||||
@@ -586,6 +637,11 @@ msgstr "E-postvarslinger"
|
||||
msgid "Empty"
|
||||
msgstr "Tom"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "End Time"
|
||||
msgstr "Sluttid"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Enter email address to reset password"
|
||||
msgstr "Skriv inn e-postadresse for å nullstille passordet"
|
||||
@@ -598,10 +654,16 @@ msgstr "Skriv inn e-postadresse..."
|
||||
msgid "Enter your one-time password."
|
||||
msgstr "Skriv inn ditt engangspassord."
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Ephemeral"
|
||||
msgstr "Flyktig"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Error"
|
||||
@@ -626,6 +688,10 @@ msgstr "Eksisterende systemer som ikke er er definert i <0>config.yml</0> vil bl
|
||||
msgid "Exited active"
|
||||
msgstr "Avsluttet aktiv"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Expires after one hour or on hub restart."
|
||||
msgstr "Utløper etter en time eller ved hub-omstart."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr "Eksporter"
|
||||
@@ -656,6 +722,7 @@ msgstr "Autentisering mislyktes"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Failed to save settings"
|
||||
msgstr "Kunne ikke lagre innstillingene"
|
||||
|
||||
@@ -714,6 +781,10 @@ msgstr "Fullt"
|
||||
msgid "General"
|
||||
msgstr "Generelt"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Global"
|
||||
msgstr "Global"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "GPU Engines"
|
||||
msgstr "GPU-motorer"
|
||||
@@ -758,15 +829,14 @@ msgctxt "Docker image"
|
||||
msgid "Image"
|
||||
msgstr "Image"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Inactive"
|
||||
msgstr "Inaktiv"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Invalid email address."
|
||||
msgstr "Ugyldig e-postadresse."
|
||||
|
||||
#. Linux kernel
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Kernel"
|
||||
msgstr "Kjerne"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "Språk"
|
||||
@@ -859,6 +929,7 @@ msgid "Max 1 min"
|
||||
msgstr "Maks 1 min"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
@@ -959,12 +1030,19 @@ msgstr "OAuth 2 / OIDC-støtte"
|
||||
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
|
||||
msgstr "Ved hver omstart vil systemer i databasen bli oppdatert til å matche systemene definert i fila."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "One-time"
|
||||
msgstr "Engangs"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "One-time password"
|
||||
msgstr "Engangspassord"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Open menu"
|
||||
msgstr "Åpne meny"
|
||||
@@ -981,6 +1059,7 @@ msgstr "Andre"
|
||||
msgid "Overwrite existing alerts"
|
||||
msgstr "Overskriv eksisterende alarmer"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Page"
|
||||
@@ -1013,6 +1092,10 @@ msgstr "Passord må være mindre enn 72 byte."
|
||||
msgid "Password reset request received"
|
||||
msgstr "Mottatt forespørsel om å nullstille passord"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Past"
|
||||
msgstr "Fortid"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Pause"
|
||||
msgstr "Pause"
|
||||
@@ -1034,6 +1117,14 @@ msgstr "Gjennomsnittlig utnyttelse per kjerne"
|
||||
msgid "Percentage of time spent in each state"
|
||||
msgstr "Prosentandel av tid brukt i hver tilstand"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Permanent"
|
||||
msgstr "Permanent"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Persistence"
|
||||
msgstr "Vedvarenhet"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
msgstr "Vennligst <0>konfigurer en SMTP-server</0> for å forsikre deg om at varsler blir levert."
|
||||
@@ -1094,6 +1185,10 @@ msgstr "Prosess startet"
|
||||
msgid "Public Key"
|
||||
msgstr "Offentlig Nøkkel"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Quiet Hours"
|
||||
msgstr "Stille timer"
|
||||
|
||||
#. Disk read
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
@@ -1106,6 +1201,7 @@ msgstr "Mottatt"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Refresh"
|
||||
msgstr "Oppdater"
|
||||
|
||||
@@ -1185,6 +1281,22 @@ msgstr "Lagre Innstillinger"
|
||||
msgid "Save system"
|
||||
msgstr "Lagre system"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Saved in the database and does not expire until you disable it."
|
||||
msgstr "Lagret i databasen og utløper ikke før du deaktiverer det."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule"
|
||||
msgstr "Tidsplan"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule quiet hours where notifications will not be sent, such as during maintenance periods."
|
||||
msgstr "Planlegg stille timer hvor varsler ikke sendes, for eksempel under vedlikeholdsperioder."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule quiet hours where notifications will not be sent."
|
||||
msgstr "Planlegg stille timer hvor varsler ikke sendes."
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Search"
|
||||
msgstr "Søk"
|
||||
@@ -1197,6 +1309,10 @@ msgstr "Søk etter systemer eller innstillinger..."
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "Se <0>varslingsinnstillingene</0> for å konfigurere hvordan du vil motta varsler."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Select {foo}"
|
||||
msgstr "Velg {foo}"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "Sendt"
|
||||
@@ -1240,8 +1356,14 @@ msgstr "SMTP-innstillinger"
|
||||
msgid "Sort By"
|
||||
msgstr "Sorter Etter"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Start Time"
|
||||
msgstr "Starttid"
|
||||
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "Tilstand"
|
||||
@@ -1266,9 +1388,15 @@ msgstr "Swap-plass i bruk av systemet"
|
||||
msgid "Swap Usage"
|
||||
msgstr "Swap-bruk"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "System"
|
||||
@@ -1353,8 +1481,8 @@ msgstr "Tidsformat"
|
||||
msgid "To email(s)"
|
||||
msgstr "Til e-postadresse(r)"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "Toggle grid"
|
||||
msgstr "Rutenett av/på"
|
||||
|
||||
@@ -1384,7 +1512,7 @@ msgstr "Tokens og fingeravtrykk blir brukt for å autentisere WebSocket-tilkobli
|
||||
#: src/components/ui/chart.tsx
|
||||
#: src/components/ui/chart.tsx
|
||||
msgid "Total"
|
||||
msgstr "Total"
|
||||
msgstr "Totalt"
|
||||
|
||||
#: src/components/routes/system/network-sheet.tsx
|
||||
msgid "Total data received for each interface"
|
||||
@@ -1423,6 +1551,10 @@ msgstr "Slår inn når gjennomsnittsbelastningen over 5 minutter overstiger en g
|
||||
msgid "Triggers when any sensor exceeds a threshold"
|
||||
msgstr "Slår inn når enhver sensor overstiger en grenseverdi"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when battery charge drops below a threshold"
|
||||
msgstr "Utløses når batterilading faller under en terskel"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when combined up/down exceeds a threshold"
|
||||
msgstr "Slår inn når kombinert opp/ned overskrider en grenseverdi"
|
||||
@@ -1447,6 +1579,8 @@ msgstr "Slår inn når statusen veksler mellom oppe og nede"
|
||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||
msgstr "Slår inn når forbruk av hvilken som helst disk overstiger en grenseverdi"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Type"
|
||||
msgstr "Type"
|
||||
@@ -1476,7 +1610,7 @@ msgid "Unlimited"
|
||||
msgstr "Ubegrenset"
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Up"
|
||||
msgstr "Oppe"
|
||||
@@ -1485,7 +1619,12 @@ msgstr "Oppe"
|
||||
msgid "Up ({upSystemsLength})"
|
||||
msgstr "Oppe ({upSystemsLength})"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Update"
|
||||
msgstr "Oppdater"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Updated"
|
||||
msgstr "Oppdatert"
|
||||
@@ -1498,7 +1637,7 @@ msgstr "Oppdatert hvert 10. minutt."
|
||||
msgid "Upload"
|
||||
msgstr "Last opp"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "Uptime"
|
||||
msgstr "Oppetid"
|
||||
|
||||
@@ -1570,8 +1709,8 @@ msgid "Webhook / Push notifications"
|
||||
msgstr "Webhook / Push-varslinger"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation. Expires after one hour or on hub restart."
|
||||
msgstr "Når aktivert lar denne tokenen agenter registrere seg selv uten å opprettes på systemet først. Utløper etter én time eller når huben starter på nytt."
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation."
|
||||
msgstr "Når aktivert, tillater denne tokenen agenter å registrere seg selv uten forutgående systemskapelse."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: pl\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-10-28 23:00\n"
|
||||
"PO-Revision-Date: 2025-12-18 19:21\n"
|
||||
"Last-Translator: \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"
|
||||
@@ -24,6 +24,10 @@ msgstr ""
|
||||
msgid "{0} of {1} row(s) selected."
|
||||
msgstr "{0} z {1} wybranych wierszy."
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "{cores, plural, one {# core} other {# cores}}"
|
||||
msgstr "{cores, plural, one {# rdzeń} few {# rdzenie} many {# rdzeni} other {# rdzeni}}"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "{count, plural, one {{countString} day} other {{countString} days}}"
|
||||
msgstr "{count, plural, one {{countString} dzień} few {{countString} dni} many {{countString} dni} other {{countString} dni}}"
|
||||
@@ -36,6 +40,10 @@ msgstr "{count, plural, one {godzinę} few {{countString} godziny} many {{countS
|
||||
msgid "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
|
||||
msgstr "{count, plural, one {{countString} minuta} few {{countString} minuty} many {{countString} minut} other {{countString} minut}}"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "{threads, plural, one {# thread} other {# threads}}"
|
||||
msgstr "{threads, plural, one {# wątek} few {# wątki} many {# wątków} other {# wątków}}"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 hour"
|
||||
msgstr "1 godzina"
|
||||
@@ -76,13 +84,16 @@ msgid "5 min"
|
||||
msgstr "5 min"
|
||||
|
||||
#. Table column
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Actions"
|
||||
msgstr "Akcje"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Active"
|
||||
msgstr "Aktywny"
|
||||
|
||||
@@ -95,12 +106,14 @@ msgid "Active state"
|
||||
msgstr "Stan aktywny"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add <0>System</0>"
|
||||
msgstr "Dodaj <0>system</0>"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Add {foo}"
|
||||
msgstr "Dodaj {foo}"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add New System"
|
||||
msgstr "Dodaj nowy system"
|
||||
msgid "Add <0>System</0>"
|
||||
msgstr "Dodaj <0>system</0>"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add system"
|
||||
@@ -151,6 +164,7 @@ msgstr "Wszystkie kontenery"
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/home.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "All Systems"
|
||||
@@ -176,6 +190,11 @@ msgstr "Średnia"
|
||||
msgid "Average CPU utilization of containers"
|
||||
msgstr "Średnie wykorzystanie procesora przez kontenery"
|
||||
|
||||
#. placeholder {0}: alertData.unit
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "Average drops below <0>{value}{0}</0>"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: alertData.unit
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "Average exceeds <0>{value}{0}</0>"
|
||||
@@ -208,7 +227,13 @@ msgstr "Kopie"
|
||||
msgid "Bandwidth"
|
||||
msgstr "Przepustowość"
|
||||
|
||||
#. Battery label in systems table header
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Bat"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Battery"
|
||||
msgstr "Bateria"
|
||||
|
||||
@@ -224,6 +249,13 @@ msgstr "Stało się nieaktywnym"
|
||||
msgid "Before"
|
||||
msgstr "Przed"
|
||||
|
||||
#. placeholder {0}: alert.value
|
||||
#. placeholder {1}: info.unit
|
||||
#. placeholder {2}: alert.min
|
||||
#: src/components/active-alerts.tsx
|
||||
msgid "Below {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Beszel supports OpenID Connect and many OAuth2 authentication providers."
|
||||
msgstr "Beszel obsługuje OpenID Connect i wielu dostawców uwierzytelniania OAuth2."
|
||||
@@ -267,6 +299,7 @@ msgid "Can stop"
|
||||
msgstr "Może zatrzymać"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Cancel"
|
||||
msgstr "Anuluj"
|
||||
@@ -320,6 +353,12 @@ msgstr "Sprawdź logi, aby uzyskać więcej informacji."
|
||||
msgid "Check your notification service"
|
||||
msgstr "Sprawdź swój serwis powiadomień"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Clear"
|
||||
msgstr "Wyczyść"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
msgid "Click on a container to view more information."
|
||||
msgstr "Kliknij na kontener, aby wyświetlić więcej informacji."
|
||||
@@ -442,6 +481,10 @@ msgstr "Podział czasu CPU"
|
||||
msgid "CPU Usage"
|
||||
msgstr "Użycie procesora"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Create"
|
||||
msgstr "Utwórz"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Create account"
|
||||
msgstr "Utwórz konto"
|
||||
@@ -473,15 +516,18 @@ msgstr "Aktualny stan"
|
||||
msgid "Cycles"
|
||||
msgstr "Cykle"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Dashboard"
|
||||
msgstr "Panel kontrolny"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Daily"
|
||||
msgstr "Codziennie"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Default time period"
|
||||
msgstr "Domyślny przedział czasu"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Delete"
|
||||
msgstr "Usuń"
|
||||
@@ -548,7 +594,7 @@ msgstr "Dokumentacja"
|
||||
|
||||
#. Context: System is down
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Down"
|
||||
@@ -566,11 +612,16 @@ msgstr "Pobieranie"
|
||||
msgid "Duration"
|
||||
msgstr "Czas trwania"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Edit"
|
||||
msgstr "Edytuj"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Edit {foo}"
|
||||
msgstr "Edytuj {foo}"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
#: src/components/login/otp-forms.tsx
|
||||
@@ -586,6 +637,11 @@ msgstr "Powiadomienia e-mail"
|
||||
msgid "Empty"
|
||||
msgstr "Pusta"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "End Time"
|
||||
msgstr "Czas zakończenia"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Enter email address to reset password"
|
||||
msgstr "Wprowadź adres e-mail, aby zresetować hasło"
|
||||
@@ -598,10 +654,16 @@ msgstr "Wprowadź adres e-mail..."
|
||||
msgid "Enter your one-time password."
|
||||
msgstr "Wprowadź swoje jednorazowe hasło."
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Ephemeral"
|
||||
msgstr "Efemeryczny"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Error"
|
||||
@@ -626,6 +688,10 @@ msgstr "Istniejące systemy, które nie są zdefiniowane w <0>config.yml</0>, zo
|
||||
msgid "Exited active"
|
||||
msgstr "Zakończono aktywnie"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Expires after one hour or on hub restart."
|
||||
msgstr "Wygasa po godzinie lub przy ponownym uruchomieniu huba."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr "Eksport"
|
||||
@@ -656,6 +722,7 @@ msgstr "Błąd autoryzacji"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Failed to save settings"
|
||||
msgstr "Nie udało się zapisać ustawień"
|
||||
|
||||
@@ -714,6 +781,10 @@ msgstr "Pełna"
|
||||
msgid "General"
|
||||
msgstr "Ogólne"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Global"
|
||||
msgstr "Globalny"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "GPU Engines"
|
||||
msgstr "Silniki GPU"
|
||||
@@ -758,15 +829,14 @@ msgctxt "Docker image"
|
||||
msgid "Image"
|
||||
msgstr "Obraz"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Inactive"
|
||||
msgstr "Nieaktywny"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Invalid email address."
|
||||
msgstr "Nieprawidłowy adres e-mail."
|
||||
|
||||
#. Linux kernel
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Kernel"
|
||||
msgstr "Jądro"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "Język"
|
||||
@@ -786,7 +856,7 @@ msgstr "Cykl życia"
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "limit"
|
||||
msgstr ""
|
||||
msgstr "limit"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Load Average"
|
||||
@@ -859,6 +929,7 @@ msgid "Max 1 min"
|
||||
msgstr "Maks. 1 min"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
@@ -959,12 +1030,19 @@ msgstr "Wsparcie OAuth 2 / OIDC"
|
||||
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
|
||||
msgstr "Przy każdym ponownym uruchomieniu systemy w bazie danych będą aktualizowane, aby odpowiadały systemom zdefiniowanym w pliku."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "One-time"
|
||||
msgstr "Jednorazowy"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "One-time password"
|
||||
msgstr "Hasło jednorazowe"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Open menu"
|
||||
msgstr "Otwórz menu"
|
||||
@@ -981,6 +1059,7 @@ msgstr "Inne"
|
||||
msgid "Overwrite existing alerts"
|
||||
msgstr "Nadpisz istniejące alerty"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Page"
|
||||
@@ -1013,6 +1092,10 @@ msgstr "Hasło musi być mniejsze niż 72 bajty."
|
||||
msgid "Password reset request received"
|
||||
msgstr "Otrzymane żądanie resetowania hasła"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Past"
|
||||
msgstr "Przeszłe"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Pause"
|
||||
msgstr "Pauza"
|
||||
@@ -1034,6 +1117,14 @@ msgstr "Średnie wykorzystanie na rdzeń"
|
||||
msgid "Percentage of time spent in each state"
|
||||
msgstr "Procent czasu spędzonego w każdym stanie"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Permanent"
|
||||
msgstr "Stały"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Persistence"
|
||||
msgstr "Trwałość"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
msgstr "Proszę <0>skonfigurować serwer SMTP</0>, aby zapewnić dostarczanie powiadomień."
|
||||
@@ -1094,6 +1185,10 @@ msgstr "Proces uruchomiony"
|
||||
msgid "Public Key"
|
||||
msgstr "Klucz publiczny"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Quiet Hours"
|
||||
msgstr "Godziny ciszy"
|
||||
|
||||
#. Disk read
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
@@ -1106,6 +1201,7 @@ msgstr "Otrzymane"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Refresh"
|
||||
msgstr "Odśwież"
|
||||
|
||||
@@ -1150,7 +1246,7 @@ msgstr "Wznów"
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgctxt "Root disk label"
|
||||
msgid "Root"
|
||||
msgstr ""
|
||||
msgstr "Root"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Rotate token"
|
||||
@@ -1185,6 +1281,22 @@ msgstr "Zapisz ustawienia"
|
||||
msgid "Save system"
|
||||
msgstr "Zapisz system"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Saved in the database and does not expire until you disable it."
|
||||
msgstr "Zapisane w bazie danych i nie wygasa, dopóki go nie wyłączysz."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule"
|
||||
msgstr "Harmonogram"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule quiet hours where notifications will not be sent, such as during maintenance periods."
|
||||
msgstr "Zaplanuj godziny ciszy, w których powiadomienia nie będą wysyłane, na przykład podczas okresów konserwacji."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule quiet hours where notifications will not be sent."
|
||||
msgstr "Zaplanuj godziny ciszy, w których powiadomienia nie będą wysyłane."
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Search"
|
||||
msgstr "Szukaj"
|
||||
@@ -1197,6 +1309,10 @@ msgstr "Szukaj systemów lub ustawień..."
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "Zobacz <0>ustawienia powiadomień</0>, aby skonfigurować sposób, w jaki otrzymujesz powiadomienia."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Select {foo}"
|
||||
msgstr "Wybierz {foo}"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "Wysłane"
|
||||
@@ -1240,8 +1356,14 @@ msgstr "Ustawienia SMTP"
|
||||
msgid "Sort By"
|
||||
msgstr "Sortuj według"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Start Time"
|
||||
msgstr "Czas rozpoczęcia"
|
||||
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "Stan"
|
||||
@@ -1266,9 +1388,15 @@ msgstr "Pamięć wymiany używana przez system"
|
||||
msgid "Swap Usage"
|
||||
msgstr "Użycie pamięci wymiany"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "System"
|
||||
@@ -1353,8 +1481,8 @@ msgstr "Format czasu"
|
||||
msgid "To email(s)"
|
||||
msgstr "Do e-mail(ów)"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "Toggle grid"
|
||||
msgstr "Przełącz siatkę"
|
||||
|
||||
@@ -1423,6 +1551,10 @@ msgstr "Uruchamia się, gdy 5-minutowe średnie obciążenie systemu przekroczy
|
||||
msgid "Triggers when any sensor exceeds a threshold"
|
||||
msgstr "Wyzwalane, gdy jakikolwiek czujnik przekroczy ustalony próg."
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when battery charge drops below a threshold"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when combined up/down exceeds a threshold"
|
||||
msgstr "Wyzwalane, gdy łączna wartość w górę/w dół przekroczy próg"
|
||||
@@ -1447,6 +1579,8 @@ msgstr "Wyzwalane, gdy status przełącza się między stanem aktywnym a nieakty
|
||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||
msgstr "Wyzwalane, gdy wykorzystanie któregokolwiek dysku przekroczy ustalony próg"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Type"
|
||||
msgstr "Typ"
|
||||
@@ -1476,7 +1610,7 @@ msgid "Unlimited"
|
||||
msgstr "Bez limitu"
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Up"
|
||||
msgstr "Działa"
|
||||
@@ -1485,7 +1619,12 @@ msgstr "Działa"
|
||||
msgid "Up ({upSystemsLength})"
|
||||
msgstr "Działa ({upSystemsLength})"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Update"
|
||||
msgstr "Aktualizuj"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Updated"
|
||||
msgstr "Zaktualizowano"
|
||||
@@ -1498,7 +1637,7 @@ msgstr "Aktualizowane co 10 minut."
|
||||
msgid "Upload"
|
||||
msgstr "Wysyłanie"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "Uptime"
|
||||
msgstr "Czas pracy"
|
||||
|
||||
@@ -1570,8 +1709,8 @@ msgid "Webhook / Push notifications"
|
||||
msgstr "Webhook / Powiadomienia push"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation. Expires after one hour or on hub restart."
|
||||
msgstr "Po włączeniu ten token umożliwia agentom samodzielną rejestrację bez wcześniejszego utworzenia systemu. Wygasa po jednej godzinie lub po ponownym uruchomieniu huba."
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation."
|
||||
msgstr "Gdy jest włączony, ten token pozwala agentom na samodzielną rejestrację bez wcześniejszego tworzenia systemu."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: pt\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-11-04 22:13\n"
|
||||
"PO-Revision-Date: 2025-12-05 20:24\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Portuguese\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -24,6 +24,10 @@ msgstr ""
|
||||
msgid "{0} of {1} row(s) selected."
|
||||
msgstr "{0} de {1} linha(s) selecionada(s)."
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "{cores, plural, one {# core} other {# cores}}"
|
||||
msgstr "{cores, plural, one {# núcleo} other {# núcleos}}"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "{count, plural, one {{countString} day} other {{countString} days}}"
|
||||
msgstr "{count, plural, one {{countString} dia} other {{countString} dias}}"
|
||||
@@ -36,6 +40,10 @@ msgstr "{count, plural, one {{countString} hora} other {{countString} horas}}"
|
||||
msgid "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
|
||||
msgstr "{count, plural, one {{countString} minuto} other {{countString} minutos}}"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "{threads, plural, one {# thread} other {# threads}}"
|
||||
msgstr "{threads, plural, one {# thread} other {# threads}}"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 hour"
|
||||
msgstr "1 hora"
|
||||
@@ -76,13 +84,16 @@ msgid "5 min"
|
||||
msgstr "5 min"
|
||||
|
||||
#. Table column
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Actions"
|
||||
msgstr "Ações"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Active"
|
||||
msgstr "Ativo"
|
||||
|
||||
@@ -95,12 +106,14 @@ msgid "Active state"
|
||||
msgstr "Estado ativo"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add <0>System</0>"
|
||||
msgstr "Adicionar <0>Sistema</0>"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Add {foo}"
|
||||
msgstr "Adicionar {foo}"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add New System"
|
||||
msgstr "Adicionar Novo Sistema"
|
||||
msgid "Add <0>System</0>"
|
||||
msgstr "Adicionar <0>Sistema</0>"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add system"
|
||||
@@ -151,6 +164,7 @@ msgstr "Todos os Contêineres"
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/home.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "All Systems"
|
||||
@@ -176,6 +190,11 @@ msgstr "Média"
|
||||
msgid "Average CPU utilization of containers"
|
||||
msgstr "Utilização média de CPU dos contêineres"
|
||||
|
||||
#. placeholder {0}: alertData.unit
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "Average drops below <0>{value}{0}</0>"
|
||||
msgstr "A média cai abaixo de <0>{value}{0}</0>"
|
||||
|
||||
#. placeholder {0}: alertData.unit
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "Average exceeds <0>{value}{0}</0>"
|
||||
@@ -208,7 +227,13 @@ msgstr "Cópias de segurança"
|
||||
msgid "Bandwidth"
|
||||
msgstr "Largura de Banda"
|
||||
|
||||
#. Battery label in systems table header
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Bat"
|
||||
msgstr "Bat"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Battery"
|
||||
msgstr "Bateria"
|
||||
|
||||
@@ -224,6 +249,13 @@ msgstr "Tornou-se inativo"
|
||||
msgid "Before"
|
||||
msgstr "Antes"
|
||||
|
||||
#. placeholder {0}: alert.value
|
||||
#. placeholder {1}: info.unit
|
||||
#. placeholder {2}: alert.min
|
||||
#: src/components/active-alerts.tsx
|
||||
msgid "Below {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
|
||||
msgstr "Abaixo de {0}{1} no último {2, plural, one {# minuto} other {# minutos}}"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Beszel supports OpenID Connect and many OAuth2 authentication providers."
|
||||
msgstr "Beszel suporta OpenID Connect e muitos provedores de autenticação OAuth2."
|
||||
@@ -267,6 +299,7 @@ msgid "Can stop"
|
||||
msgstr "Pode parar"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Cancel"
|
||||
msgstr "Cancelar"
|
||||
@@ -320,6 +353,12 @@ msgstr "Verifique os logs para mais detalhes."
|
||||
msgid "Check your notification service"
|
||||
msgstr "Verifique seu serviço de notificação"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Clear"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
msgid "Click on a container to view more information."
|
||||
msgstr "Clique num contentor para ver mais informações."
|
||||
@@ -442,6 +481,10 @@ msgstr "Distribuição do Tempo de CPU"
|
||||
msgid "CPU Usage"
|
||||
msgstr "Uso de CPU"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Create"
|
||||
msgstr "Criar"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Create account"
|
||||
msgstr "Criar conta"
|
||||
@@ -473,15 +516,18 @@ msgstr "Estado atual"
|
||||
msgid "Cycles"
|
||||
msgstr "Ciclos"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Dashboard"
|
||||
msgstr "Painel"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Daily"
|
||||
msgstr "Diariamente"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Default time period"
|
||||
msgstr "Período de tempo padrão"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Delete"
|
||||
msgstr "Excluir"
|
||||
@@ -548,7 +594,7 @@ msgstr "Documentação"
|
||||
|
||||
#. Context: System is down
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Down"
|
||||
@@ -566,11 +612,16 @@ msgstr "Transferir"
|
||||
msgid "Duration"
|
||||
msgstr "Duração"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Edit"
|
||||
msgstr "Editar"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Edit {foo}"
|
||||
msgstr "Editar {foo}"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
#: src/components/login/otp-forms.tsx
|
||||
@@ -586,6 +637,11 @@ msgstr "Notificações por email"
|
||||
msgid "Empty"
|
||||
msgstr "Vazia"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "End Time"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Enter email address to reset password"
|
||||
msgstr "Digite o endereço de email para redefinir a senha"
|
||||
@@ -598,10 +654,16 @@ msgstr "Digite o endereço de email..."
|
||||
msgid "Enter your one-time password."
|
||||
msgstr "Insira a sua senha de uso único."
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Ephemeral"
|
||||
msgstr "Efêmero"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Error"
|
||||
@@ -626,6 +688,10 @@ msgstr "Sistemas existentes não definidos em <0>config.yml</0> serão excluído
|
||||
msgid "Exited active"
|
||||
msgstr "Saiu ativo"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Expires after one hour or on hub restart."
|
||||
msgstr "Expira após uma hora ou no reinício do hub."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr "Exportar"
|
||||
@@ -656,6 +722,7 @@ msgstr "Falha na autenticação"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Failed to save settings"
|
||||
msgstr "Falha ao guardar as definições"
|
||||
|
||||
@@ -714,6 +781,10 @@ msgstr "Cheia"
|
||||
msgid "General"
|
||||
msgstr "Geral"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Global"
|
||||
msgstr "Global"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "GPU Engines"
|
||||
msgstr "Motores GPU"
|
||||
@@ -758,15 +829,14 @@ msgctxt "Docker image"
|
||||
msgid "Image"
|
||||
msgstr "Imagem"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Inactive"
|
||||
msgstr "Inativo"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Invalid email address."
|
||||
msgstr "Endereço de email inválido."
|
||||
|
||||
#. Linux kernel
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Kernel"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "Idioma"
|
||||
@@ -834,7 +904,7 @@ msgstr "Tentativa de login falhou"
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Logs"
|
||||
msgstr ""
|
||||
msgstr "Logs"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Looking instead for where to create alerts? Click the bell <0/> icons in the systems table."
|
||||
@@ -859,6 +929,7 @@ msgid "Max 1 min"
|
||||
msgstr "Máx 1 min"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
@@ -959,12 +1030,19 @@ msgstr "Suporte a OAuth 2 / OIDC"
|
||||
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
|
||||
msgstr "A cada reinício, os sistemas no banco de dados serão atualizados para corresponder aos sistemas definidos no arquivo."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "One-time"
|
||||
msgstr "Uma vez"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "One-time password"
|
||||
msgstr "Senha de uso único"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Open menu"
|
||||
msgstr "Abrir menu"
|
||||
@@ -981,6 +1059,7 @@ msgstr "Outro"
|
||||
msgid "Overwrite existing alerts"
|
||||
msgstr "Sobrescrever alertas existentes"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Page"
|
||||
@@ -1013,6 +1092,10 @@ msgstr "A password tem que ter menos de 72 bytes."
|
||||
msgid "Password reset request received"
|
||||
msgstr "Solicitação de redefinição de senha recebida"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Past"
|
||||
msgstr "Passado"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Pause"
|
||||
msgstr "Pausar"
|
||||
@@ -1034,6 +1117,14 @@ msgstr "Utilização média por núcleo"
|
||||
msgid "Percentage of time spent in each state"
|
||||
msgstr "Percentagem de tempo gasto em cada estado"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Permanent"
|
||||
msgstr "Permanente"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Persistence"
|
||||
msgstr "Persistência"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
msgstr "Por favor, <0>configure um servidor SMTP</0> para garantir que os alertas sejam entregues."
|
||||
@@ -1094,6 +1185,10 @@ msgstr "Processo iniciado"
|
||||
msgid "Public Key"
|
||||
msgstr "Chave Pública"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Quiet Hours"
|
||||
msgstr "Horas Silenciosas"
|
||||
|
||||
#. Disk read
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
@@ -1106,6 +1201,7 @@ msgstr "Recebido"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Refresh"
|
||||
msgstr "Atualizar"
|
||||
|
||||
@@ -1185,6 +1281,22 @@ msgstr "Guardar Definições"
|
||||
msgid "Save system"
|
||||
msgstr "Guardar Sistema"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Saved in the database and does not expire until you disable it."
|
||||
msgstr "Salvo no banco de dados e não expira até você desativá-lo."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule"
|
||||
msgstr "Agendar"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule quiet hours where notifications will not be sent, such as during maintenance periods."
|
||||
msgstr "Agende horas silenciosas onde as notificações não serão enviadas, como durante períodos de manutenção."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Schedule quiet hours where notifications will not be sent."
|
||||
msgstr "Agende horas silenciosas onde as notificações não serão enviadas."
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Search"
|
||||
msgstr "Pesquisar"
|
||||
@@ -1197,6 +1309,10 @@ msgstr "Pesquisar por sistemas ou configurações..."
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "Veja <0>configurações de notificação</0> para configurar como você recebe alertas."
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Select {foo}"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "Enviado"
|
||||
@@ -1240,8 +1356,14 @@ msgstr "Configurações SMTP"
|
||||
msgid "Sort By"
|
||||
msgstr "Ordenar Por"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Start Time"
|
||||
msgstr ""
|
||||
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "Estado"
|
||||
@@ -1266,9 +1388,15 @@ msgstr "Espaço de swap usado pelo sistema"
|
||||
msgid "Swap Usage"
|
||||
msgstr "Uso de Swap"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "System"
|
||||
@@ -1353,8 +1481,8 @@ msgstr "Formato de hora"
|
||||
msgid "To email(s)"
|
||||
msgstr "Para email(s)"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "Toggle grid"
|
||||
msgstr "Alternar grade"
|
||||
|
||||
@@ -1397,7 +1525,7 @@ msgstr "Dados totais enviados para cada interface"
|
||||
#. placeholder {0}: data.length
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Total: {0}"
|
||||
msgstr "Total: {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Triggered by"
|
||||
@@ -1423,6 +1551,10 @@ msgstr "Dispara quando a média de carga de 5 minutos excede um limite"
|
||||
msgid "Triggers when any sensor exceeds a threshold"
|
||||
msgstr "Dispara quando qualquer sensor excede um limite"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when battery charge drops below a threshold"
|
||||
msgstr "Dispara quando a carga da bateria cai abaixo de um limite"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when combined up/down exceeds a threshold"
|
||||
msgstr "Dispara quando a soma de subida/descida excede um limite"
|
||||
@@ -1447,6 +1579,8 @@ msgstr "Dispara quando o status alterna entre ativo e inativo"
|
||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||
msgstr "Dispara quando o uso de qualquer disco excede um limite"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Type"
|
||||
msgstr "Tipo"
|
||||
@@ -1476,7 +1610,7 @@ msgid "Unlimited"
|
||||
msgstr "Ilimitado"
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Up"
|
||||
msgstr "Ligado"
|
||||
@@ -1485,7 +1619,12 @@ msgstr "Ligado"
|
||||
msgid "Up ({upSystemsLength})"
|
||||
msgstr "Ativo ({upSystemsLength})"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Update"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Updated"
|
||||
msgstr "Atualizado"
|
||||
@@ -1498,7 +1637,7 @@ msgstr "Atualizado a cada 10 minutos."
|
||||
msgid "Upload"
|
||||
msgstr "Carregar"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "Uptime"
|
||||
msgstr "Tempo de Atividade"
|
||||
|
||||
@@ -1570,8 +1709,8 @@ msgid "Webhook / Push notifications"
|
||||
msgstr "Notificações Webhook / Push"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation. Expires after one hour or on hub restart."
|
||||
msgstr "Quando habilitado, este token permite que os agentes se registrem automaticamente sem criação prévia do sistema. Expira após uma hora ou na reinicialização do hub."
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation."
|
||||
msgstr "Quando ativado, este token permite que os agentes se registrem automaticamente sem criação prévia do sistema."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user