Compare commits

..

48 Commits

Author SHA1 Message Date
henrygd
618e5b4cc1 fix purego build errors on non-supported architectures 2026-01-11 17:48:19 -05:00
henrygd
42c3ca5db5 release 0.18.0 2026-01-11 17:18:32 -05:00
henrygd
534791776b update translations 2026-01-11 17:09:43 -05:00
henrygd
0c6c53fc7d fix isSystemdAvailable in containers and update alpine to 3.23 2026-01-11 16:07:24 -05:00
henrygd
0dfd5ce07d update go deps and changelog 2026-01-11 15:06:58 -05:00
henrygd
2cd6d46f7c add option to make universal token permanent (#1097, 1614) 2026-01-11 15:03:33 -05:00
henrygd
c333a9fadd update translations 2026-01-11 13:50:11 -05:00
henrygd
ba3d1c66f0 refactor(auth): rename honeypot field to avoid autofill (#1011) 2026-01-09 15:12:34 -05:00
henrygd
7276e533ce update changelog and go deps 2026-01-09 13:23:05 -05:00
henrygd
8b84231042 refactor: update languages data structure 2026-01-09 12:19:43 -05:00
Natxo
77da744008 use origin country flags for Spanish and Portuguese languages (#1571) 2026-01-09 12:10:55 -05:00
henrygd
5da7a21119 agent: fix container logs decoding for raw streams (#1535) 2026-01-08 13:57:56 -05:00
henrygd
78d742c712 web: refactor gpu code for slighly better perf 2026-01-08 13:15:16 -05:00
crimist
1c97ea3e2c site: only hide GPU power graph if entire timescale is 0W 2026-01-05 19:42:10 -08:00
henrygd
3d970defe9 refactor: small comment / structure updates 2026-01-05 16:25:30 -05:00
Sven van Ginkel
6282794004 Add systemd check (#1550) 2026-01-05 15:59:17 -05:00
crimist
475c53a55d nvml: add rtd3 memory workaround, fix slog imports (#1587)
* NVML: only read memory usage if utilization > 0% to allow rtd3, #1522

* logging: /x/exp/slog -> log/slog everywhere, fixes log instance inconsistencies
2026-01-05 15:26:59 -05:00
henrygd
4547ff7b5d refactor: unify agent communication with Transport interface
- Introduce `Transport` interface to abstract WebSocket and SSH
communication
- Add generic `Data` field to `AgentResponse` for streamlined future
endpoints
- Maintain backward compatibility with legacy hubs and agents using
typed fields
- Unify fetch operations (SMART, systemd, containers) under a single
`request` method
- Improve `RequestManager` with deadline awareness and legacy response
support
- Refactor agent response routing into dedicated `agent/response.go`
- Update version to 0.18.0-beta.2
2026-01-05 13:13:55 -05:00
henrygd
e7b4be3dc5 fix(agent): update GPU average calculation tests to account for suspended state detection 2026-01-05 13:09:17 -05:00
henrygd
2bd85e04fc add experimental nvml gpu collector (#1522) 2025-12-21 17:10:42 -05:00
henrygd
f6ab5f2af1 refactor: rm diskinfo abstraction from smart-table.tsx 2025-12-21 12:25:12 -05:00
Sven van Ginkel
7d943633a3 fix capacity sorting in smart table (#1551) 2025-12-21 12:21:44 -05:00
henrygd
7fff3c999a update changelog and comments 2025-12-19 16:27:39 -05:00
henrygd
a9068a11a9 add SMART_INTERVAL env var with background smart data fetching 2025-12-19 16:14:31 -05:00
henrygd
d3d102516c refactor system.tsx: change null fallback for details 2025-12-19 00:45:15 -05:00
henrygd
32131439f9 fix systemd table visibility after moving os info to system_details
- also update bun.lock
2025-12-19 00:37:46 -05:00
Jesper Ek
d17685c540 fix non unique fingerprint (#1556) 2025-12-18 18:21:19 -05:00
henrygd
e59f8eee36 add system_details collection for infrequently updated data
- add includedetails flag to data requests for better efficiency
- integrate docker host info api for better os detection
- pull more OS details as well as cpu arch
- separate info bar component and refactor for new info
2025-12-18 17:26:05 -05:00
henrygd
35329abcbd agent install: improve freebsd checksum verification (#1526) 2025-12-12 15:32:55 -05:00
henrygd
ee7741c3ab add --url and --token command line args for agent (#1524) 2025-12-12 13:58:44 -05:00
David Davis
ab0803b2da Upgrade react to address CVE-2025-55182
More info: https://react.dev/blog/2025/12/03/critical-security-vulnerability-in-react-server-components
2025-12-12 07:42:29 -05:00
henrygd
96196a353c smart: fallback to nvme namespace path if base controller path fails (#1504) 2025-12-10 14:09:20 -05:00
henrygd
2a8796c38d update hub install script to support freebsd 2025-12-09 15:08:59 -05:00
Zero2A11
c8d4f7427d fix: When there is no client, LoaderCircle will always transfer (#1511) 2025-12-09 11:01:13 -05:00
henrygd
8d41a797d3 add battery charge to systems table 2025-12-08 18:20:51 -05:00
henrygd
570e1cbf40 pin alpine version to 3.22 2025-12-08 18:14:14 -05:00
henrygd
4c9b00a066 add low battery alert (#1507) 2025-12-08 15:19:46 -05:00
henrygd
7d1f8bb180 raise smartctl timeout to 15 seconds (#1465) 2025-12-05 14:59:11 -05:00
henrygd
3a6caeb06e add serbian 2025-12-05 14:21:50 -05:00
henrygd
9e67245e60 bump minimum go version to 1.25.5 2025-12-02 19:04:40 -05:00
henrygd
b7a95d5d76 release 0.17.0 2025-12-02 18:52:12 -05:00
henrygd
fe550c5901 refactor decodeDockerLogStream 2025-12-02 18:22:22 -05:00
henrygd
8aac0a571a update translations 2025-12-02 17:58:55 -05:00
henrygd
c506b8b0ad pull translations 2025-12-02 16:57:12 -05:00
henrygd
a6e84c207e update changelog 2025-11-30 16:24:05 -05:00
henrygd
249402eaed add hub builds for windows and freebsd 2025-11-30 15:25:31 -05:00
henrygd
394c476f2a strip ansi escape sequences from docker logs (#1478) 2025-11-30 14:36:00 -05:00
henrygd
86e8a141ea add DISK_USAGE_CACHE env var (#1426) 2025-11-30 14:21:00 -05:00
115 changed files with 11052 additions and 6607 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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{},
}

View File

@@ -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.

View File

@@ -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

View File

@@ -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")
})
}

View File

@@ -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
}

View File

@@ -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)
})
}
}

View File

@@ -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
View 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
View 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
}

View 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
View 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
}

View File

@@ -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{

View File

@@ -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
View 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
}

View File

@@ -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)
}

View File

@@ -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{
{

View File

@@ -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{

View File

@@ -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)
})
}
}

View File

@@ -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")
}

View File

@@ -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")

View File

@@ -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

View 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"
}

View File

@@ -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
View File

@@ -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
View File

@@ -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=

View File

@@ -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 {

View 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")
}

View File

@@ -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"
}

View File

@@ -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
}

View File

@@ -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 {

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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"`
}

View File

@@ -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)

View File

@@ -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)

View File

@@ -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) {

View File

@@ -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)
}

View File

@@ -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,
},
{

View File

@@ -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

View File

@@ -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()

View 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 }

View File

@@ -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,

View File

@@ -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()

View 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)
}

View 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)
}

View 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)
}
}

View File

@@ -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

View File

@@ -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())
}

View File

@@ -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()

View File

@@ -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)
}

View File

@@ -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

View File

@@ -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
}
]`

View File

@@ -33,10 +33,7 @@
"noUnusedFunctionParameters": "error",
"noUnusedPrivateClassMembers": "error",
"useExhaustiveDependencies": {
"level": "warn",
"options": {
"reportUnnecessaryDependencies": false
}
"level": "off"
},
"useUniqueElementIds": "off",
"noUnusedVariables": "error"

View File

@@ -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=="],
}
}

View File

@@ -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",

View File

@@ -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": {

View File

@@ -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"
}
}
}

View File

@@ -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}

View File

@@ -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>

View File

@@ -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

View File

@@ -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 (

View File

@@ -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")}

View File

@@ -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>

View File

@@ -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>

View File

@@ -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) => (

View File

@@ -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>

View File

@@ -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>
)
}
}

View 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>
)
}

View File

@@ -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>

View File

@@ -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])
})

View File

@@ -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>
)
}

View File

@@ -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 {

View File

@@ -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 */

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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