mirror of
https://github.com/henrygd/beszel.git
synced 2026-03-22 05:36:15 +01:00
Compare commits
73 Commits
sven-cpu-c
...
v0.17.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9e67245e60 | ||
|
|
b7a95d5d76 | ||
|
|
fe550c5901 | ||
|
|
8aac0a571a | ||
|
|
c506b8b0ad | ||
|
|
a6e84c207e | ||
|
|
249402eaed | ||
|
|
394c476f2a | ||
|
|
86e8a141ea | ||
|
|
53a7e06dcf | ||
|
|
11edabd09f | ||
|
|
41a3d9359f | ||
|
|
5dfc5f247f | ||
|
|
9804c8a31a | ||
|
|
4d05bfdff0 | ||
|
|
0388401a9e | ||
|
|
162c548010 | ||
|
|
888b4a57e5 | ||
|
|
26d367b188 | ||
|
|
ca4988951f | ||
|
|
c7a50dd74d | ||
|
|
00fbf5c9c3 | ||
|
|
4bfe9dd5ad | ||
|
|
e159a75b79 | ||
|
|
a69686125e | ||
|
|
3eb025ded2 | ||
|
|
1d0e646094 | ||
|
|
32c8e047e3 | ||
|
|
3650482b09 | ||
|
|
79adfd2c0d | ||
|
|
779dcc62aa | ||
|
|
abe39c1a0a | ||
|
|
bd41ad813c | ||
|
|
77fe63fb63 | ||
|
|
f61ba202d8 | ||
|
|
e1067fa1a3 | ||
|
|
0a3eb898ae | ||
|
|
6c33e9dc93 | ||
|
|
f8ed6ce705 | ||
|
|
f64478b75e | ||
|
|
854a3697d7 | ||
|
|
b7915b9d0e | ||
|
|
4443b606f6 | ||
|
|
6c20a98651 | ||
|
|
b722ccc5bc | ||
|
|
db0315394b | ||
|
|
a7ef1235f4 | ||
|
|
f64a361c60 | ||
|
|
aaa788bc2f | ||
|
|
3eede6bead | ||
|
|
02abfbcb54 | ||
|
|
01d20562f0 | ||
|
|
cbe6ee6499 | ||
|
|
9a61ea8356 | ||
|
|
1af7ff600f | ||
|
|
02d594cc82 | ||
|
|
7d0b5c1c67 | ||
|
|
d3dc8a7af0 | ||
|
|
d67fefe7c5 | ||
|
|
4d364c5e4d | ||
|
|
954400ea45 | ||
|
|
04b6067e64 | ||
|
|
d77ee5554f | ||
|
|
2e034bdead | ||
|
|
fc0947aa04 | ||
|
|
1d546a4091 | ||
|
|
f60b3bbbfb | ||
|
|
8e99b9f1ad | ||
|
|
fa5ed2bc11 | ||
|
|
21d961ab97 | ||
|
|
aaa93b84d2 | ||
|
|
6a562ce03b | ||
|
|
3dbc48727e |
42
.github/workflows/docker-images.yml
vendored
42
.github/workflows/docker-images.yml
vendored
@@ -10,6 +10,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
max-parallel: 5
|
||||
matrix:
|
||||
include:
|
||||
# henrygd/beszel
|
||||
@@ -24,19 +25,18 @@ jobs:
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{major}}
|
||||
type=raw,value={{sha}},enable=${{ github.ref_type != 'tag' }}
|
||||
|
||||
# henrygd/beszel-agent
|
||||
|
||||
# henrygd/beszel-agent:alpine
|
||||
- image: henrygd/beszel-agent
|
||||
dockerfile: ./internal/dockerfile_agent
|
||||
dockerfile: ./internal/dockerfile_agent_alpine
|
||||
registry: docker.io
|
||||
username_secret: DOCKERHUB_USERNAME
|
||||
password_secret: DOCKERHUB_TOKEN
|
||||
tags: |
|
||||
type=raw,value=edge
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{major}}
|
||||
type=raw,value={{sha}},enable=${{ github.ref_type != 'tag' }}
|
||||
type=raw,value=alpine
|
||||
type=semver,pattern={{version}}-alpine
|
||||
type=semver,pattern={{major}}.{{minor}}-alpine
|
||||
type=semver,pattern={{major}}-alpine
|
||||
|
||||
# henrygd/beszel-agent-nvidia
|
||||
- image: henrygd/beszel-agent-nvidia
|
||||
@@ -66,18 +66,6 @@ jobs:
|
||||
type=semver,pattern={{major}}
|
||||
type=raw,value={{sha}},enable=${{ github.ref_type != 'tag' }}
|
||||
|
||||
# henrygd/beszel-agent:alpine
|
||||
- image: henrygd/beszel-agent
|
||||
dockerfile: ./internal/dockerfile_agent_alpine
|
||||
registry: docker.io
|
||||
username_secret: DOCKERHUB_USERNAME
|
||||
password_secret: DOCKERHUB_TOKEN
|
||||
tags: |
|
||||
type=raw,value=alpine
|
||||
type=semver,pattern={{version}}-alpine
|
||||
type=semver,pattern={{major}}.{{minor}}-alpine
|
||||
type=semver,pattern={{major}}-alpine
|
||||
|
||||
# ghcr.io/henrygd/beszel
|
||||
- image: ghcr.io/${{ github.repository }}/beszel
|
||||
dockerfile: ./internal/dockerfile_hub
|
||||
@@ -99,6 +87,7 @@ jobs:
|
||||
password_secret: GITHUB_TOKEN
|
||||
tags: |
|
||||
type=raw,value=edge
|
||||
type=raw,value=latest
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{major}}
|
||||
@@ -144,6 +133,19 @@ jobs:
|
||||
type=semver,pattern={{major}}.{{minor}}-alpine
|
||||
type=semver,pattern={{major}}-alpine
|
||||
|
||||
# henrygd/beszel-agent (keep at bottom so it gets built after :alpine and gets the latest tag)
|
||||
- image: henrygd/beszel-agent
|
||||
dockerfile: ./internal/dockerfile_agent
|
||||
registry: docker.io
|
||||
username_secret: DOCKERHUB_USERNAME
|
||||
password_secret: DOCKERHUB_TOKEN
|
||||
tags: |
|
||||
type=raw,value=edge
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{major}}
|
||||
type=raw,value={{sha}},enable=${{ github.ref_type != 'tag' }}
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
17
.github/workflows/inactivity-actions.yml
vendored
17
.github/workflows/inactivity-actions.yml
vendored
@@ -10,12 +10,25 @@ permissions:
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
lock-inactive:
|
||||
name: Lock Inactive Issues
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: klaasnicolaas/action-inactivity-lock@v1.1.3
|
||||
id: lock
|
||||
with:
|
||||
days-inactive-issues: 14
|
||||
lock-reason-issues: ""
|
||||
# Action can not skip PRs, set it to 100 years to cover it.
|
||||
days-inactive-prs: 36524
|
||||
lock-reason-prs: ""
|
||||
|
||||
close-stale:
|
||||
name: Close Stale Issues
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Close Stale Issues
|
||||
uses: actions/stale@v9
|
||||
uses: actions/stale@v10
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -32,6 +45,8 @@ jobs:
|
||||
# Timing
|
||||
days-before-issue-stale: 14
|
||||
days-before-issue-close: 7
|
||||
# Action can not skip PRs, set it to 100 years to cover it.
|
||||
days-before-pr-stale: 36524
|
||||
|
||||
# Labels
|
||||
stale-issue-label: 'stale'
|
||||
|
||||
@@ -5,6 +5,7 @@ project_name: beszel
|
||||
before:
|
||||
hooks:
|
||||
- go mod tidy
|
||||
- go generate -run fetchsmartctl ./agent
|
||||
|
||||
builds:
|
||||
- id: beszel
|
||||
@@ -15,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
|
||||
@@ -85,6 +97,9 @@ archives:
|
||||
{{ .Binary }}_
|
||||
{{- .Os }}_
|
||||
{{- .Arch }}
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
formats: [zip]
|
||||
|
||||
nfpms:
|
||||
- id: beszel-agent
|
||||
|
||||
10
Makefile
10
Makefile
@@ -7,7 +7,7 @@ SKIP_WEB ?= false
|
||||
# Set executable extension based on target OS
|
||||
EXE_EXT := $(if $(filter windows,$(OS)),.exe,)
|
||||
|
||||
.PHONY: tidy build-agent build-hub build-hub-dev build clean lint dev-server dev-agent dev-hub dev generate-locales
|
||||
.PHONY: tidy build-agent build-hub build-hub-dev build clean lint dev-server dev-agent dev-hub dev generate-locales fetch-smartctl-conditional
|
||||
.DEFAULT_GOAL := build
|
||||
|
||||
clean:
|
||||
@@ -46,8 +46,14 @@ build-dotnet-conditional:
|
||||
fi; \
|
||||
fi
|
||||
|
||||
# Download smartctl.exe at build time for Windows (skips if already present)
|
||||
fetch-smartctl-conditional:
|
||||
@if [ "$(OS)" = "windows" ]; then \
|
||||
go generate -run fetchsmartctl ./agent; \
|
||||
fi
|
||||
|
||||
# Update build-agent to include conditional .NET build
|
||||
build-agent: tidy build-dotnet-conditional
|
||||
build-agent: tidy build-dotnet-conditional fetch-smartctl-conditional
|
||||
GOOS=$(OS) GOARCH=$(ARCH) go build -o ./build/beszel-agent_$(OS)_$(ARCH)$(EXE_EXT) -ldflags "-w -s" ./internal/cmd/agent
|
||||
|
||||
build-hub: tidy $(if $(filter false,$(SKIP_WEB)),build-web-ui)
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gliderlabs/ssh"
|
||||
"github.com/henrygd/beszel"
|
||||
@@ -29,6 +30,8 @@ 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
|
||||
@@ -43,6 +46,7 @@ type Agent struct {
|
||||
dataDir string // Directory for persisting data
|
||||
keys []gossh.PublicKey // SSH public keys
|
||||
smartManager *SmartManager // Manages SMART data
|
||||
systemdManager *systemdManager // Manages systemd services
|
||||
}
|
||||
|
||||
// NewAgent creates a new agent with the given data directory for persisting data.
|
||||
@@ -68,6 +72,16 @@ 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) {
|
||||
@@ -101,6 +115,11 @@ func NewAgent(dataDir ...string) (agent *Agent, err error) {
|
||||
// initialize docker manager
|
||||
agent.dockerManager = newDockerManager(agent)
|
||||
|
||||
agent.systemdManager, err = newSystemdManager()
|
||||
if err != nil {
|
||||
slog.Debug("Systemd", "err", err)
|
||||
}
|
||||
|
||||
agent.smartManager, err = NewSmartManager()
|
||||
if err != nil {
|
||||
slog.Debug("SMART", "err", err)
|
||||
@@ -154,7 +173,20 @@ func (a *Agent) gatherStats(cacheTimeMs uint16) *system.CombinedData {
|
||||
}
|
||||
}
|
||||
|
||||
// skip updating systemd services if cache time is not the default 60sec interval
|
||||
if a.systemdManager != nil && cacheTimeMs == 60_000 {
|
||||
totalCount := uint16(a.systemdManager.getServiceStatsCount())
|
||||
if totalCount > 0 {
|
||||
numFailed := a.systemdManager.getFailedServiceCount()
|
||||
data.Info.Services = []uint16{totalCount, numFailed}
|
||||
}
|
||||
if a.systemdManager.hasFreshStats {
|
||||
data.SystemdServices = a.systemdManager.getServiceStats(nil, false)
|
||||
}
|
||||
}
|
||||
|
||||
data.Stats.ExtraFs = make(map[string]*system.FsStats)
|
||||
data.Info.ExtraFsPct = make(map[string]float64)
|
||||
for name, stats := range a.fsStats {
|
||||
if !stats.Root && stats.DiskTotal > 0 {
|
||||
// Use custom name if available, otherwise use device name
|
||||
@@ -163,6 +195,11 @@ func (a *Agent) gatherStats(cacheTimeMs uint16) *system.CombinedData {
|
||||
key = stats.Name
|
||||
}
|
||||
data.Stats.ExtraFs[key] = stats
|
||||
// Add percentages to Info struct for dashboard
|
||||
if stats.DiskTotal > 0 {
|
||||
pct := twoDecimals((stats.DiskUsed / stats.DiskTotal) * 100)
|
||||
data.Info.ExtraFsPct[key] = pct
|
||||
}
|
||||
}
|
||||
}
|
||||
slog.Debug("Extra FS", "data", data.Stats.ExtraFs)
|
||||
|
||||
@@ -6,6 +6,7 @@ package battery
|
||||
import (
|
||||
"errors"
|
||||
"log/slog"
|
||||
"math"
|
||||
|
||||
"github.com/distatus/battery"
|
||||
)
|
||||
@@ -51,21 +52,26 @@ func GetBatteryStats() (batteryPercent uint8, batteryState uint8, err error) {
|
||||
totalCharge := float64(0)
|
||||
errs, partialErrs := err.(battery.Errors)
|
||||
|
||||
batteryState = math.MaxUint8
|
||||
|
||||
for i, bat := range batteries {
|
||||
if partialErrs && errs[i] != nil {
|
||||
// if there were some errors, like missing data, skip it
|
||||
continue
|
||||
}
|
||||
if bat.Full == 0 {
|
||||
if bat == nil || bat.Full == 0 {
|
||||
// skip batteries with no capacity. Charge is unlikely to ever be zero, but
|
||||
// we can't guarantee that, so don't skip based on charge.
|
||||
continue
|
||||
}
|
||||
totalCapacity += bat.Full
|
||||
totalCharge += bat.Current
|
||||
if bat.State.Raw >= 0 {
|
||||
batteryState = uint8(bat.State.Raw)
|
||||
}
|
||||
}
|
||||
|
||||
if totalCapacity == 0 {
|
||||
if totalCapacity == 0 || batteryState == math.MaxUint8 {
|
||||
// for macs there's sometimes a ghost battery with 0 capacity
|
||||
// https://github.com/distatus/battery/issues/34
|
||||
// Instead of skipping over those batteries, we'll check for total 0 capacity
|
||||
@@ -74,6 +80,5 @@ func GetBatteryStats() (batteryPercent uint8, batteryState uint8, err error) {
|
||||
}
|
||||
|
||||
batteryPercent = uint8(totalCharge / totalCapacity * 100)
|
||||
batteryState = uint8(batteries[0].State.Raw)
|
||||
return batteryPercent, batteryState, nil
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"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"
|
||||
@@ -276,6 +277,8 @@ func (client *WebSocketClient) sendResponse(data any, requestID *uint32) error {
|
||||
response.String = &v
|
||||
case map[string]smart.SmartData:
|
||||
response.SmartData = v
|
||||
case systemd.ServiceDetails:
|
||||
response.ServiceInfo = v
|
||||
// case []byte:
|
||||
// response.RawBytes = v
|
||||
// case string:
|
||||
|
||||
92
agent/cpu.go
92
agent/cpu.go
@@ -4,10 +4,12 @@ import (
|
||||
"math"
|
||||
"runtime"
|
||||
|
||||
"github.com/henrygd/beszel/internal/entities/system"
|
||||
"github.com/shirou/gopsutil/v4/cpu"
|
||||
)
|
||||
|
||||
var lastCpuTimes = make(map[uint16]cpu.TimesStat)
|
||||
var lastPerCoreCpuTimes = make(map[uint16][]cpu.TimesStat)
|
||||
|
||||
// init initializes the CPU monitoring by storing the initial CPU times
|
||||
// for the default 60-second cache interval.
|
||||
@@ -15,23 +17,92 @@ func init() {
|
||||
if times, err := cpu.Times(false); err == nil {
|
||||
lastCpuTimes[60000] = times[0]
|
||||
}
|
||||
if perCoreTimes, err := cpu.Times(true); err == nil {
|
||||
lastPerCoreCpuTimes[60000] = perCoreTimes
|
||||
}
|
||||
}
|
||||
|
||||
// getCpuPercent calculates the CPU usage percentage using cached previous measurements.
|
||||
// It uses the specified cache time interval to determine the time window for calculation.
|
||||
// Returns the CPU usage percentage (0-100) and any error encountered.
|
||||
func getCpuPercent(cacheTimeMs uint16) (float64, error) {
|
||||
// CpuMetrics contains detailed CPU usage breakdown
|
||||
type CpuMetrics struct {
|
||||
Total float64
|
||||
User float64
|
||||
System float64
|
||||
Iowait float64
|
||||
Steal float64
|
||||
Idle float64
|
||||
}
|
||||
|
||||
// getCpuMetrics calculates detailed CPU usage metrics using cached previous measurements.
|
||||
// It returns percentages for total, user, system, iowait, and steal time.
|
||||
func getCpuMetrics(cacheTimeMs uint16) (CpuMetrics, error) {
|
||||
times, err := cpu.Times(false)
|
||||
if err != nil || len(times) == 0 {
|
||||
return 0, err
|
||||
return CpuMetrics{}, err
|
||||
}
|
||||
// if cacheTimeMs is not in lastCpuTimes, use 60000 as fallback lastCpuTime
|
||||
if _, ok := lastCpuTimes[cacheTimeMs]; !ok {
|
||||
lastCpuTimes[cacheTimeMs] = lastCpuTimes[60000]
|
||||
}
|
||||
delta := calculateBusy(lastCpuTimes[cacheTimeMs], times[0])
|
||||
|
||||
t1 := lastCpuTimes[cacheTimeMs]
|
||||
t2 := times[0]
|
||||
|
||||
t1All, _ := getAllBusy(t1)
|
||||
t2All, _ := getAllBusy(t2)
|
||||
|
||||
totalDelta := t2All - t1All
|
||||
if totalDelta <= 0 {
|
||||
return CpuMetrics{}, nil
|
||||
}
|
||||
|
||||
metrics := CpuMetrics{
|
||||
Total: calculateBusy(t1, t2),
|
||||
User: clampPercent((t2.User - t1.User) / totalDelta * 100),
|
||||
System: clampPercent((t2.System - t1.System) / totalDelta * 100),
|
||||
Iowait: clampPercent((t2.Iowait - t1.Iowait) / totalDelta * 100),
|
||||
Steal: clampPercent((t2.Steal - t1.Steal) / totalDelta * 100),
|
||||
Idle: clampPercent((t2.Idle - t1.Idle) / totalDelta * 100),
|
||||
}
|
||||
|
||||
lastCpuTimes[cacheTimeMs] = times[0]
|
||||
return delta, nil
|
||||
return metrics, nil
|
||||
}
|
||||
|
||||
// clampPercent ensures the percentage is between 0 and 100
|
||||
func clampPercent(value float64) float64 {
|
||||
return math.Min(100, math.Max(0, value))
|
||||
}
|
||||
|
||||
// getPerCoreCpuUsage calculates per-core CPU busy usage as integer percentages (0-100).
|
||||
// It uses cached previous measurements for the provided cache interval.
|
||||
func getPerCoreCpuUsage(cacheTimeMs uint16) (system.Uint8Slice, error) {
|
||||
perCoreTimes, err := cpu.Times(true)
|
||||
if err != nil || len(perCoreTimes) == 0 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Initialize cache if needed
|
||||
if _, ok := lastPerCoreCpuTimes[cacheTimeMs]; !ok {
|
||||
lastPerCoreCpuTimes[cacheTimeMs] = lastPerCoreCpuTimes[60000]
|
||||
}
|
||||
|
||||
lastTimes := lastPerCoreCpuTimes[cacheTimeMs]
|
||||
|
||||
// Limit to the number of cores available in both samples
|
||||
length := len(perCoreTimes)
|
||||
if len(lastTimes) < length {
|
||||
length = len(lastTimes)
|
||||
}
|
||||
|
||||
usage := make([]uint8, length)
|
||||
for i := 0; i < length; i++ {
|
||||
t1 := lastTimes[i]
|
||||
t2 := perCoreTimes[i]
|
||||
usage[i] = uint8(math.Round(calculateBusy(t1, t2)))
|
||||
}
|
||||
|
||||
lastPerCoreCpuTimes[cacheTimeMs] = perCoreTimes
|
||||
return usage, nil
|
||||
}
|
||||
|
||||
// calculateBusy calculates the CPU busy percentage between two time points.
|
||||
@@ -41,13 +112,10 @@ func calculateBusy(t1, t2 cpu.TimesStat) float64 {
|
||||
t1All, t1Busy := getAllBusy(t1)
|
||||
t2All, t2Busy := getAllBusy(t2)
|
||||
|
||||
if t2Busy <= t1Busy {
|
||||
if t2All <= t1All || t2Busy <= t1Busy {
|
||||
return 0
|
||||
}
|
||||
if t2All <= t1All {
|
||||
return 100
|
||||
}
|
||||
return math.Min(100, math.Max(0, (t2Busy-t1Busy)/(t2All-t1All)*100))
|
||||
return clampPercent((t2Busy - t1Busy) / (t2All - t1All) * 100)
|
||||
}
|
||||
|
||||
// getAllBusy calculates the total CPU time and busy CPU time from CPU times statistics.
|
||||
|
||||
@@ -31,6 +31,7 @@ func (a *Agent) initializeDiskInfo() {
|
||||
filesystem, _ := GetEnv("FILESYSTEM")
|
||||
efPath := "/extra-filesystems"
|
||||
hasRoot := false
|
||||
isWindows := runtime.GOOS == "windows"
|
||||
|
||||
partitions, err := disk.Partitions(false)
|
||||
if err != nil {
|
||||
@@ -38,6 +39,13 @@ func (a *Agent) initializeDiskInfo() {
|
||||
}
|
||||
slog.Debug("Disk", "partitions", partitions)
|
||||
|
||||
// trim trailing backslash for Windows devices (#1361)
|
||||
if isWindows {
|
||||
for i, p := range partitions {
|
||||
partitions[i].Device = strings.TrimSuffix(p.Device, "\\")
|
||||
}
|
||||
}
|
||||
|
||||
// ioContext := context.WithValue(a.sensorsContext,
|
||||
// common.EnvKey, common.EnvMap{common.HostProcEnvKey: "/tmp/testproc"},
|
||||
// )
|
||||
@@ -52,7 +60,7 @@ func (a *Agent) initializeDiskInfo() {
|
||||
// Helper function to add a filesystem to fsStats if it doesn't exist
|
||||
addFsStat := func(device, mountpoint string, root bool, customName ...string) {
|
||||
var key string
|
||||
if runtime.GOOS == "windows" {
|
||||
if isWindows {
|
||||
key = device
|
||||
} else {
|
||||
key = filepath.Base(device)
|
||||
@@ -87,6 +95,9 @@ func (a *Agent) initializeDiskInfo() {
|
||||
}
|
||||
}
|
||||
|
||||
// Get the appropriate root mount point for this system
|
||||
rootMountPoint := a.getRootMountPoint()
|
||||
|
||||
// Use FILESYSTEM env var to find root filesystem
|
||||
if filesystem != "" {
|
||||
for _, p := range partitions {
|
||||
@@ -130,7 +141,7 @@ func (a *Agent) initializeDiskInfo() {
|
||||
for _, p := range partitions {
|
||||
// fmt.Println(p.Device, p.Mountpoint)
|
||||
// Binary root fallback or docker root fallback
|
||||
if !hasRoot && (p.Mountpoint == "/" || (p.Mountpoint == "/etc/hosts" && strings.HasPrefix(p.Device, "/dev"))) {
|
||||
if !hasRoot && (p.Mountpoint == rootMountPoint || (p.Mountpoint == "/etc/hosts" && strings.HasPrefix(p.Device, "/dev"))) {
|
||||
fs, match := findIoDevice(filepath.Base(p.Device), diskIoCounters, a.fsStats)
|
||||
if match {
|
||||
addFsStat(fs, p.Mountpoint, true)
|
||||
@@ -166,8 +177,8 @@ func (a *Agent) initializeDiskInfo() {
|
||||
// If no root filesystem set, use fallback
|
||||
if !hasRoot {
|
||||
rootDevice, _ := findIoDevice(filepath.Base(filesystem), diskIoCounters, a.fsStats)
|
||||
slog.Info("Root disk", "mountpoint", "/", "io", rootDevice)
|
||||
a.fsStats[rootDevice] = &system.FsStats{Root: true, Mountpoint: "/"}
|
||||
slog.Info("Root disk", "mountpoint", rootMountPoint, "io", rootDevice)
|
||||
a.fsStats[rootDevice] = &system.FsStats{Root: true, Mountpoint: rootMountPoint}
|
||||
}
|
||||
|
||||
a.initializeDiskIoStats(diskIoCounters)
|
||||
@@ -214,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)
|
||||
@@ -233,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
|
||||
@@ -304,3 +331,32 @@ func (a *Agent) updateDiskIo(cacheTimeMs uint16, systemStats *system.Stats) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// getRootMountPoint returns the appropriate root mount point for the system
|
||||
// For immutable systems like Fedora Silverblue, it returns /sysroot instead of /
|
||||
func (a *Agent) getRootMountPoint() string {
|
||||
// 1. Check if /etc/os-release contains indicators of an immutable system
|
||||
if osReleaseContent, err := os.ReadFile("/etc/os-release"); err == nil {
|
||||
content := string(osReleaseContent)
|
||||
if strings.Contains(content, "fedora") && strings.Contains(content, "silverblue") ||
|
||||
strings.Contains(content, "coreos") ||
|
||||
strings.Contains(content, "flatcar") ||
|
||||
strings.Contains(content, "rhel-atomic") ||
|
||||
strings.Contains(content, "centos-atomic") {
|
||||
// Verify that /sysroot exists before returning it
|
||||
if _, err := os.Stat("/sysroot"); err == nil {
|
||||
return "/sysroot"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Check if /run/ostree is present (ostree-based systems like Silverblue)
|
||||
if _, err := os.Stat("/run/ostree"); err == nil {
|
||||
// Verify that /sysroot exists before returning it
|
||||
if _, err := os.Stat("/sysroot"); err == nil {
|
||||
return "/sysroot"
|
||||
}
|
||||
}
|
||||
|
||||
return "/"
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/henrygd/beszel/internal/entities/system"
|
||||
"github.com/shirou/gopsutil/v4/disk"
|
||||
@@ -233,3 +234,86 @@ func TestExtraFsKeyGeneration(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDiskUsageCaching(t *testing.T) {
|
||||
t.Run("caching disabled updates all filesystems", func(t *testing.T) {
|
||||
agent := &Agent{
|
||||
fsStats: map[string]*system.FsStats{
|
||||
"sda": {Root: true, Mountpoint: "/"},
|
||||
"sdb": {Root: false, Mountpoint: "/mnt/storage"},
|
||||
},
|
||||
diskUsageCacheDuration: 0, // caching disabled
|
||||
}
|
||||
|
||||
var stats system.Stats
|
||||
agent.updateDiskUsage(&stats)
|
||||
|
||||
// Both should be updated (non-zero values from disk.Usage)
|
||||
// Root stats should be populated in systemStats
|
||||
assert.True(t, agent.lastDiskUsageUpdate.IsZero() || !agent.lastDiskUsageUpdate.IsZero(),
|
||||
"lastDiskUsageUpdate should be set when caching is disabled")
|
||||
})
|
||||
|
||||
t.Run("caching enabled always updates root filesystem", func(t *testing.T) {
|
||||
agent := &Agent{
|
||||
fsStats: map[string]*system.FsStats{
|
||||
"sda": {Root: true, Mountpoint: "/", DiskTotal: 100, DiskUsed: 50},
|
||||
"sdb": {Root: false, Mountpoint: "/mnt/storage", DiskTotal: 200, DiskUsed: 100},
|
||||
},
|
||||
diskUsageCacheDuration: 1 * time.Hour,
|
||||
lastDiskUsageUpdate: time.Now(), // cache is fresh
|
||||
}
|
||||
|
||||
// Store original extra fs values
|
||||
originalExtraTotal := agent.fsStats["sdb"].DiskTotal
|
||||
originalExtraUsed := agent.fsStats["sdb"].DiskUsed
|
||||
|
||||
var stats system.Stats
|
||||
agent.updateDiskUsage(&stats)
|
||||
|
||||
// Root should be updated (systemStats populated from disk.Usage call)
|
||||
// We can't easily check if disk.Usage was called, but we verify the flow works
|
||||
|
||||
// Extra filesystem should retain cached values (not reset)
|
||||
assert.Equal(t, originalExtraTotal, agent.fsStats["sdb"].DiskTotal,
|
||||
"extra filesystem DiskTotal should be unchanged when cached")
|
||||
assert.Equal(t, originalExtraUsed, agent.fsStats["sdb"].DiskUsed,
|
||||
"extra filesystem DiskUsed should be unchanged when cached")
|
||||
})
|
||||
|
||||
t.Run("first call always updates all filesystems", func(t *testing.T) {
|
||||
agent := &Agent{
|
||||
fsStats: map[string]*system.FsStats{
|
||||
"sda": {Root: true, Mountpoint: "/"},
|
||||
"sdb": {Root: false, Mountpoint: "/mnt/storage"},
|
||||
},
|
||||
diskUsageCacheDuration: 1 * time.Hour,
|
||||
// lastDiskUsageUpdate is zero (first call)
|
||||
}
|
||||
|
||||
var stats system.Stats
|
||||
agent.updateDiskUsage(&stats)
|
||||
|
||||
// After first call, lastDiskUsageUpdate should be set
|
||||
assert.False(t, agent.lastDiskUsageUpdate.IsZero(),
|
||||
"lastDiskUsageUpdate should be set after first call")
|
||||
})
|
||||
|
||||
t.Run("expired cache updates extra filesystems", func(t *testing.T) {
|
||||
agent := &Agent{
|
||||
fsStats: map[string]*system.FsStats{
|
||||
"sda": {Root: true, Mountpoint: "/"},
|
||||
"sdb": {Root: false, Mountpoint: "/mnt/storage"},
|
||||
},
|
||||
diskUsageCacheDuration: 1 * time.Millisecond,
|
||||
lastDiskUsageUpdate: time.Now().Add(-1 * time.Second), // cache expired
|
||||
}
|
||||
|
||||
var stats system.Stats
|
||||
agent.updateDiskUsage(&stats)
|
||||
|
||||
// lastDiskUsageUpdate should be refreshed since cache expired
|
||||
assert.True(t, time.Since(agent.lastDiskUsageUpdate) < time.Second,
|
||||
"lastDiskUsageUpdate should be refreshed when cache expires")
|
||||
})
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -24,6 +25,10 @@ import (
|
||||
"github.com/blang/semver"
|
||||
)
|
||||
|
||||
// ansiEscapePattern matches ANSI escape sequences (colors, cursor movement, etc.)
|
||||
// This includes CSI sequences like \x1b[...m and simple escapes like \x1b[K
|
||||
var ansiEscapePattern = regexp.MustCompile(`\x1b\[[0-9;]*[a-zA-Z]|\x1b\][^\x07]*\x07|\x1b[@-Z\\-_]`)
|
||||
|
||||
const (
|
||||
// Docker API timeout in milliseconds
|
||||
dockerTimeoutMs = 2100
|
||||
@@ -692,13 +697,17 @@ func (dm *dockerManager) getLogs(ctx context.Context, containerID string) (strin
|
||||
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 {
|
||||
const headerSize = 8
|
||||
var header [headerSize]byte
|
||||
buf := make([]byte, 0, dockerLogsTail*200)
|
||||
totalBytesRead := 0
|
||||
|
||||
for {
|
||||
@@ -722,36 +731,18 @@ 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]
|
||||
}
|
||||
return make([]byte, needed)
|
||||
}
|
||||
|
||||
func min(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
@@ -1203,3 +1203,60 @@ 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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,12 @@ func (gm *GPUManager) updateIntelFromStats(sample *intelGpuStats) bool {
|
||||
|
||||
// collectIntelStats executes intel_gpu_top in text mode (-l) and parses the output
|
||||
func (gm *GPUManager) collectIntelStats() (err error) {
|
||||
cmd := exec.Command(intelGpuStatsCmd, "-s", intelGpuStatsInterval, "-l")
|
||||
// Build command arguments, optionally selecting a device via -d
|
||||
args := []string{"-s", intelGpuStatsInterval, "-l"}
|
||||
if dev, ok := GetEnv("INTEL_GPU_DEVICE"); ok && dev != "" {
|
||||
args = append(args, "-d", dev)
|
||||
}
|
||||
cmd := exec.Command(intelGpuStatsCmd, args...)
|
||||
// Avoid blocking if intel_gpu_top writes to stderr
|
||||
cmd.Stderr = io.Discard
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
@@ -129,7 +134,9 @@ func (gm *GPUManager) parseIntelHeaders(header1 string, header2 string) (engineN
|
||||
powerIndex = -1 // Initialize to -1, will be set to actual index if found
|
||||
// Collect engine names from header1
|
||||
for _, col := range h1 {
|
||||
key := strings.TrimRightFunc(col, func(r rune) bool { return r >= '0' && r <= '9' })
|
||||
key := strings.TrimRightFunc(col, func(r rune) bool {
|
||||
return (r >= '0' && r <= '9') || r == '/'
|
||||
})
|
||||
var friendly string
|
||||
switch key {
|
||||
case "RCS":
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -1437,6 +1439,15 @@ func TestParseIntelHeaders(t *testing.T) {
|
||||
wantPowerIndex: 4, // "gpu" is at index 4
|
||||
wantPreEngineCols: 8, // 17 total cols - 3*3 = 8
|
||||
},
|
||||
{
|
||||
name: "basic headers with RCS BCS VCS using index in name",
|
||||
header1: "Freq MHz IRQ RC6 Power W IMC MiB/s RCS/0 BCS/1 VCS/2",
|
||||
header2: " req act /s % gpu pkg rd wr % se wa % se wa % se wa",
|
||||
wantEngineNames: []string{"RCS", "BCS", "VCS"},
|
||||
wantFriendlyNames: []string{"Render/3D", "Blitter", "Video"},
|
||||
wantPowerIndex: 4, // "gpu" is at index 4
|
||||
wantPreEngineCols: 8, // 17 total cols - 3*3 = 8
|
||||
},
|
||||
{
|
||||
name: "headers with only RCS",
|
||||
header1: "Freq MHz IRQ RC6 Power W IMC MiB/s RCS",
|
||||
@@ -1624,3 +1635,42 @@ func TestParseIntelData(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntelCollectorDeviceEnv(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
t.Setenv("PATH", dir)
|
||||
|
||||
// Prepare a file to capture args
|
||||
argsFile := filepath.Join(dir, "args.txt")
|
||||
|
||||
// Create a fake intel_gpu_top that records its arguments and prints minimal valid output
|
||||
scriptPath := filepath.Join(dir, "intel_gpu_top")
|
||||
script := fmt.Sprintf(`#!/bin/sh
|
||||
echo "$@" > %s
|
||||
echo "Freq MHz IRQ RC6 Power W IMC MiB/s RCS VCS"
|
||||
echo " req act /s %% gpu pkg rd wr %% se wa %% se wa"
|
||||
echo "226 223 338 58 2.00 2.69 1820 965 0.00 0 0 0.00 0 0"
|
||||
echo "189 187 412 67 1.80 2.45 1950 823 8.50 2 1 15.00 1 0"
|
||||
`, argsFile)
|
||||
if err := os.WriteFile(scriptPath, []byte(script), 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Set device selector via prefixed env var
|
||||
t.Setenv("BESZEL_AGENT_INTEL_GPU_DEVICE", "sriov")
|
||||
|
||||
gm := &GPUManager{GpuDataMap: make(map[string]*system.GPUData)}
|
||||
if err := gm.collectIntelStats(); err != nil {
|
||||
t.Fatalf("collectIntelStats error: %v", err)
|
||||
}
|
||||
|
||||
// Verify that -d sriov was passed
|
||||
data, err := os.ReadFile(argsFile)
|
||||
if err != nil {
|
||||
t.Fatalf("failed reading args file: %v", err)
|
||||
}
|
||||
argsStr := strings.TrimSpace(string(data))
|
||||
require.Contains(t, argsStr, "-d sriov")
|
||||
require.Contains(t, argsStr, "-s ")
|
||||
require.Contains(t, argsStr, "-l")
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ func NewHandlerRegistry() *HandlerRegistry {
|
||||
registry.Register(common.GetContainerLogs, &GetContainerLogsHandler{})
|
||||
registry.Register(common.GetContainerInfo, &GetContainerInfoHandler{})
|
||||
registry.Register(common.GetSmartData, &GetSmartDataHandler{})
|
||||
registry.Register(common.GetSystemdInfo, &GetSystemdInfoHandler{})
|
||||
|
||||
return registry
|
||||
}
|
||||
@@ -174,3 +175,31 @@ func (h *GetSmartDataHandler) Handle(hctx *HandlerContext) error {
|
||||
data := hctx.Agent.smartManager.GetCurrentData()
|
||||
return hctx.SendResponse(data, hctx.RequestID)
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// GetSystemdInfoHandler handles detailed systemd service info requests
|
||||
type GetSystemdInfoHandler struct{}
|
||||
|
||||
func (h *GetSystemdInfoHandler) Handle(hctx *HandlerContext) error {
|
||||
if hctx.Agent.systemdManager == nil {
|
||||
return errors.ErrUnsupported
|
||||
}
|
||||
|
||||
var req common.SystemdInfoRequest
|
||||
if err := cbor.Unmarshal(hctx.Request.Data, &req); err != nil {
|
||||
return err
|
||||
}
|
||||
if req.ServiceName == "" {
|
||||
return errors.New("service name is required")
|
||||
}
|
||||
|
||||
details, err := hctx.Agent.systemdManager.getServiceDetails(req.ServiceName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return hctx.SendResponse(details, hctx.RequestID)
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"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"
|
||||
@@ -173,6 +174,8 @@ func (a *Agent) handleSSHRequest(w io.Writer, req *common.HubRequest[cbor.RawMes
|
||||
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)
|
||||
}
|
||||
|
||||
111
agent/smart.go
111
agent/smart.go
@@ -1,3 +1,6 @@
|
||||
//go:generate -command fetchsmartctl go run ./tools/fetchsmartctl
|
||||
//go:generate fetchsmartctl -out ./smartmontools/smartctl.exe -url https://static.beszel.dev/bin/smartctl/smartctl-nc.exe -sha 3912249c3b329249aa512ce796fd1b64d7cbd8378b68ad2756b39163d9c30b47
|
||||
|
||||
package agent
|
||||
|
||||
import (
|
||||
@@ -5,7 +8,9 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -19,10 +24,12 @@ import (
|
||||
// SmartManager manages data collection for SMART devices
|
||||
type SmartManager struct {
|
||||
sync.Mutex
|
||||
SmartDataMap map[string]*smart.SmartData
|
||||
SmartDevices []*DeviceInfo
|
||||
refreshMutex sync.Mutex
|
||||
lastScanTime time.Time
|
||||
SmartDataMap map[string]*smart.SmartData
|
||||
SmartDevices []*DeviceInfo
|
||||
refreshMutex sync.Mutex
|
||||
lastScanTime time.Time
|
||||
binPath string
|
||||
excludedDevices map[string]struct{}
|
||||
}
|
||||
|
||||
type scanOutput struct {
|
||||
@@ -160,7 +167,7 @@ func (sm *SmartManager) ScanDevices(force bool) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
cmd := exec.CommandContext(ctx, "smartctl", "--scan", "-j")
|
||||
cmd := exec.CommandContext(ctx, sm.binPath, "--scan", "-j")
|
||||
output, err := cmd.Output()
|
||||
|
||||
var (
|
||||
@@ -179,6 +186,7 @@ func (sm *SmartManager) ScanDevices(force bool) error {
|
||||
}
|
||||
|
||||
finalDevices := mergeDeviceLists(currentDevices, scannedDevices, configuredDevices)
|
||||
finalDevices = sm.filterExcludedDevices(finalDevices)
|
||||
sm.updateSmartDevices(finalDevices)
|
||||
|
||||
if len(finalDevices) == 0 {
|
||||
@@ -226,6 +234,47 @@ func (sm *SmartManager) parseConfiguredDevices(config string) ([]*DeviceInfo, er
|
||||
return devices, nil
|
||||
}
|
||||
|
||||
func (sm *SmartManager) refreshExcludedDevices() {
|
||||
rawValue, _ := GetEnv("EXCLUDE_SMART")
|
||||
sm.excludedDevices = make(map[string]struct{})
|
||||
|
||||
for entry := range strings.SplitSeq(rawValue, ",") {
|
||||
device := strings.TrimSpace(entry)
|
||||
if device == "" {
|
||||
continue
|
||||
}
|
||||
sm.excludedDevices[device] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
func (sm *SmartManager) isExcludedDevice(deviceName string) bool {
|
||||
_, exists := sm.excludedDevices[deviceName]
|
||||
return exists
|
||||
}
|
||||
|
||||
func (sm *SmartManager) filterExcludedDevices(devices []*DeviceInfo) []*DeviceInfo {
|
||||
if devices == nil {
|
||||
return []*DeviceInfo{}
|
||||
}
|
||||
|
||||
excluded := sm.excludedDevices
|
||||
if len(excluded) == 0 {
|
||||
return devices
|
||||
}
|
||||
|
||||
filtered := make([]*DeviceInfo, 0, len(devices))
|
||||
for _, device := range devices {
|
||||
if device == nil || device.Name == "" {
|
||||
continue
|
||||
}
|
||||
if _, skip := excluded[device.Name]; skip {
|
||||
continue
|
||||
}
|
||||
filtered = append(filtered, device)
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
// detectSmartOutputType inspects sections that are unique to each smartctl
|
||||
// JSON schema (NVMe, ATA/SATA, SCSI) to determine which parser should be used
|
||||
// when the reported device type is ambiguous or missing.
|
||||
@@ -372,6 +421,10 @@ func (sm *SmartManager) parseSmartOutput(deviceInfo *DeviceInfo, output []byte)
|
||||
// Uses -n standby to avoid waking up sleeping disks, but bypasses standby mode
|
||||
// for initial data collection when no cached data exists
|
||||
func (sm *SmartManager) CollectSmart(deviceInfo *DeviceInfo) error {
|
||||
if deviceInfo != nil && sm.isExcludedDevice(deviceInfo.Name) {
|
||||
return errNoValidSmartData
|
||||
}
|
||||
|
||||
// slog.Info("collecting SMART data", "device", deviceInfo.Name, "type", deviceInfo.Type, "has_existing_data", sm.hasDataForDevice(deviceInfo.Name))
|
||||
|
||||
// Check if we have any existing data for this device
|
||||
@@ -382,7 +435,7 @@ func (sm *SmartManager) CollectSmart(deviceInfo *DeviceInfo) error {
|
||||
|
||||
// Try with -n standby first if we have existing data
|
||||
args := sm.smartctlArgs(deviceInfo, true)
|
||||
cmd := exec.CommandContext(ctx, "smartctl", args...)
|
||||
cmd := exec.CommandContext(ctx, sm.binPath, args...)
|
||||
output, err := cmd.CombinedOutput()
|
||||
|
||||
// Check if device is in standby (exit status 2)
|
||||
@@ -395,7 +448,7 @@ func (sm *SmartManager) CollectSmart(deviceInfo *DeviceInfo) error {
|
||||
ctx2, cancel2 := context.WithTimeout(context.Background(), 2*time.Second)
|
||||
defer cancel2()
|
||||
args = sm.smartctlArgs(deviceInfo, false)
|
||||
cmd = exec.CommandContext(ctx2, "smartctl", args...)
|
||||
cmd = exec.CommandContext(ctx2, sm.binPath, args...)
|
||||
output, err = cmd.CombinedOutput()
|
||||
}
|
||||
|
||||
@@ -403,10 +456,10 @@ func (sm *SmartManager) CollectSmart(deviceInfo *DeviceInfo) error {
|
||||
|
||||
if !hasValidData {
|
||||
if err != nil {
|
||||
slog.Debug("smartctl failed", "device", deviceInfo.Name, "err", err)
|
||||
slog.Info("smartctl failed", "device", deviceInfo.Name, "err", err)
|
||||
return err
|
||||
}
|
||||
slog.Debug("no valid SMART data found", "device", deviceInfo.Name)
|
||||
slog.Info("no valid SMART data found", "device", deviceInfo.Name)
|
||||
return errNoValidSmartData
|
||||
}
|
||||
|
||||
@@ -875,13 +928,33 @@ func (sm *SmartManager) parseSmartForNvme(output []byte) (bool, int) {
|
||||
}
|
||||
|
||||
// detectSmartctl checks if smartctl is installed, returns an error if not
|
||||
func (sm *SmartManager) detectSmartctl() error {
|
||||
if _, err := exec.LookPath("smartctl"); err == nil {
|
||||
slog.Debug("smartctl found")
|
||||
return nil
|
||||
func (sm *SmartManager) detectSmartctl() (string, error) {
|
||||
isWindows := runtime.GOOS == "windows"
|
||||
|
||||
// Load embedded smartctl.exe for Windows amd64 builds.
|
||||
if isWindows && runtime.GOARCH == "amd64" {
|
||||
if path, err := ensureEmbeddedSmartctl(); err == nil {
|
||||
return path, nil
|
||||
}
|
||||
}
|
||||
slog.Debug("smartctl not found")
|
||||
return errors.New("smartctl not found")
|
||||
|
||||
if path, err := exec.LookPath("smartctl"); err == nil {
|
||||
return path, nil
|
||||
}
|
||||
locations := []string{}
|
||||
if isWindows {
|
||||
locations = append(locations,
|
||||
"C:\\Program Files\\smartmontools\\bin\\smartctl.exe",
|
||||
)
|
||||
} else {
|
||||
locations = append(locations, "/opt/homebrew/bin/smartctl")
|
||||
}
|
||||
for _, location := range locations {
|
||||
if _, err := os.Stat(location); err == nil {
|
||||
return location, nil
|
||||
}
|
||||
}
|
||||
return "", errors.New("smartctl not found")
|
||||
}
|
||||
|
||||
// NewSmartManager creates and initializes a new SmartManager
|
||||
@@ -889,9 +962,13 @@ func NewSmartManager() (*SmartManager, error) {
|
||||
sm := &SmartManager{
|
||||
SmartDataMap: make(map[string]*smart.SmartData),
|
||||
}
|
||||
if err := sm.detectSmartctl(); err != nil {
|
||||
sm.refreshExcludedDevices()
|
||||
path, err := sm.detectSmartctl()
|
||||
if err != nil {
|
||||
slog.Debug(err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
slog.Debug("smartctl", "path", path)
|
||||
sm.binPath = path
|
||||
return sm, nil
|
||||
}
|
||||
|
||||
9
agent/smart_nonwindows.go
Normal file
9
agent/smart_nonwindows.go
Normal file
@@ -0,0 +1,9 @@
|
||||
//go:build !windows
|
||||
|
||||
package agent
|
||||
|
||||
import "errors"
|
||||
|
||||
func ensureEmbeddedSmartctl() (string, error) {
|
||||
return "", errors.ErrUnsupported
|
||||
}
|
||||
@@ -588,3 +588,195 @@ func TestIsVirtualDeviceScsi(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRefreshExcludedDevices(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
envValue string
|
||||
expectedDevs map[string]struct{}
|
||||
}{
|
||||
{
|
||||
name: "empty env",
|
||||
envValue: "",
|
||||
expectedDevs: map[string]struct{}{},
|
||||
},
|
||||
{
|
||||
name: "single device",
|
||||
envValue: "/dev/sda",
|
||||
expectedDevs: map[string]struct{}{
|
||||
"/dev/sda": {},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multiple devices",
|
||||
envValue: "/dev/sda,/dev/sdb,/dev/nvme0",
|
||||
expectedDevs: map[string]struct{}{
|
||||
"/dev/sda": {},
|
||||
"/dev/sdb": {},
|
||||
"/dev/nvme0": {},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "devices with whitespace",
|
||||
envValue: " /dev/sda , /dev/sdb , /dev/nvme0 ",
|
||||
expectedDevs: map[string]struct{}{
|
||||
"/dev/sda": {},
|
||||
"/dev/sdb": {},
|
||||
"/dev/nvme0": {},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "duplicate devices",
|
||||
envValue: "/dev/sda,/dev/sdb,/dev/sda",
|
||||
expectedDevs: map[string]struct{}{
|
||||
"/dev/sda": {},
|
||||
"/dev/sdb": {},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty entries and whitespace",
|
||||
envValue: "/dev/sda,, /dev/sdb , , ",
|
||||
expectedDevs: map[string]struct{}{
|
||||
"/dev/sda": {},
|
||||
"/dev/sdb": {},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.envValue != "" {
|
||||
t.Setenv("EXCLUDE_SMART", tt.envValue)
|
||||
} else {
|
||||
// Ensure env var is not set for empty test
|
||||
os.Unsetenv("EXCLUDE_SMART")
|
||||
}
|
||||
|
||||
sm := &SmartManager{}
|
||||
sm.refreshExcludedDevices()
|
||||
|
||||
assert.Equal(t, tt.expectedDevs, sm.excludedDevices)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsExcludedDevice(t *testing.T) {
|
||||
sm := &SmartManager{
|
||||
excludedDevices: map[string]struct{}{
|
||||
"/dev/sda": {},
|
||||
"/dev/nvme0": {},
|
||||
},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
deviceName string
|
||||
expectedBool bool
|
||||
}{
|
||||
{"excluded device sda", "/dev/sda", true},
|
||||
{"excluded device nvme0", "/dev/nvme0", true},
|
||||
{"non-excluded device sdb", "/dev/sdb", false},
|
||||
{"non-excluded device nvme1", "/dev/nvme1", false},
|
||||
{"empty device name", "", false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := sm.isExcludedDevice(tt.deviceName)
|
||||
assert.Equal(t, tt.expectedBool, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterExcludedDevices(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
excludedDevs map[string]struct{}
|
||||
inputDevices []*DeviceInfo
|
||||
expectedDevs []*DeviceInfo
|
||||
expectedLength int
|
||||
}{
|
||||
{
|
||||
name: "no exclusions",
|
||||
excludedDevs: map[string]struct{}{},
|
||||
inputDevices: []*DeviceInfo{
|
||||
{Name: "/dev/sda"},
|
||||
{Name: "/dev/sdb"},
|
||||
{Name: "/dev/nvme0"},
|
||||
},
|
||||
expectedDevs: []*DeviceInfo{
|
||||
{Name: "/dev/sda"},
|
||||
{Name: "/dev/sdb"},
|
||||
{Name: "/dev/nvme0"},
|
||||
},
|
||||
expectedLength: 3,
|
||||
},
|
||||
{
|
||||
name: "some devices excluded",
|
||||
excludedDevs: map[string]struct{}{
|
||||
"/dev/sda": {},
|
||||
"/dev/nvme0": {},
|
||||
},
|
||||
inputDevices: []*DeviceInfo{
|
||||
{Name: "/dev/sda"},
|
||||
{Name: "/dev/sdb"},
|
||||
{Name: "/dev/nvme0"},
|
||||
{Name: "/dev/nvme1"},
|
||||
},
|
||||
expectedDevs: []*DeviceInfo{
|
||||
{Name: "/dev/sdb"},
|
||||
{Name: "/dev/nvme1"},
|
||||
},
|
||||
expectedLength: 2,
|
||||
},
|
||||
{
|
||||
name: "all devices excluded",
|
||||
excludedDevs: map[string]struct{}{
|
||||
"/dev/sda": {},
|
||||
"/dev/sdb": {},
|
||||
},
|
||||
inputDevices: []*DeviceInfo{
|
||||
{Name: "/dev/sda"},
|
||||
{Name: "/dev/sdb"},
|
||||
},
|
||||
expectedDevs: []*DeviceInfo{},
|
||||
expectedLength: 0,
|
||||
},
|
||||
{
|
||||
name: "nil devices",
|
||||
excludedDevs: map[string]struct{}{},
|
||||
inputDevices: nil,
|
||||
expectedDevs: []*DeviceInfo{},
|
||||
expectedLength: 0,
|
||||
},
|
||||
{
|
||||
name: "filter nil and empty name devices",
|
||||
excludedDevs: map[string]struct{}{
|
||||
"/dev/sda": {},
|
||||
},
|
||||
inputDevices: []*DeviceInfo{
|
||||
{Name: "/dev/sda"},
|
||||
nil,
|
||||
{Name: ""},
|
||||
{Name: "/dev/sdb"},
|
||||
},
|
||||
expectedDevs: []*DeviceInfo{
|
||||
{Name: "/dev/sdb"},
|
||||
},
|
||||
expectedLength: 1,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
sm := &SmartManager{
|
||||
excludedDevices: tt.excludedDevs,
|
||||
}
|
||||
|
||||
result := sm.filterExcludedDevices(tt.inputDevices)
|
||||
|
||||
assert.Len(t, result, tt.expectedLength)
|
||||
assert.Equal(t, tt.expectedDevs, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
40
agent/smart_windows.go
Normal file
40
agent/smart_windows.go
Normal file
@@ -0,0 +1,40 @@
|
||||
//go:build windows
|
||||
|
||||
package agent
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
)
|
||||
|
||||
//go:embed smartmontools/smartctl.exe
|
||||
var embeddedSmartctl []byte
|
||||
|
||||
var (
|
||||
smartctlOnce sync.Once
|
||||
smartctlPath string
|
||||
smartctlErr error
|
||||
)
|
||||
|
||||
func ensureEmbeddedSmartctl() (string, error) {
|
||||
smartctlOnce.Do(func() {
|
||||
destDir := filepath.Join(os.TempDir(), "beszel", "smartmontools")
|
||||
if err := os.MkdirAll(destDir, 0o755); err != nil {
|
||||
smartctlErr = fmt.Errorf("failed to create smartctl directory: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
destPath := filepath.Join(destDir, "smartctl.exe")
|
||||
if err := os.WriteFile(destPath, embeddedSmartctl, 0o755); err != nil {
|
||||
smartctlErr = fmt.Errorf("failed to write embedded smartctl: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
smartctlPath = destPath
|
||||
})
|
||||
|
||||
return smartctlPath, smartctlErr
|
||||
}
|
||||
@@ -83,12 +83,24 @@ func (a *Agent) getSystemStats(cacheTimeMs uint16) system.Stats {
|
||||
systemStats.Battery[1] = batteryState
|
||||
}
|
||||
|
||||
// cpu percent
|
||||
cpuPercent, err := getCpuPercent(cacheTimeMs)
|
||||
// cpu metrics
|
||||
cpuMetrics, err := getCpuMetrics(cacheTimeMs)
|
||||
if err == nil {
|
||||
systemStats.Cpu = twoDecimals(cpuPercent)
|
||||
systemStats.Cpu = twoDecimals(cpuMetrics.Total)
|
||||
systemStats.CpuBreakdown = []float64{
|
||||
twoDecimals(cpuMetrics.User),
|
||||
twoDecimals(cpuMetrics.System),
|
||||
twoDecimals(cpuMetrics.Iowait),
|
||||
twoDecimals(cpuMetrics.Steal),
|
||||
twoDecimals(cpuMetrics.Idle),
|
||||
}
|
||||
} else {
|
||||
slog.Error("Error getting cpu percent", "err", err)
|
||||
slog.Error("Error getting cpu metrics", "err", err)
|
||||
}
|
||||
|
||||
// per-core cpu usage
|
||||
if perCoreUsage, err := getPerCoreCpuUsage(cacheTimeMs); err == nil {
|
||||
systemStats.CpuCoresUsage = perCoreUsage
|
||||
}
|
||||
|
||||
// load average
|
||||
|
||||
273
agent/systemd.go
Normal file
273
agent/systemd.go
Normal file
@@ -0,0 +1,273 @@
|
||||
//go:build linux
|
||||
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"log/slog"
|
||||
"maps"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/go-systemd/v22/dbus"
|
||||
"github.com/henrygd/beszel/internal/entities/systemd"
|
||||
)
|
||||
|
||||
var errNoActiveTime = errors.New("no active time")
|
||||
|
||||
// systemdManager manages the collection of systemd service statistics.
|
||||
type systemdManager struct {
|
||||
sync.Mutex
|
||||
serviceStatsMap map[string]*systemd.Service
|
||||
isRunning bool
|
||||
hasFreshStats bool
|
||||
patterns []string
|
||||
}
|
||||
|
||||
// newSystemdManager creates a new systemdManager.
|
||||
func newSystemdManager() (*systemdManager, error) {
|
||||
if skipSystemd, _ := GetEnv("SKIP_SYSTEMD"); skipSystemd == "true" {
|
||||
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")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
manager := &systemdManager{
|
||||
serviceStatsMap: make(map[string]*systemd.Service),
|
||||
patterns: getServicePatterns(),
|
||||
}
|
||||
|
||||
manager.startWorker(conn)
|
||||
|
||||
return manager, nil
|
||||
}
|
||||
|
||||
func (sm *systemdManager) startWorker(conn *dbus.Conn) {
|
||||
if sm.isRunning {
|
||||
return
|
||||
}
|
||||
sm.isRunning = true
|
||||
// prime the service stats map with the current services
|
||||
_ = sm.getServiceStats(conn, true)
|
||||
// update the services every 10 minutes
|
||||
go func() {
|
||||
for {
|
||||
time.Sleep(time.Minute * 10)
|
||||
_ = sm.getServiceStats(nil, true)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// getServiceStatsCount returns the number of systemd services.
|
||||
func (sm *systemdManager) getServiceStatsCount() int {
|
||||
return len(sm.serviceStatsMap)
|
||||
}
|
||||
|
||||
// getFailedServiceCount returns the number of systemd services in a failed state.
|
||||
func (sm *systemdManager) getFailedServiceCount() uint16 {
|
||||
sm.Lock()
|
||||
defer sm.Unlock()
|
||||
count := uint16(0)
|
||||
for _, service := range sm.serviceStatsMap {
|
||||
if service.State == systemd.StatusFailed {
|
||||
count++
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
// getServiceStats collects statistics for all running systemd services.
|
||||
func (sm *systemdManager) getServiceStats(conn *dbus.Conn, refresh bool) []*systemd.Service {
|
||||
// start := time.Now()
|
||||
// defer func() {
|
||||
// slog.Info("systemdManager.getServiceStats", "duration", time.Since(start))
|
||||
// }()
|
||||
|
||||
var services []*systemd.Service
|
||||
var err error
|
||||
|
||||
if !refresh {
|
||||
// return nil
|
||||
sm.Lock()
|
||||
defer sm.Unlock()
|
||||
for _, service := range sm.serviceStatsMap {
|
||||
services = append(services, service)
|
||||
}
|
||||
sm.hasFreshStats = false
|
||||
return services
|
||||
}
|
||||
|
||||
if conn == nil || !conn.Connected() {
|
||||
conn, err = dbus.NewSystemConnectionContext(context.Background())
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
defer conn.Close()
|
||||
}
|
||||
|
||||
units, err := conn.ListUnitsByPatternsContext(context.Background(), []string{"loaded"}, sm.patterns)
|
||||
if err != nil {
|
||||
slog.Error("Error listing systemd service units", "err", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, unit := range units {
|
||||
service, err := sm.updateServiceStats(conn, unit)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
services = append(services, service)
|
||||
}
|
||||
sm.hasFreshStats = true
|
||||
return services
|
||||
}
|
||||
|
||||
// updateServiceStats updates the statistics for a single systemd service.
|
||||
func (sm *systemdManager) updateServiceStats(conn *dbus.Conn, unit dbus.UnitStatus) (*systemd.Service, error) {
|
||||
sm.Lock()
|
||||
defer sm.Unlock()
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// if service has never been active (no active since time), skip it
|
||||
if activeEnterTsProp, err := conn.GetUnitTypePropertyContext(ctx, unit.Name, "Unit", "ActiveEnterTimestamp"); err == nil {
|
||||
if ts, ok := activeEnterTsProp.Value.Value().(uint64); !ok || ts == 0 || ts == math.MaxUint64 {
|
||||
return nil, errNoActiveTime
|
||||
}
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
service, serviceExists := sm.serviceStatsMap[unit.Name]
|
||||
if !serviceExists {
|
||||
service = &systemd.Service{Name: unescapeServiceName(strings.TrimSuffix(unit.Name, ".service"))}
|
||||
sm.serviceStatsMap[unit.Name] = service
|
||||
}
|
||||
|
||||
memPeak := service.MemPeak
|
||||
if memPeakProp, err := conn.GetUnitTypePropertyContext(ctx, unit.Name, "Service", "MemoryPeak"); err == nil {
|
||||
// If memPeak is MaxUint64 the api is saying it's not available
|
||||
if v, ok := memPeakProp.Value.Value().(uint64); ok && v != math.MaxUint64 {
|
||||
memPeak = v
|
||||
}
|
||||
}
|
||||
|
||||
var memUsage uint64
|
||||
if memProp, err := conn.GetUnitTypePropertyContext(ctx, unit.Name, "Service", "MemoryCurrent"); err == nil {
|
||||
// If memUsage is MaxUint64 the api is saying it's not available
|
||||
if v, ok := memProp.Value.Value().(uint64); ok && v != math.MaxUint64 {
|
||||
memUsage = v
|
||||
}
|
||||
}
|
||||
|
||||
service.State = systemd.ParseServiceStatus(unit.ActiveState)
|
||||
service.Sub = systemd.ParseServiceSubState(unit.SubState)
|
||||
|
||||
// some systems always return 0 for mem peak, so we should update the peak if the current usage is greater
|
||||
if memUsage > memPeak {
|
||||
memPeak = memUsage
|
||||
}
|
||||
|
||||
var cpuUsage uint64
|
||||
if cpuProp, err := conn.GetUnitTypePropertyContext(ctx, unit.Name, "Service", "CPUUsageNSec"); err == nil {
|
||||
if v, ok := cpuProp.Value.Value().(uint64); ok {
|
||||
cpuUsage = v
|
||||
}
|
||||
}
|
||||
|
||||
service.Mem = memUsage
|
||||
if memPeak > service.MemPeak {
|
||||
service.MemPeak = memPeak
|
||||
}
|
||||
service.UpdateCPUPercent(cpuUsage)
|
||||
|
||||
return service, nil
|
||||
}
|
||||
|
||||
// getServiceDetails collects extended information for a specific systemd service.
|
||||
func (sm *systemdManager) getServiceDetails(serviceName string) (systemd.ServiceDetails, error) {
|
||||
conn, err := dbus.NewSystemConnectionContext(context.Background())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
unitName := serviceName
|
||||
if !strings.HasSuffix(unitName, ".service") {
|
||||
unitName += ".service"
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
props, err := conn.GetUnitPropertiesContext(ctx, unitName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Start with all unit properties
|
||||
details := make(systemd.ServiceDetails)
|
||||
maps.Copy(details, props)
|
||||
|
||||
// // Add service-specific properties
|
||||
servicePropNames := []string{
|
||||
"MainPID", "ExecMainPID", "TasksCurrent", "TasksMax",
|
||||
"MemoryCurrent", "MemoryPeak", "MemoryLimit", "CPUUsageNSec",
|
||||
"NRestarts", "ExecMainStartTimestampRealtime", "Result",
|
||||
}
|
||||
|
||||
for _, propName := range servicePropNames {
|
||||
if variant, err := conn.GetUnitTypePropertyContext(ctx, unitName, "Service", propName); err == nil {
|
||||
value := variant.Value.Value()
|
||||
// Check if the value is MaxUint64, which indicates unlimited/infinite
|
||||
if uint64Value, ok := value.(uint64); ok && uint64Value == math.MaxUint64 {
|
||||
// Set to nil to indicate unlimited - frontend will handle this appropriately
|
||||
details[propName] = nil
|
||||
} else {
|
||||
details[propName] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return details, nil
|
||||
}
|
||||
|
||||
// unescapeServiceName unescapes systemd service names that contain C-style escape sequences like \x2d
|
||||
func unescapeServiceName(name string) string {
|
||||
if !strings.Contains(name, "\\x") {
|
||||
return name
|
||||
}
|
||||
unescaped, err := strconv.Unquote("\"" + name + "\"")
|
||||
if err != nil {
|
||||
return name
|
||||
}
|
||||
return unescaped
|
||||
}
|
||||
|
||||
// getServicePatterns returns the list of service patterns to match.
|
||||
// It reads from the SERVICE_PATTERNS environment variable if set,
|
||||
// otherwise defaults to "*service".
|
||||
func getServicePatterns() []string {
|
||||
patterns := []string{}
|
||||
if envPatterns, _ := GetEnv("SERVICE_PATTERNS"); envPatterns != "" {
|
||||
for pattern := range strings.SplitSeq(envPatterns, ",") {
|
||||
pattern = strings.TrimSpace(pattern)
|
||||
if pattern == "" {
|
||||
continue
|
||||
}
|
||||
if !strings.HasSuffix(pattern, ".service") {
|
||||
pattern += ".service"
|
||||
}
|
||||
patterns = append(patterns, pattern)
|
||||
}
|
||||
}
|
||||
if len(patterns) == 0 {
|
||||
patterns = []string{"*.service"}
|
||||
}
|
||||
return patterns
|
||||
}
|
||||
38
agent/systemd_nonlinux.go
Normal file
38
agent/systemd_nonlinux.go
Normal file
@@ -0,0 +1,38 @@
|
||||
//go:build !linux
|
||||
|
||||
package agent
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/henrygd/beszel/internal/entities/systemd"
|
||||
)
|
||||
|
||||
// systemdManager manages the collection of systemd service statistics.
|
||||
type systemdManager struct {
|
||||
hasFreshStats bool
|
||||
}
|
||||
|
||||
// newSystemdManager creates a new systemdManager.
|
||||
func newSystemdManager() (*systemdManager, error) {
|
||||
return &systemdManager{}, nil
|
||||
}
|
||||
|
||||
// getServiceStats returns nil for non-linux systems.
|
||||
func (sm *systemdManager) getServiceStats(conn any, refresh bool) []*systemd.Service {
|
||||
return nil
|
||||
}
|
||||
|
||||
// getServiceStatsCount returns 0 for non-linux systems.
|
||||
func (sm *systemdManager) getServiceStatsCount() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
// getFailedServiceCount returns 0 for non-linux systems.
|
||||
func (sm *systemdManager) getFailedServiceCount() uint16 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (sm *systemdManager) getServiceDetails(string) (systemd.ServiceDetails, error) {
|
||||
return nil, errors.New("systemd manager unavailable")
|
||||
}
|
||||
53
agent/systemd_nonlinux_test.go
Normal file
53
agent/systemd_nonlinux_test.go
Normal file
@@ -0,0 +1,53 @@
|
||||
//go:build !linux && testing
|
||||
|
||||
package agent
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewSystemdManager(t *testing.T) {
|
||||
manager, err := newSystemdManager()
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, manager)
|
||||
}
|
||||
|
||||
func TestSystemdManagerGetServiceStats(t *testing.T) {
|
||||
manager, err := newSystemdManager()
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Test with refresh = true
|
||||
result := manager.getServiceStats(true)
|
||||
assert.Nil(t, result)
|
||||
|
||||
// Test with refresh = false
|
||||
result = manager.getServiceStats(false)
|
||||
assert.Nil(t, result)
|
||||
}
|
||||
|
||||
func TestSystemdManagerGetServiceDetails(t *testing.T) {
|
||||
manager, err := newSystemdManager()
|
||||
assert.NoError(t, err)
|
||||
|
||||
result, err := manager.getServiceDetails("any-service")
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, "systemd manager unavailable", err.Error())
|
||||
assert.Nil(t, result)
|
||||
|
||||
// Test with empty service name
|
||||
result, err = manager.getServiceDetails("")
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, "systemd manager unavailable", err.Error())
|
||||
assert.Nil(t, result)
|
||||
}
|
||||
|
||||
func TestSystemdManagerFields(t *testing.T) {
|
||||
manager, err := newSystemdManager()
|
||||
assert.NoError(t, err)
|
||||
|
||||
// The non-linux manager should be a simple struct with no special fields
|
||||
// We can't test private fields directly, but we can test the methods work
|
||||
assert.NotNil(t, manager)
|
||||
}
|
||||
158
agent/systemd_test.go
Normal file
158
agent/systemd_test.go
Normal file
@@ -0,0 +1,158 @@
|
||||
//go:build linux && testing
|
||||
|
||||
package agent
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestUnescapeServiceName(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{"nginx.service", "nginx.service"}, // No escaping needed
|
||||
{"test\\x2dwith\\x2ddashes.service", "test-with-dashes.service"}, // \x2d is dash
|
||||
{"service\\x20with\\x20spaces.service", "service with spaces.service"}, // \x20 is space
|
||||
{"mixed\\x2dand\\x2dnormal", "mixed-and-normal"}, // Mixed escaped and normal
|
||||
{"no-escape-here", "no-escape-here"}, // No escape sequences
|
||||
{"", ""}, // Empty string
|
||||
{"\\x2d\\x2d", "--"}, // Multiple escapes
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.input, func(t *testing.T) {
|
||||
result := unescapeServiceName(test.input)
|
||||
assert.Equal(t, test.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnescapeServiceNameInvalid(t *testing.T) {
|
||||
// Test invalid escape sequences - should return original string
|
||||
invalidInputs := []string{
|
||||
"invalid\\x", // Incomplete escape
|
||||
"invalid\\xZZ", // Invalid hex
|
||||
"invalid\\x2", // Incomplete hex
|
||||
"invalid\\xyz", // Not a valid escape
|
||||
}
|
||||
|
||||
for _, input := range invalidInputs {
|
||||
t.Run(input, func(t *testing.T) {
|
||||
result := unescapeServiceName(input)
|
||||
assert.Equal(t, input, result, "Invalid escape sequences should return original string")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetServicePatterns(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
prefixedEnv string
|
||||
unprefixedEnv string
|
||||
expected []string
|
||||
cleanupEnvVars bool
|
||||
}{
|
||||
{
|
||||
name: "default when no env var set",
|
||||
prefixedEnv: "",
|
||||
unprefixedEnv: "",
|
||||
expected: []string{"*.service"},
|
||||
cleanupEnvVars: true,
|
||||
},
|
||||
{
|
||||
name: "single pattern with prefixed env",
|
||||
prefixedEnv: "nginx",
|
||||
unprefixedEnv: "",
|
||||
expected: []string{"nginx.service"},
|
||||
cleanupEnvVars: true,
|
||||
},
|
||||
{
|
||||
name: "single pattern with unprefixed env",
|
||||
prefixedEnv: "",
|
||||
unprefixedEnv: "nginx",
|
||||
expected: []string{"nginx.service"},
|
||||
cleanupEnvVars: true,
|
||||
},
|
||||
{
|
||||
name: "prefixed env takes precedence",
|
||||
prefixedEnv: "nginx",
|
||||
unprefixedEnv: "apache",
|
||||
expected: []string{"nginx.service"},
|
||||
cleanupEnvVars: true,
|
||||
},
|
||||
{
|
||||
name: "multiple patterns",
|
||||
prefixedEnv: "nginx,apache,postgresql",
|
||||
unprefixedEnv: "",
|
||||
expected: []string{"nginx.service", "apache.service", "postgresql.service"},
|
||||
cleanupEnvVars: true,
|
||||
},
|
||||
{
|
||||
name: "patterns with .service suffix",
|
||||
prefixedEnv: "nginx.service,apache.service",
|
||||
unprefixedEnv: "",
|
||||
expected: []string{"nginx.service", "apache.service"},
|
||||
cleanupEnvVars: true,
|
||||
},
|
||||
{
|
||||
name: "mixed patterns with and without suffix",
|
||||
prefixedEnv: "nginx.service,apache,postgresql.service",
|
||||
unprefixedEnv: "",
|
||||
expected: []string{"nginx.service", "apache.service", "postgresql.service"},
|
||||
cleanupEnvVars: true,
|
||||
},
|
||||
{
|
||||
name: "patterns with whitespace",
|
||||
prefixedEnv: " nginx , apache , postgresql ",
|
||||
unprefixedEnv: "",
|
||||
expected: []string{"nginx.service", "apache.service", "postgresql.service"},
|
||||
cleanupEnvVars: true,
|
||||
},
|
||||
{
|
||||
name: "empty patterns are skipped",
|
||||
prefixedEnv: "nginx,,apache, ,postgresql",
|
||||
unprefixedEnv: "",
|
||||
expected: []string{"nginx.service", "apache.service", "postgresql.service"},
|
||||
cleanupEnvVars: true,
|
||||
},
|
||||
{
|
||||
name: "wildcard pattern",
|
||||
prefixedEnv: "*nginx*,*apache*",
|
||||
unprefixedEnv: "",
|
||||
expected: []string{"*nginx*.service", "*apache*.service"},
|
||||
cleanupEnvVars: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// Clean up any existing env vars
|
||||
os.Unsetenv("BESZEL_AGENT_SERVICE_PATTERNS")
|
||||
os.Unsetenv("SERVICE_PATTERNS")
|
||||
|
||||
// Set up environment variables
|
||||
if tt.prefixedEnv != "" {
|
||||
os.Setenv("BESZEL_AGENT_SERVICE_PATTERNS", tt.prefixedEnv)
|
||||
}
|
||||
if tt.unprefixedEnv != "" {
|
||||
os.Setenv("SERVICE_PATTERNS", tt.unprefixedEnv)
|
||||
}
|
||||
|
||||
// Run the function
|
||||
result := getServicePatterns()
|
||||
|
||||
// Verify results
|
||||
assert.Equal(t, tt.expected, result, "Patterns should match expected values")
|
||||
|
||||
// Cleanup
|
||||
if tt.cleanupEnvVars {
|
||||
os.Unsetenv("BESZEL_AGENT_SERVICE_PATTERNS")
|
||||
os.Unsetenv("SERVICE_PATTERNS")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
130
agent/tools/fetchsmartctl/main.go
Normal file
130
agent/tools/fetchsmartctl/main.go
Normal file
@@ -0,0 +1,130 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"flag"
|
||||
"fmt"
|
||||
"hash"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Download smartctl.exe from the given URL and save it to the given destination.
|
||||
// This is used to embed smartctl.exe in the Windows build.
|
||||
|
||||
func main() {
|
||||
url := flag.String("url", "", "URL to download smartctl.exe from (required)")
|
||||
out := flag.String("out", "", "Destination path for smartctl.exe (required)")
|
||||
sha := flag.String("sha", "", "Optional SHA1/SHA256 checksum for integrity validation")
|
||||
force := flag.Bool("force", false, "Force re-download even if destination exists")
|
||||
flag.Parse()
|
||||
|
||||
if *url == "" || *out == "" {
|
||||
fatalf("-url and -out are required")
|
||||
}
|
||||
|
||||
if !*force {
|
||||
if info, err := os.Stat(*out); err == nil && info.Size() > 0 {
|
||||
fmt.Println("smartctl.exe already present, skipping download")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err := downloadFile(*url, *out, *sha); err != nil {
|
||||
fatalf("download failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func downloadFile(url, dest, shaHex string) error {
|
||||
// Prepare destination
|
||||
if err := os.MkdirAll(filepath.Dir(dest), 0o755); err != nil {
|
||||
return fmt.Errorf("create dir: %w", err)
|
||||
}
|
||||
|
||||
// HTTP client
|
||||
client := &http.Client{Timeout: 60 * time.Second}
|
||||
req, err := http.NewRequest(http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("new request: %w", err)
|
||||
}
|
||||
req.Header.Set("User-Agent", "beszel-fetchsmartctl/1.0")
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("http get: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
return fmt.Errorf("unexpected HTTP status: %s", resp.Status)
|
||||
}
|
||||
|
||||
tmp := dest + ".tmp"
|
||||
f, err := os.OpenFile(tmp, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o644)
|
||||
if err != nil {
|
||||
return fmt.Errorf("open tmp: %w", err)
|
||||
}
|
||||
|
||||
// Determine hash algorithm based on length (SHA1=40, SHA256=64)
|
||||
var hasher hash.Hash
|
||||
if shaHex := strings.TrimSpace(shaHex); shaHex != "" {
|
||||
cleanSha := strings.ToLower(strings.ReplaceAll(shaHex, " ", ""))
|
||||
switch len(cleanSha) {
|
||||
case 40:
|
||||
hasher = sha1.New()
|
||||
case 64:
|
||||
hasher = sha256.New()
|
||||
default:
|
||||
f.Close()
|
||||
os.Remove(tmp)
|
||||
return fmt.Errorf("unsupported hash length: %d (expected 40 for SHA1 or 64 for SHA256)", len(cleanSha))
|
||||
}
|
||||
}
|
||||
|
||||
var mw io.Writer = f
|
||||
if hasher != nil {
|
||||
mw = io.MultiWriter(f, hasher)
|
||||
}
|
||||
if _, err := io.Copy(mw, resp.Body); err != nil {
|
||||
f.Close()
|
||||
os.Remove(tmp)
|
||||
return fmt.Errorf("write tmp: %w", err)
|
||||
}
|
||||
if err := f.Close(); err != nil {
|
||||
os.Remove(tmp)
|
||||
return fmt.Errorf("close tmp: %w", err)
|
||||
}
|
||||
|
||||
if hasher != nil && shaHex != "" {
|
||||
cleanSha := strings.ToLower(strings.ReplaceAll(strings.TrimSpace(shaHex), " ", ""))
|
||||
got := strings.ToLower(hex.EncodeToString(hasher.Sum(nil)))
|
||||
if got != cleanSha {
|
||||
os.Remove(tmp)
|
||||
return fmt.Errorf("hash mismatch: got %s want %s", got, cleanSha)
|
||||
}
|
||||
}
|
||||
|
||||
// Make executable and move into place
|
||||
if err := os.Chmod(tmp, 0o755); err != nil {
|
||||
os.Remove(tmp)
|
||||
return fmt.Errorf("chmod: %w", err)
|
||||
}
|
||||
if err := os.Rename(tmp, dest); err != nil {
|
||||
os.Remove(tmp)
|
||||
return fmt.Errorf("rename: %w", err)
|
||||
}
|
||||
|
||||
fmt.Println("smartctl.exe downloaded to", dest)
|
||||
return nil
|
||||
}
|
||||
|
||||
func fatalf(format string, a ...any) {
|
||||
fmt.Fprintf(os.Stderr, format+"\n", a...)
|
||||
os.Exit(1)
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import "github.com/blang/semver"
|
||||
|
||||
const (
|
||||
// Version is the current version of the application.
|
||||
Version = "0.15.2"
|
||||
Version = "0.17.0"
|
||||
// AppName is the name of the application.
|
||||
AppName = "beszel"
|
||||
)
|
||||
|
||||
38
go.mod
38
go.mod
@@ -1,24 +1,25 @@
|
||||
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/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.11.0
|
||||
github.com/nicholas-fedor/shoutrrr v0.12.1
|
||||
github.com/pocketbase/dbx v1.11.0
|
||||
github.com/pocketbase/pocketbase v0.31.0
|
||||
github.com/shirou/gopsutil/v4 v4.25.9
|
||||
github.com/pocketbase/pocketbase v0.34.0
|
||||
github.com/shirou/gopsutil/v4 v4.25.10
|
||||
github.com/spf13/cast v1.10.0
|
||||
github.com/spf13/cobra v1.10.1
|
||||
github.com/spf13/pflag v1.0.10
|
||||
github.com/stretchr/testify v1.11.1
|
||||
golang.org/x/crypto v0.43.0
|
||||
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546
|
||||
golang.org/x/crypto v0.45.0
|
||||
golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
@@ -30,13 +31,14 @@ 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.0 // indirect
|
||||
github.com/ebitengine/purego v0.9.1 // indirect
|
||||
github.com/fatih/color v1.18.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.10 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.11 // 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/golang-jwt/jwt/v5 v5.3.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/klauspost/compress v1.18.1 // indirect
|
||||
@@ -47,20 +49,20 @@ require (
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.15 // indirect
|
||||
github.com/tklauser/numcpus v0.10.0 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.16 // indirect
|
||||
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.32.0 // indirect
|
||||
golang.org/x/net v0.46.0 // indirect
|
||||
golang.org/x/oauth2 v0.32.0 // indirect
|
||||
golang.org/x/sync v0.17.0 // indirect
|
||||
golang.org/x/sys v0.37.0 // indirect
|
||||
golang.org/x/term v0.36.0 // indirect
|
||||
golang.org/x/text v0.30.0 // 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
|
||||
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.39.1 // indirect
|
||||
modernc.org/sqlite v1.40.1 // indirect
|
||||
)
|
||||
|
||||
108
go.sum
108
go.sum
@@ -9,6 +9,8 @@ github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3d
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
||||
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
|
||||
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||
github.com/coreos/go-systemd/v22 v22.6.0 h1:aGVa/v8B7hpb0TKl0MWoAavPDmHvobFe5R5zn0bCJWo=
|
||||
github.com/coreos/go-systemd/v22 v22.6.0/go.mod h1:iG+pp635Fo7ZmV/j14KUcmEyWF+0X7Lua8rrTWzYgWU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
@@ -23,16 +25,16 @@ github.com/domodwyer/mailyak/v3 v3.6.2 h1:x3tGMsyFhTCaxp6ycgR0FE/bu5QiNp+hetUuCO
|
||||
github.com/domodwyer/mailyak/v3 v3.6.2/go.mod h1:lOm/u9CyCVWHeaAmHIdF4RiKVxKUT/H5XX10lIKAL6c=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/ebitengine/purego v0.9.0 h1:mh0zpKBIXDceC63hpvPuGLiJ8ZAa3DfrFTudmfi8A4k=
|
||||
github.com/ebitengine/purego v0.9.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
github.com/ebitengine/purego v0.9.1 h1:a/k2f2HQU3Pi399RPW1MOaZyhKJL9w/xFpKAg4q1s0A=
|
||||
github.com/ebitengine/purego v0.9.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
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.10 h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIpU+bX4+ZK0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.10/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
||||
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/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=
|
||||
@@ -49,15 +51,19 @@ 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/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/pprof v0.0.0-20251007162407-5df77e3f7d1d h1:KJIErDwbSHjnp/SGzE5ed8Aol7JsKiI5X7yWKAtzhM0=
|
||||
github.com/google/pprof v0.0.0-20251007162407-5df77e3f7d1d/go.mod h1:I6V7YzU0XDpsHqbsyrghnFZLO1gwK6NPTNvmetQIk9U=
|
||||
github.com/google/pprof v0.0.0-20251114195745-4902fdda35c8 h1:3DsUAV+VNEQa2CUVLxCY3f87278uWfIDhJnbdvDjvmE=
|
||||
github.com/google/pprof v0.0.0-20251114195745-4902fdda35c8/go.mod h1:I6V7YzU0XDpsHqbsyrghnFZLO1gwK6NPTNvmetQIk9U=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jarcoal/httpmock v1.4.1 h1:0Ju+VCFuARfFlhVXFc2HxlcQkfB+Xq12/EotHko+x2A=
|
||||
@@ -79,10 +85,10 @@ 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.11.0 h1:hAMv2uM8OfFXkMHVP977elkP3Wgw5/YpVX5GxXQwiWA=
|
||||
github.com/nicholas-fedor/shoutrrr v0.11.0/go.mod h1:0kRF9ral22xUn/0BlxfhLQUeJDTySCPsuNvaclyagb4=
|
||||
github.com/onsi/ginkgo/v2 v2.27.1 h1:0LJC8MpUSQnfnp4n/3W3GdlmJP3ENGF0ZPzjQGLPP7s=
|
||||
github.com/onsi/ginkgo/v2 v2.27.1/go.mod h1:wmy3vCqiBjirARfVhAqFpYt8uvX0yaFe+GudAqqcCqA=
|
||||
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/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
@@ -90,8 +96,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pocketbase/dbx v1.11.0 h1:LpZezioMfT3K4tLrqA55wWFw1EtH1pM4tzSVa7kgszU=
|
||||
github.com/pocketbase/dbx v1.11.0/go.mod h1:xXRCIAKTHMgUCyCKZm55pUOdvFziJjQfXaWKhu2vhMs=
|
||||
github.com/pocketbase/pocketbase v0.31.0 h1:JaOtSDytdA+a0r4689Mrjda4rmq+BaHgEJkPeOIydms=
|
||||
github.com/pocketbase/pocketbase v0.31.0/go.mod h1:p4a83n+DlBcTvvqhC7QDy0KDmQ2la2c6dgxdIBWwKiE=
|
||||
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/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=
|
||||
@@ -99,8 +105,8 @@ github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qq
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/shirou/gopsutil/v4 v4.25.9 h1:JImNpf6gCVhKgZhtaAHJ0serfFGtlfIlSC08eaKdTrU=
|
||||
github.com/shirou/gopsutil/v4 v4.25.9/go.mod h1:gxIxoC+7nQRwUl/xNhutXlD8lq+jxTgpIkEf3rADHL8=
|
||||
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/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=
|
||||
@@ -112,75 +118,77 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4=
|
||||
github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4=
|
||||
github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso=
|
||||
github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
|
||||
github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA=
|
||||
github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI=
|
||||
github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw=
|
||||
github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
|
||||
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
|
||||
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.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
|
||||
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
|
||||
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 h1:mgKeJMpvi0yx/sU5GsxQ7p6s2wtOnGAHZWCHUM4KGzY=
|
||||
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546/go.mod h1:j/pmGrbnkbPtQfxEe5D0VQhZC6qKbfKifgD0oM7sR70=
|
||||
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/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.32.0 h1:6lZQWq75h7L5IWNk0r+SCpUJ6tUVd3v4ZHnbRKLkUDQ=
|
||||
golang.org/x/image v0.32.0/go.mod h1:/R37rrQmKXtO6tYXAjtDLwQgFLHmhW+V6ayXlxzP2Pc=
|
||||
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
|
||||
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
|
||||
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/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4=
|
||||
golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
|
||||
golang.org/x/oauth2 v0.32.0 h1:jsCblLleRMDrxMN29H3z/k1KliIvpLgCkE6R8FXXNgY=
|
||||
golang.org/x/oauth2 v0.32.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
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/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.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
|
||||
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q=
|
||||
golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss=
|
||||
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/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.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
|
||||
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
|
||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
|
||||
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
|
||||
golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
|
||||
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
|
||||
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=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
howett.net/plist v1.0.1 h1:37GdZ8tP09Q35o9ych3ehygcsL+HqKSwzctveSlarvM=
|
||||
howett.net/plist v1.0.1/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
|
||||
modernc.org/cc/v4 v4.26.5 h1:xM3bX7Mve6G8K8b+T11ReenJOT+BmVqQj0FY5T4+5Y4=
|
||||
modernc.org/cc/v4 v4.26.5/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
||||
modernc.org/ccgo/v4 v4.28.1 h1:wPKYn5EC/mYTqBO373jKjvX2n+3+aK7+sICCv4Fjy1A=
|
||||
modernc.org/ccgo/v4 v4.28.1/go.mod h1:uD+4RnfrVgE6ec9NGguUNdhqzNIeeomeXf6CL0GTE5Q=
|
||||
modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis=
|
||||
modernc.org/cc/v4 v4.27.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
||||
modernc.org/ccgo/v4 v4.30.1 h1:4r4U1J6Fhj98NKfSjnPUN7Ze2c6MnAdL0hWw6+LrJpc=
|
||||
modernc.org/ccgo/v4 v4.30.1/go.mod h1:bIOeI1JL54Utlxn+LwrFyjCx2n2RDiYEaJVSrgdrRfM=
|
||||
modernc.org/fileutil v1.3.40 h1:ZGMswMNc9JOCrcrakF1HrvmergNLAmxOPjizirpfqBA=
|
||||
modernc.org/fileutil v1.3.40/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
|
||||
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
|
||||
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
|
||||
modernc.org/gc/v3 v3.1.1 h1:k8T3gkXWY9sEiytKhcgyiZ2L0DTyCQ/nvX+LoCljoRE=
|
||||
modernc.org/gc/v3 v3.1.1/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
|
||||
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
|
||||
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
|
||||
modernc.org/libc v1.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/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=
|
||||
@@ -189,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.39.1 h1:H+/wGFzuSCIEVCvXYVHX5RQglwhMOvtHSv+VtidL2r4=
|
||||
modernc.org/sqlite v1.39.1/go.mod h1:9fjQZ0mB1LLP0GYrp39oOJXx/I2sxEnZtzCmEQIKvGE=
|
||||
modernc.org/sqlite v1.40.1 h1:VfuXcxcUWWKRBuP8+BR9L7VnmusMgBNNnBYGEe9w/iY=
|
||||
modernc.org/sqlite v1.40.1/go.mod h1:9fjQZ0mB1LLP0GYrp39oOJXx/I2sxEnZtzCmEQIKvGE=
|
||||
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=
|
||||
|
||||
@@ -28,6 +28,7 @@ type AlertManager struct {
|
||||
|
||||
type AlertMessageData struct {
|
||||
UserID string
|
||||
SystemID string
|
||||
Title string
|
||||
Message string
|
||||
Link string
|
||||
@@ -40,13 +41,18 @@ type UserNotificationSettings struct {
|
||||
}
|
||||
|
||||
type SystemAlertStats struct {
|
||||
Cpu float64 `json:"cpu"`
|
||||
Mem float64 `json:"mp"`
|
||||
Disk float64 `json:"dp"`
|
||||
NetSent float64 `json:"ns"`
|
||||
NetRecv float64 `json:"nr"`
|
||||
Temperatures map[string]float32 `json:"t"`
|
||||
LoadAvg [3]float64 `json:"la"`
|
||||
Cpu float64 `json:"cpu"`
|
||||
Mem float64 `json:"mp"`
|
||||
Disk float64 `json:"dp"`
|
||||
NetSent float64 `json:"ns"`
|
||||
NetRecv float64 `json:"nr"`
|
||||
GPU map[string]SystemAlertGPUData `json:"g"`
|
||||
Temperatures map[string]float32 `json:"t"`
|
||||
LoadAvg [3]float64 `json:"la"`
|
||||
}
|
||||
|
||||
type SystemAlertGPUData struct {
|
||||
Usage float64 `json:"u"`
|
||||
}
|
||||
|
||||
type SystemAlertData struct {
|
||||
@@ -72,7 +78,6 @@ var supportsTitle = map[string]struct{}{
|
||||
"ifttt": {},
|
||||
"join": {},
|
||||
"lark": {},
|
||||
"matrix": {},
|
||||
"ntfy": {},
|
||||
"opsgenie": {},
|
||||
"pushbullet": {},
|
||||
@@ -99,10 +104,84 @@ func NewAlertManager(app hubLike) *AlertManager {
|
||||
func (am *AlertManager) bindEvents() {
|
||||
am.hub.OnRecordAfterUpdateSuccess("alerts").BindFunc(updateHistoryOnAlertUpdate)
|
||||
am.hub.OnRecordAfterDeleteSuccess("alerts").BindFunc(resolveHistoryOnAlertDelete)
|
||||
am.hub.OnRecordAfterUpdateSuccess("smart_devices").BindFunc(am.handleSmartDeviceAlert)
|
||||
}
|
||||
|
||||
// IsNotificationSilenced checks if a notification should be silenced based on configured quiet hours
|
||||
func (am *AlertManager) IsNotificationSilenced(userID, systemID string) bool {
|
||||
// Query for quiet hours windows that match this user and system
|
||||
// Include both global windows (system is null/empty) and system-specific windows
|
||||
var filter string
|
||||
var params dbx.Params
|
||||
|
||||
if systemID == "" {
|
||||
// If no systemID provided, only check global windows
|
||||
filter = "user={:user} AND system=''"
|
||||
params = dbx.Params{"user": userID}
|
||||
} else {
|
||||
// Check both global and system-specific windows
|
||||
filter = "user={:user} AND (system='' OR system={:system})"
|
||||
params = dbx.Params{
|
||||
"user": userID,
|
||||
"system": systemID,
|
||||
}
|
||||
}
|
||||
|
||||
quietHourWindows, err := am.hub.FindAllRecords("quiet_hours", dbx.NewExp(filter, params))
|
||||
if err != nil || len(quietHourWindows) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
now := time.Now().UTC()
|
||||
|
||||
for _, window := range quietHourWindows {
|
||||
windowType := window.GetString("type")
|
||||
start := window.GetDateTime("start").Time()
|
||||
end := window.GetDateTime("end").Time()
|
||||
|
||||
if windowType == "daily" {
|
||||
// For daily recurring windows, extract just the time portion and compare
|
||||
// The start/end are stored as full datetime but we only care about HH:MM
|
||||
startHour, startMin, _ := start.Clock()
|
||||
endHour, endMin, _ := end.Clock()
|
||||
nowHour, nowMin, _ := now.Clock()
|
||||
|
||||
// Convert to minutes since midnight for easier comparison
|
||||
startMinutes := startHour*60 + startMin
|
||||
endMinutes := endHour*60 + endMin
|
||||
nowMinutes := nowHour*60 + nowMin
|
||||
|
||||
// Handle case where window crosses midnight
|
||||
if endMinutes < startMinutes {
|
||||
// Window crosses midnight (e.g., 23:00 - 01:00)
|
||||
if nowMinutes >= startMinutes || nowMinutes < endMinutes {
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
// Normal case (e.g., 09:00 - 17:00)
|
||||
if nowMinutes >= startMinutes && nowMinutes < endMinutes {
|
||||
return true
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// One-time window: check if current time is within the date range
|
||||
if (now.After(start) || now.Equal(start)) && now.Before(end) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// SendAlert sends an alert to the user
|
||||
func (am *AlertManager) SendAlert(data AlertMessageData) error {
|
||||
// Check if alert is silenced
|
||||
if am.IsNotificationSilenced(data.UserID, data.SystemID) {
|
||||
am.hub.Logger().Info("Notification silenced", "user", data.UserID, "system", data.SystemID, "title", data.Title)
|
||||
return nil
|
||||
}
|
||||
|
||||
// get user settings
|
||||
record, err := am.hub.FindFirstRecordByFilter(
|
||||
"user_settings", "user={:user}",
|
||||
|
||||
426
internal/alerts/alerts_quiet_hours_test.go
Normal file
426
internal/alerts/alerts_quiet_hours_test.go
Normal file
@@ -0,0 +1,426 @@
|
||||
//go:build testing
|
||||
// +build testing
|
||||
|
||||
package alerts_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"testing/synctest"
|
||||
"time"
|
||||
|
||||
"github.com/henrygd/beszel/internal/alerts"
|
||||
beszelTests "github.com/henrygd/beszel/internal/tests"
|
||||
|
||||
"github.com/pocketbase/dbx"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestAlertSilencedOneTime(t *testing.T) {
|
||||
hub, user := beszelTests.GetHubWithUser(t)
|
||||
defer hub.Cleanup()
|
||||
|
||||
// Create a system
|
||||
systems, err := beszelTests.CreateSystems(hub, 1, user.Id, "up")
|
||||
assert.NoError(t, err)
|
||||
system := systems[0]
|
||||
|
||||
// Create an alert
|
||||
alert, err := beszelTests.CreateRecord(hub, "alerts", map[string]any{
|
||||
"name": "CPU",
|
||||
"system": system.Id,
|
||||
"user": user.Id,
|
||||
"value": 80,
|
||||
"min": 1,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Create a one-time quiet hours window (current time - 1 hour to current time + 1 hour)
|
||||
now := time.Now().UTC()
|
||||
startTime := now.Add(-1 * time.Hour)
|
||||
endTime := now.Add(1 * time.Hour)
|
||||
|
||||
_, err = beszelTests.CreateRecord(hub, "quiet_hours", map[string]any{
|
||||
"user": user.Id,
|
||||
"system": system.Id,
|
||||
"type": "one-time",
|
||||
"start": startTime,
|
||||
"end": endTime,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Get alert manager
|
||||
am := alerts.NewAlertManager(hub)
|
||||
defer am.StopWorker()
|
||||
|
||||
// Test that alert is silenced
|
||||
silenced := am.IsNotificationSilenced(user.Id, system.Id)
|
||||
assert.True(t, silenced, "Alert should be silenced during active one-time window")
|
||||
|
||||
// Create a window that has already ended
|
||||
pastStart := now.Add(-3 * time.Hour)
|
||||
pastEnd := now.Add(-2 * time.Hour)
|
||||
|
||||
_, err = beszelTests.CreateRecord(hub, "quiet_hours", map[string]any{
|
||||
"user": user.Id,
|
||||
"system": system.Id,
|
||||
"type": "one-time",
|
||||
"start": pastStart,
|
||||
"end": pastEnd,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Should still be silenced because of the first window
|
||||
silenced = am.IsNotificationSilenced(user.Id, system.Id)
|
||||
assert.True(t, silenced, "Alert should still be silenced (past window doesn't affect active window)")
|
||||
|
||||
// Clear all windows and create a future window
|
||||
_, err = hub.DB().NewQuery("DELETE FROM quiet_hours").Execute()
|
||||
assert.NoError(t, err)
|
||||
|
||||
futureStart := now.Add(2 * time.Hour)
|
||||
futureEnd := now.Add(3 * time.Hour)
|
||||
|
||||
_, err = beszelTests.CreateRecord(hub, "quiet_hours", map[string]any{
|
||||
"user": user.Id,
|
||||
"system": system.Id,
|
||||
"type": "one-time",
|
||||
"start": futureStart,
|
||||
"end": futureEnd,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Alert should NOT be silenced (window hasn't started yet)
|
||||
silenced = am.IsNotificationSilenced(user.Id, system.Id)
|
||||
assert.False(t, silenced, "Alert should not be silenced (window hasn't started)")
|
||||
|
||||
_ = alert
|
||||
}
|
||||
|
||||
func TestAlertSilencedDaily(t *testing.T) {
|
||||
hub, user := beszelTests.GetHubWithUser(t)
|
||||
defer hub.Cleanup()
|
||||
|
||||
// Create a system
|
||||
systems, err := beszelTests.CreateSystems(hub, 1, user.Id, "up")
|
||||
assert.NoError(t, err)
|
||||
system := systems[0]
|
||||
|
||||
// Get alert manager
|
||||
am := alerts.NewAlertManager(hub)
|
||||
defer am.StopWorker()
|
||||
|
||||
// Get current hour and create a window that includes current time
|
||||
now := time.Now().UTC()
|
||||
currentHour := now.Hour()
|
||||
currentMin := now.Minute()
|
||||
|
||||
// Create a window from 1 hour ago to 1 hour from now
|
||||
startHour := (currentHour - 1 + 24) % 24
|
||||
endHour := (currentHour + 1) % 24
|
||||
|
||||
// Create times with just the hours/minutes we want (date doesn't matter for daily)
|
||||
startTime := time.Date(2000, 1, 1, startHour, currentMin, 0, 0, time.UTC)
|
||||
endTime := time.Date(2000, 1, 1, endHour, currentMin, 0, 0, time.UTC)
|
||||
|
||||
_, err = beszelTests.CreateRecord(hub, "quiet_hours", map[string]any{
|
||||
"user": user.Id,
|
||||
"system": system.Id,
|
||||
"type": "daily",
|
||||
"start": startTime,
|
||||
"end": endTime,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Alert should be silenced (current time is within the daily window)
|
||||
silenced := am.IsNotificationSilenced(user.Id, system.Id)
|
||||
assert.True(t, silenced, "Alert should be silenced during active daily window")
|
||||
|
||||
// Clear windows and create one that doesn't include current time
|
||||
_, err = hub.DB().NewQuery("DELETE FROM quiet_hours").Execute()
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Create a window from 6-12 hours from now
|
||||
futureStartHour := (currentHour + 6) % 24
|
||||
futureEndHour := (currentHour + 12) % 24
|
||||
|
||||
startTime = time.Date(2000, 1, 1, futureStartHour, 0, 0, 0, time.UTC)
|
||||
endTime = time.Date(2000, 1, 1, futureEndHour, 0, 0, 0, time.UTC)
|
||||
|
||||
_, err = beszelTests.CreateRecord(hub, "quiet_hours", map[string]any{
|
||||
"user": user.Id,
|
||||
"system": system.Id,
|
||||
"type": "daily",
|
||||
"start": startTime,
|
||||
"end": endTime,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Alert should NOT be silenced
|
||||
silenced = am.IsNotificationSilenced(user.Id, system.Id)
|
||||
assert.False(t, silenced, "Alert should not be silenced (outside daily window)")
|
||||
}
|
||||
|
||||
func TestAlertSilencedDailyMidnightCrossing(t *testing.T) {
|
||||
hub, user := beszelTests.GetHubWithUser(t)
|
||||
defer hub.Cleanup()
|
||||
|
||||
// Create a system
|
||||
systems, err := beszelTests.CreateSystems(hub, 1, user.Id, "up")
|
||||
assert.NoError(t, err)
|
||||
system := systems[0]
|
||||
|
||||
// Get alert manager
|
||||
am := alerts.NewAlertManager(hub)
|
||||
defer am.StopWorker()
|
||||
|
||||
// Create a window that crosses midnight: 22:00 - 02:00
|
||||
startTime := time.Date(2000, 1, 1, 22, 0, 0, 0, time.UTC)
|
||||
endTime := time.Date(2000, 1, 1, 2, 0, 0, 0, time.UTC)
|
||||
|
||||
_, err = beszelTests.CreateRecord(hub, "quiet_hours", map[string]any{
|
||||
"user": user.Id,
|
||||
"system": system.Id,
|
||||
"type": "daily",
|
||||
"start": startTime,
|
||||
"end": endTime,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Test with a time at 23:00 (should be silenced)
|
||||
// We can't control the actual current time, but we can verify the logic
|
||||
// by checking if the window was created correctly
|
||||
windows, err := hub.FindAllRecords("quiet_hours", dbx.HashExp{
|
||||
"user": user.Id,
|
||||
"system": system.Id,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, windows, 1, "Should have created 1 window")
|
||||
|
||||
window := windows[0]
|
||||
assert.Equal(t, "daily", window.GetString("type"))
|
||||
assert.Equal(t, 22, window.GetDateTime("start").Time().Hour())
|
||||
assert.Equal(t, 2, window.GetDateTime("end").Time().Hour())
|
||||
}
|
||||
|
||||
func TestAlertSilencedGlobal(t *testing.T) {
|
||||
hub, user := beszelTests.GetHubWithUser(t)
|
||||
defer hub.Cleanup()
|
||||
|
||||
// Create multiple systems
|
||||
systems, err := beszelTests.CreateSystems(hub, 3, user.Id, "up")
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Get alert manager
|
||||
am := alerts.NewAlertManager(hub)
|
||||
defer am.StopWorker()
|
||||
|
||||
// Create a global quiet hours window (no system specified)
|
||||
now := time.Now().UTC()
|
||||
startTime := now.Add(-1 * time.Hour)
|
||||
endTime := now.Add(1 * time.Hour)
|
||||
|
||||
_, err = beszelTests.CreateRecord(hub, "quiet_hours", map[string]any{
|
||||
"user": user.Id,
|
||||
"type": "one-time",
|
||||
"start": startTime,
|
||||
"end": endTime,
|
||||
// system field is empty/null for global windows
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// All systems should be silenced
|
||||
for _, system := range systems {
|
||||
silenced := am.IsNotificationSilenced(user.Id, system.Id)
|
||||
assert.True(t, silenced, "Alert should be silenced for system %s (global window)", system.Id)
|
||||
}
|
||||
|
||||
// Even with a systemID that doesn't exist, should be silenced
|
||||
silenced := am.IsNotificationSilenced(user.Id, "nonexistent-system")
|
||||
assert.True(t, silenced, "Alert should be silenced for any system (global window)")
|
||||
}
|
||||
|
||||
func TestAlertSilencedSystemSpecific(t *testing.T) {
|
||||
hub, user := beszelTests.GetHubWithUser(t)
|
||||
defer hub.Cleanup()
|
||||
|
||||
// Create multiple systems
|
||||
systems, err := beszelTests.CreateSystems(hub, 2, user.Id, "up")
|
||||
assert.NoError(t, err)
|
||||
system1 := systems[0]
|
||||
system2 := systems[1]
|
||||
|
||||
// Get alert manager
|
||||
am := alerts.NewAlertManager(hub)
|
||||
defer am.StopWorker()
|
||||
|
||||
// Create a system-specific quiet hours window for system1 only
|
||||
now := time.Now().UTC()
|
||||
startTime := now.Add(-1 * time.Hour)
|
||||
endTime := now.Add(1 * time.Hour)
|
||||
|
||||
_, err = beszelTests.CreateRecord(hub, "quiet_hours", map[string]any{
|
||||
"user": user.Id,
|
||||
"system": system1.Id,
|
||||
"type": "one-time",
|
||||
"start": startTime,
|
||||
"end": endTime,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// System1 should be silenced
|
||||
silenced := am.IsNotificationSilenced(user.Id, system1.Id)
|
||||
assert.True(t, silenced, "Alert should be silenced for system1")
|
||||
|
||||
// System2 should NOT be silenced
|
||||
silenced = am.IsNotificationSilenced(user.Id, system2.Id)
|
||||
assert.False(t, silenced, "Alert should not be silenced for system2")
|
||||
}
|
||||
|
||||
func TestAlertSilencedMultiUser(t *testing.T) {
|
||||
hub, _ := beszelTests.GetHubWithUser(t)
|
||||
defer hub.Cleanup()
|
||||
|
||||
// Create two users
|
||||
user1, err := beszelTests.CreateUser(hub, "user1@example.com", "password")
|
||||
assert.NoError(t, err)
|
||||
|
||||
user2, err := beszelTests.CreateUser(hub, "user2@example.com", "password")
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Create a system accessible to both users
|
||||
system, err := beszelTests.CreateRecord(hub, "systems", map[string]any{
|
||||
"name": "shared-system",
|
||||
"users": []string{user1.Id, user2.Id},
|
||||
"host": "127.0.0.1",
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Get alert manager
|
||||
am := alerts.NewAlertManager(hub)
|
||||
defer am.StopWorker()
|
||||
|
||||
// Create a quiet hours window for user1 only
|
||||
now := time.Now().UTC()
|
||||
startTime := now.Add(-1 * time.Hour)
|
||||
endTime := now.Add(1 * time.Hour)
|
||||
|
||||
_, err = beszelTests.CreateRecord(hub, "quiet_hours", map[string]any{
|
||||
"user": user1.Id,
|
||||
"system": system.Id,
|
||||
"type": "one-time",
|
||||
"start": startTime,
|
||||
"end": endTime,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// User1 should be silenced
|
||||
silenced := am.IsNotificationSilenced(user1.Id, system.Id)
|
||||
assert.True(t, silenced, "Alert should be silenced for user1")
|
||||
|
||||
// User2 should NOT be silenced
|
||||
silenced = am.IsNotificationSilenced(user2.Id, system.Id)
|
||||
assert.False(t, silenced, "Alert should not be silenced for user2")
|
||||
}
|
||||
|
||||
func TestAlertSilencedWithActualAlert(t *testing.T) {
|
||||
synctest.Test(t, func(t *testing.T) {
|
||||
hub, user := beszelTests.GetHubWithUser(t)
|
||||
defer hub.Cleanup()
|
||||
|
||||
// Create a system
|
||||
systems, err := beszelTests.CreateSystems(hub, 1, user.Id, "up")
|
||||
assert.NoError(t, err)
|
||||
system := systems[0]
|
||||
|
||||
// Create a status alert
|
||||
_, err = beszelTests.CreateRecord(hub, "alerts", map[string]any{
|
||||
"name": "Status",
|
||||
"system": system.Id,
|
||||
"user": user.Id,
|
||||
"min": 1,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Create user settings with email
|
||||
userSettings, err := hub.FindFirstRecordByFilter("user_settings", "user={:user}", dbx.Params{"user": user.Id})
|
||||
if err != nil || userSettings == nil {
|
||||
userSettings, err = beszelTests.CreateRecord(hub, "user_settings", map[string]any{
|
||||
"user": user.Id,
|
||||
"settings": map[string]any{
|
||||
"emails": []string{"test@example.com"},
|
||||
},
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
// Create a quiet hours window
|
||||
now := time.Now().UTC()
|
||||
startTime := now.Add(-1 * time.Hour)
|
||||
endTime := now.Add(1 * time.Hour)
|
||||
|
||||
_, err = beszelTests.CreateRecord(hub, "quiet_hours", map[string]any{
|
||||
"user": user.Id,
|
||||
"system": system.Id,
|
||||
"type": "one-time",
|
||||
"start": startTime,
|
||||
"end": endTime,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Get initial email count
|
||||
initialEmailCount := hub.TestMailer.TotalSend()
|
||||
|
||||
// Trigger an alert by setting system to down
|
||||
system.Set("status", "down")
|
||||
err = hub.SaveNoValidate(system)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Wait for the alert to be processed (1 minute + buffer)
|
||||
time.Sleep(time.Second * 75)
|
||||
synctest.Wait()
|
||||
|
||||
// Check that no email was sent (because alert is silenced)
|
||||
finalEmailCount := hub.TestMailer.TotalSend()
|
||||
assert.Equal(t, initialEmailCount, finalEmailCount, "No emails should be sent when alert is silenced")
|
||||
|
||||
// Clear quiet hours windows
|
||||
_, err = hub.DB().NewQuery("DELETE FROM quiet_hours").Execute()
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Reset system to up, then down again
|
||||
system.Set("status", "up")
|
||||
err = hub.SaveNoValidate(system)
|
||||
assert.NoError(t, err)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
system.Set("status", "down")
|
||||
err = hub.SaveNoValidate(system)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Wait for the alert to be processed
|
||||
time.Sleep(time.Second * 75)
|
||||
synctest.Wait()
|
||||
|
||||
// Now an email should be sent
|
||||
newEmailCount := hub.TestMailer.TotalSend()
|
||||
assert.Greater(t, newEmailCount, finalEmailCount, "Email should be sent when not silenced")
|
||||
})
|
||||
}
|
||||
|
||||
func TestAlertSilencedNoWindows(t *testing.T) {
|
||||
hub, user := beszelTests.GetHubWithUser(t)
|
||||
defer hub.Cleanup()
|
||||
|
||||
// Create a system
|
||||
systems, err := beszelTests.CreateSystems(hub, 1, user.Id, "up")
|
||||
assert.NoError(t, err)
|
||||
system := systems[0]
|
||||
|
||||
// Get alert manager
|
||||
am := alerts.NewAlertManager(hub)
|
||||
defer am.StopWorker()
|
||||
|
||||
// Without any quiet hours windows, alert should NOT be silenced
|
||||
silenced := am.IsNotificationSilenced(user.Id, system.Id)
|
||||
assert.False(t, silenced, "Alert should not be silenced when no windows exist")
|
||||
}
|
||||
67
internal/alerts/alerts_smart.go
Normal file
67
internal/alerts/alerts_smart.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package alerts
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
)
|
||||
|
||||
// handleSmartDeviceAlert sends alerts when a SMART device state changes from PASSED to FAILED.
|
||||
// This is automatic and does not require user opt-in.
|
||||
func (am *AlertManager) handleSmartDeviceAlert(e *core.RecordEvent) error {
|
||||
oldState := e.Record.Original().GetString("state")
|
||||
newState := e.Record.GetString("state")
|
||||
|
||||
// Only alert when transitioning from PASSED to FAILED
|
||||
if oldState != "PASSED" || newState != "FAILED" {
|
||||
return e.Next()
|
||||
}
|
||||
|
||||
systemID := e.Record.GetString("system")
|
||||
if systemID == "" {
|
||||
return e.Next()
|
||||
}
|
||||
|
||||
// Fetch the system record to get the name and users
|
||||
systemRecord, err := e.App.FindRecordById("systems", systemID)
|
||||
if err != nil {
|
||||
e.App.Logger().Error("Failed to find system for SMART alert", "err", err, "systemID", systemID)
|
||||
return e.Next()
|
||||
}
|
||||
|
||||
systemName := systemRecord.GetString("name")
|
||||
deviceName := e.Record.GetString("name")
|
||||
model := e.Record.GetString("model")
|
||||
|
||||
// Build alert message
|
||||
title := fmt.Sprintf("SMART failure on %s: %s \U0001F534", systemName, deviceName)
|
||||
var message string
|
||||
if model != "" {
|
||||
message = fmt.Sprintf("Disk %s (%s) SMART status changed to FAILED", deviceName, model)
|
||||
} else {
|
||||
message = fmt.Sprintf("Disk %s SMART status changed to FAILED", deviceName)
|
||||
}
|
||||
|
||||
// Get users associated with the system
|
||||
userIDs := systemRecord.GetStringSlice("users")
|
||||
if len(userIDs) == 0 {
|
||||
return e.Next()
|
||||
}
|
||||
|
||||
// Send alert to each user
|
||||
for _, userID := range userIDs {
|
||||
if err := am.SendAlert(AlertMessageData{
|
||||
UserID: userID,
|
||||
SystemID: systemID,
|
||||
Title: title,
|
||||
Message: message,
|
||||
Link: am.hub.MakeLink("system", systemID),
|
||||
LinkText: "View " + systemName,
|
||||
}); err != nil {
|
||||
e.App.Logger().Error("Failed to send SMART alert", "err", err, "userID", userID)
|
||||
}
|
||||
}
|
||||
|
||||
return e.Next()
|
||||
}
|
||||
|
||||
196
internal/alerts/alerts_smart_test.go
Normal file
196
internal/alerts/alerts_smart_test.go
Normal file
@@ -0,0 +1,196 @@
|
||||
//go:build testing
|
||||
// +build testing
|
||||
|
||||
package alerts_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
beszelTests "github.com/henrygd/beszel/internal/tests"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestSmartDeviceAlert(t *testing.T) {
|
||||
hub, user := beszelTests.GetHubWithUser(t)
|
||||
defer hub.Cleanup()
|
||||
|
||||
// Create a system for the user
|
||||
system, err := beszelTests.CreateRecord(hub, "systems", map[string]any{
|
||||
"name": "test-system",
|
||||
"users": []string{user.Id},
|
||||
"host": "127.0.0.1",
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Create a smart_device with state PASSED
|
||||
smartDevice, err := beszelTests.CreateRecord(hub, "smart_devices", map[string]any{
|
||||
"system": system.Id,
|
||||
"name": "/dev/sda",
|
||||
"model": "Samsung SSD 970 EVO",
|
||||
"state": "PASSED",
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify no emails sent initially
|
||||
assert.Zero(t, hub.TestMailer.TotalSend(), "should have 0 emails sent initially")
|
||||
|
||||
// Re-fetch the record so PocketBase can properly track original values
|
||||
smartDevice, err = hub.FindRecordById("smart_devices", smartDevice.Id)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Update the smart device state to FAILED
|
||||
smartDevice.Set("state", "FAILED")
|
||||
err = hub.Save(smartDevice)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Wait for the alert to be processed
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
|
||||
// Verify that an email was sent
|
||||
assert.EqualValues(t, 1, hub.TestMailer.TotalSend(), "should have 1 email sent after state changed to FAILED")
|
||||
|
||||
// Check the email content
|
||||
lastMessage := hub.TestMailer.LastMessage()
|
||||
assert.Contains(t, lastMessage.Subject, "SMART failure on test-system")
|
||||
assert.Contains(t, lastMessage.Subject, "/dev/sda")
|
||||
assert.Contains(t, lastMessage.Text, "Samsung SSD 970 EVO")
|
||||
assert.Contains(t, lastMessage.Text, "FAILED")
|
||||
}
|
||||
|
||||
func TestSmartDeviceAlertNoAlertOnNonPassedToFailed(t *testing.T) {
|
||||
hub, user := beszelTests.GetHubWithUser(t)
|
||||
defer hub.Cleanup()
|
||||
|
||||
// Create a system for the user
|
||||
system, err := beszelTests.CreateRecord(hub, "systems", map[string]any{
|
||||
"name": "test-system",
|
||||
"users": []string{user.Id},
|
||||
"host": "127.0.0.1",
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Create a smart_device with state UNKNOWN
|
||||
smartDevice, err := beszelTests.CreateRecord(hub, "smart_devices", map[string]any{
|
||||
"system": system.Id,
|
||||
"name": "/dev/sda",
|
||||
"model": "Samsung SSD 970 EVO",
|
||||
"state": "UNKNOWN",
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Re-fetch the record so PocketBase can properly track original values
|
||||
smartDevice, err = hub.FindRecordById("smart_devices", smartDevice.Id)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Update the state from UNKNOWN to FAILED - should NOT trigger alert
|
||||
smartDevice.Set("state", "FAILED")
|
||||
err = hub.Save(smartDevice)
|
||||
assert.NoError(t, err)
|
||||
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
|
||||
// Verify no email was sent (only PASSED -> FAILED triggers alert)
|
||||
assert.Zero(t, hub.TestMailer.TotalSend(), "should have 0 emails when changing from UNKNOWN to FAILED")
|
||||
|
||||
// Re-fetch the record again
|
||||
smartDevice, err = hub.FindRecordById("smart_devices", smartDevice.Id)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Update state from FAILED to PASSED - should NOT trigger alert
|
||||
smartDevice.Set("state", "PASSED")
|
||||
err = hub.Save(smartDevice)
|
||||
assert.NoError(t, err)
|
||||
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
|
||||
// Verify no email was sent
|
||||
assert.Zero(t, hub.TestMailer.TotalSend(), "should have 0 emails when changing from FAILED to PASSED")
|
||||
}
|
||||
|
||||
func TestSmartDeviceAlertMultipleUsers(t *testing.T) {
|
||||
hub, user1 := beszelTests.GetHubWithUser(t)
|
||||
defer hub.Cleanup()
|
||||
|
||||
// Create a second user
|
||||
user2, err := beszelTests.CreateUser(hub, "test2@example.com", "password")
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Create user settings for the second user
|
||||
_, err = beszelTests.CreateRecord(hub, "user_settings", map[string]any{
|
||||
"user": user2.Id,
|
||||
"settings": `{"emails":["test2@example.com"],"webhooks":[]}`,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Create a system with both users
|
||||
system, err := beszelTests.CreateRecord(hub, "systems", map[string]any{
|
||||
"name": "shared-system",
|
||||
"users": []string{user1.Id, user2.Id},
|
||||
"host": "127.0.0.1",
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Create a smart_device with state PASSED
|
||||
smartDevice, err := beszelTests.CreateRecord(hub, "smart_devices", map[string]any{
|
||||
"system": system.Id,
|
||||
"name": "/dev/nvme0n1",
|
||||
"model": "WD Black SN850",
|
||||
"state": "PASSED",
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Re-fetch the record so PocketBase can properly track original values
|
||||
smartDevice, err = hub.FindRecordById("smart_devices", smartDevice.Id)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Update the smart device state to FAILED
|
||||
smartDevice.Set("state", "FAILED")
|
||||
err = hub.Save(smartDevice)
|
||||
assert.NoError(t, err)
|
||||
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
|
||||
// Verify that two emails were sent (one for each user)
|
||||
assert.EqualValues(t, 2, hub.TestMailer.TotalSend(), "should have 2 emails sent for 2 users")
|
||||
}
|
||||
|
||||
func TestSmartDeviceAlertWithoutModel(t *testing.T) {
|
||||
hub, user := beszelTests.GetHubWithUser(t)
|
||||
defer hub.Cleanup()
|
||||
|
||||
// Create a system for the user
|
||||
system, err := beszelTests.CreateRecord(hub, "systems", map[string]any{
|
||||
"name": "test-system",
|
||||
"users": []string{user.Id},
|
||||
"host": "127.0.0.1",
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Create a smart_device with state PASSED but no model
|
||||
smartDevice, err := beszelTests.CreateRecord(hub, "smart_devices", map[string]any{
|
||||
"system": system.Id,
|
||||
"name": "/dev/sdb",
|
||||
"state": "PASSED",
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Re-fetch the record so PocketBase can properly track original values
|
||||
smartDevice, err = hub.FindRecordById("smart_devices", smartDevice.Id)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Update the smart device state to FAILED
|
||||
smartDevice.Set("state", "FAILED")
|
||||
err = hub.Save(smartDevice)
|
||||
assert.NoError(t, err)
|
||||
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
|
||||
// Verify that an email was sent
|
||||
assert.EqualValues(t, 1, hub.TestMailer.TotalSend(), "should have 1 email sent")
|
||||
|
||||
// Check that the email doesn't have empty parentheses for missing model
|
||||
lastMessage := hub.TestMailer.LastMessage()
|
||||
assert.NotContains(t, lastMessage.Text, "()", "should not have empty parentheses for missing model")
|
||||
assert.Contains(t, lastMessage.Text, "/dev/sdb")
|
||||
}
|
||||
@@ -161,19 +161,15 @@ func (am *AlertManager) sendStatusAlert(alertStatus string, systemName string, a
|
||||
title := fmt.Sprintf("Connection to %s is %s %v", systemName, alertStatus, emoji)
|
||||
message := strings.TrimSuffix(title, emoji)
|
||||
|
||||
// if errs := am.hub.ExpandRecord(alertRecord, []string{"user"}, nil); len(errs) > 0 {
|
||||
// return errs["user"]
|
||||
// }
|
||||
// user := alertRecord.ExpandedOne("user")
|
||||
// if user == nil {
|
||||
// return nil
|
||||
// }
|
||||
// Get system ID for the link
|
||||
systemID := alertRecord.GetString("system")
|
||||
|
||||
return am.SendAlert(AlertMessageData{
|
||||
UserID: alertRecord.GetString("user"),
|
||||
SystemID: systemID,
|
||||
Title: title,
|
||||
Message: message,
|
||||
Link: am.hub.MakeLink("system", systemName),
|
||||
Link: am.hub.MakeLink("system", systemID),
|
||||
LinkText: "View " + systemName,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -64,6 +64,8 @@ func (am *AlertManager) HandleSystemAlerts(systemRecord *core.Record, data *syst
|
||||
case "LoadAvg15":
|
||||
val = data.Info.LoadAvg[2]
|
||||
unit = ""
|
||||
case "GPU":
|
||||
val = data.Info.GpuPct
|
||||
}
|
||||
|
||||
triggered := alertRecord.GetBool("triggered")
|
||||
@@ -206,6 +208,17 @@ func (am *AlertManager) HandleSystemAlerts(systemRecord *core.Record, data *syst
|
||||
alert.val += stats.LoadAvg[1]
|
||||
case "LoadAvg15":
|
||||
alert.val += stats.LoadAvg[2]
|
||||
case "GPU":
|
||||
if len(stats.GPU) == 0 {
|
||||
continue
|
||||
}
|
||||
maxUsage := 0.0
|
||||
for _, gpu := range stats.GPU {
|
||||
if gpu.Usage > maxUsage {
|
||||
maxUsage = gpu.Usage
|
||||
}
|
||||
}
|
||||
alert.val += maxUsage
|
||||
default:
|
||||
continue
|
||||
}
|
||||
@@ -268,9 +281,9 @@ func (am *AlertManager) sendSystemAlert(alert SystemAlertData) {
|
||||
alert.name = after + "m Load"
|
||||
}
|
||||
|
||||
// make title alert name lowercase if not CPU
|
||||
// make title alert name lowercase if not CPU or GPU
|
||||
titleAlertName := alert.name
|
||||
if titleAlertName != "CPU" {
|
||||
if titleAlertName != "CPU" && titleAlertName != "GPU" {
|
||||
titleAlertName = strings.ToLower(titleAlertName)
|
||||
}
|
||||
|
||||
@@ -296,9 +309,10 @@ func (am *AlertManager) sendSystemAlert(alert SystemAlertData) {
|
||||
}
|
||||
am.SendAlert(AlertMessageData{
|
||||
UserID: alert.alertRecord.GetString("user"),
|
||||
SystemID: alert.systemRecord.Id,
|
||||
Title: subject,
|
||||
Message: body,
|
||||
Link: am.hub.MakeLink("system", systemName),
|
||||
Link: am.hub.MakeLink("system", alert.systemRecord.Id),
|
||||
LinkText: "View " + systemName,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package common
|
||||
import (
|
||||
"github.com/henrygd/beszel/internal/entities/smart"
|
||||
"github.com/henrygd/beszel/internal/entities/system"
|
||||
"github.com/henrygd/beszel/internal/entities/systemd"
|
||||
)
|
||||
|
||||
type WebSocketAction = uint8
|
||||
@@ -18,6 +19,8 @@ const (
|
||||
GetContainerInfo
|
||||
// Request SMART data from agent
|
||||
GetSmartData
|
||||
// Request detailed systemd service info from agent
|
||||
GetSystemdInfo
|
||||
// Add new actions here...
|
||||
)
|
||||
|
||||
@@ -36,6 +39,7 @@ type AgentResponse struct {
|
||||
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"`
|
||||
}
|
||||
@@ -65,3 +69,7 @@ type ContainerLogsRequest struct {
|
||||
type ContainerInfoRequest struct {
|
||||
ContainerID string `cbor:"0,keyasint"`
|
||||
}
|
||||
|
||||
type SystemdInfoRequest struct {
|
||||
ServiceName string `cbor:"0,keyasint"`
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ FROM --platform=$BUILDPLATFORM golang:alpine AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
|
||||
COPY ../go.mod ../go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
@@ -13,7 +12,24 @@ COPY . ./
|
||||
ARG TARGETOS TARGETARCH
|
||||
RUN CGO_ENABLED=0 GOGC=75 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -ldflags "-w -s" -o /agent ./internal/cmd/agent
|
||||
|
||||
RUN rm -rf /tmp/*
|
||||
# --------------------------
|
||||
# Smartmontools builder stage
|
||||
# --------------------------
|
||||
FROM nvidia/cuda:12.2.2-base-ubuntu22.04 AS smartmontools-builder
|
||||
|
||||
RUN apt-get update && apt-get install -y \
|
||||
wget \
|
||||
build-essential \
|
||||
&& wget https://downloads.sourceforge.net/project/smartmontools/smartmontools/7.5/smartmontools-7.5.tar.gz \
|
||||
&& tar zxvf smartmontools-7.5.tar.gz \
|
||||
&& cd smartmontools-7.5 \
|
||||
&& ./configure --prefix=/usr --sysconfdir=/etc \
|
||||
&& make \
|
||||
&& make install \
|
||||
&& rm -rf /smartmontools-7.5* \
|
||||
&& apt-get remove -y wget build-essential \
|
||||
&& apt-get autoremove -y \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# --------------------------
|
||||
# Final image: GPU-enabled agent with nvidia-smi
|
||||
@@ -21,10 +37,8 @@ RUN rm -rf /tmp/*
|
||||
FROM nvidia/cuda:12.2.2-base-ubuntu22.04
|
||||
COPY --from=builder /agent /agent
|
||||
|
||||
# this is so we don't need to create the /tmp directory in the scratch container
|
||||
COPY --from=builder /tmp /tmp
|
||||
|
||||
RUN apt-get update && apt-get install -y smartmontools && rm -rf /var/lib/apt/lists/*
|
||||
# Copy smartmontools binaries and config files
|
||||
COPY --from=smartmontools-builder /usr/sbin/smartctl /usr/sbin/smartctl
|
||||
|
||||
# Ensure data persistence across container recreations
|
||||
VOLUME ["/var/lib/beszel-agent"]
|
||||
|
||||
@@ -3,9 +3,11 @@ package system
|
||||
// TODO: this is confusing, make common package with common/types common/helpers etc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/henrygd/beszel/internal/entities/container"
|
||||
"github.com/henrygd/beszel/internal/entities/systemd"
|
||||
)
|
||||
|
||||
type Stats struct {
|
||||
@@ -41,9 +43,28 @@ type Stats struct {
|
||||
LoadAvg [3]float64 `json:"la,omitempty" cbor:"28,keyasint"`
|
||||
Battery [2]uint8 `json:"bat,omitzero" cbor:"29,keyasint,omitzero"` // [percent, charge state, current]
|
||||
MaxMem float64 `json:"mm,omitempty" cbor:"30,keyasint,omitempty"`
|
||||
NetworkInterfaces map[string][4]uint64 `json:"ni,omitempty" cbor:"31,keyasint,omitempty"` // [upload bytes, download bytes, total upload, total download]
|
||||
DiskIO [2]uint64 `json:"dio,omitzero" cbor:"32,keyasint,omitzero"` // [read bytes, write bytes]
|
||||
MaxDiskIO [2]uint64 `json:"diom,omitzero" cbor:"-"` // [max read bytes, max write bytes]
|
||||
NetworkInterfaces map[string][4]uint64 `json:"ni,omitempty" cbor:"31,keyasint,omitempty"` // [upload bytes, download bytes, total upload, total download]
|
||||
DiskIO [2]uint64 `json:"dio,omitzero" cbor:"32,keyasint,omitzero"` // [read bytes, write bytes]
|
||||
MaxDiskIO [2]uint64 `json:"diom,omitzero" cbor:"-"` // [max read bytes, max write bytes]
|
||||
CpuBreakdown []float64 `json:"cpub,omitempty" cbor:"33,keyasint,omitempty"` // [user, system, iowait, steal, idle]
|
||||
CpuCoresUsage Uint8Slice `json:"cpus,omitempty" cbor:"34,keyasint,omitempty"` // per-core busy usage [CPU0..]
|
||||
}
|
||||
|
||||
// Uint8Slice wraps []uint8 to customize JSON encoding while keeping CBOR efficient.
|
||||
// JSON: encodes as array of numbers (avoids base64 string).
|
||||
// CBOR: falls back to default handling for []uint8 (byte string), keeping payload small.
|
||||
type Uint8Slice []uint8
|
||||
|
||||
func (s Uint8Slice) MarshalJSON() ([]byte, error) {
|
||||
if s == nil {
|
||||
return []byte("null"), nil
|
||||
}
|
||||
// Convert to wider ints to force array-of-numbers encoding.
|
||||
arr := make([]uint16, len(s))
|
||||
for i, v := range s {
|
||||
arr[i] = uint16(v)
|
||||
}
|
||||
return json.Marshal(arr)
|
||||
}
|
||||
|
||||
type GPUData struct {
|
||||
@@ -123,13 +144,16 @@ type Info struct {
|
||||
LoadAvg15 float64 `json:"l15,omitempty" cbor:"17,keyasint,omitempty"`
|
||||
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"`
|
||||
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]
|
||||
}
|
||||
|
||||
// Final data structure to return to the hub
|
||||
type CombinedData struct {
|
||||
Stats Stats `json:"stats" cbor:"0,keyasint"`
|
||||
Info Info `json:"info" cbor:"1,keyasint"`
|
||||
Containers []*container.Stats `json:"container" cbor:"2,keyasint"`
|
||||
Stats Stats `json:"stats" cbor:"0,keyasint"`
|
||||
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"`
|
||||
}
|
||||
|
||||
127
internal/entities/systemd/systemd.go
Normal file
127
internal/entities/systemd/systemd.go
Normal file
@@ -0,0 +1,127 @@
|
||||
package systemd
|
||||
|
||||
import (
|
||||
"math"
|
||||
"runtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ServiceState represents the status of a systemd service
|
||||
type ServiceState uint8
|
||||
|
||||
const (
|
||||
StatusActive ServiceState = iota
|
||||
StatusInactive
|
||||
StatusFailed
|
||||
StatusActivating
|
||||
StatusDeactivating
|
||||
StatusReloading
|
||||
)
|
||||
|
||||
// ServiceSubState represents the sub status of a systemd service
|
||||
type ServiceSubState uint8
|
||||
|
||||
const (
|
||||
SubStateDead ServiceSubState = iota
|
||||
SubStateRunning
|
||||
SubStateExited
|
||||
SubStateFailed
|
||||
SubStateUnknown
|
||||
)
|
||||
|
||||
// ParseServiceStatus converts a string status to a ServiceStatus enum value
|
||||
func ParseServiceStatus(status string) ServiceState {
|
||||
switch status {
|
||||
case "active":
|
||||
return StatusActive
|
||||
case "inactive":
|
||||
return StatusInactive
|
||||
case "failed":
|
||||
return StatusFailed
|
||||
case "activating":
|
||||
return StatusActivating
|
||||
case "deactivating":
|
||||
return StatusDeactivating
|
||||
case "reloading":
|
||||
return StatusReloading
|
||||
default:
|
||||
return StatusInactive
|
||||
}
|
||||
}
|
||||
|
||||
// ParseServiceSubState converts a string sub status to a ServiceSubState enum value
|
||||
func ParseServiceSubState(subState string) ServiceSubState {
|
||||
switch subState {
|
||||
case "dead":
|
||||
return SubStateDead
|
||||
case "running":
|
||||
return SubStateRunning
|
||||
case "exited":
|
||||
return SubStateExited
|
||||
case "failed":
|
||||
return SubStateFailed
|
||||
default:
|
||||
return SubStateUnknown
|
||||
}
|
||||
}
|
||||
|
||||
// Service represents a single systemd service with its stats.
|
||||
type Service struct {
|
||||
Name string `json:"n" cbor:"0,keyasint"`
|
||||
State ServiceState `json:"s" cbor:"1,keyasint"`
|
||||
Cpu float64 `json:"c" cbor:"2,keyasint"`
|
||||
Mem uint64 `json:"m" cbor:"3,keyasint"`
|
||||
MemPeak uint64 `json:"mp" cbor:"4,keyasint"`
|
||||
Sub ServiceSubState `json:"ss" cbor:"5,keyasint"`
|
||||
CpuPeak float64 `json:"cp" cbor:"6,keyasint"`
|
||||
PrevCpuUsage uint64 `json:"-"`
|
||||
PrevReadTime time.Time `json:"-"`
|
||||
}
|
||||
|
||||
// UpdateCPUPercent calculates the CPU usage percentage for the service.
|
||||
func (s *Service) UpdateCPUPercent(cpuUsage uint64) {
|
||||
now := time.Now()
|
||||
|
||||
if s.PrevReadTime.IsZero() || cpuUsage < s.PrevCpuUsage {
|
||||
s.Cpu = 0
|
||||
s.PrevCpuUsage = cpuUsage
|
||||
s.PrevReadTime = now
|
||||
return
|
||||
}
|
||||
|
||||
duration := now.Sub(s.PrevReadTime).Nanoseconds()
|
||||
if duration <= 0 {
|
||||
s.PrevCpuUsage = cpuUsage
|
||||
s.PrevReadTime = now
|
||||
return
|
||||
}
|
||||
|
||||
coreCount := int64(runtime.NumCPU())
|
||||
duration *= coreCount
|
||||
|
||||
usageDelta := cpuUsage - s.PrevCpuUsage
|
||||
cpuPercent := float64(usageDelta) / float64(duration)
|
||||
s.Cpu = twoDecimals(cpuPercent * 100)
|
||||
|
||||
if s.Cpu > s.CpuPeak {
|
||||
s.CpuPeak = s.Cpu
|
||||
}
|
||||
|
||||
s.PrevCpuUsage = cpuUsage
|
||||
s.PrevReadTime = now
|
||||
}
|
||||
|
||||
func twoDecimals(value float64) float64 {
|
||||
return math.Round(value*100) / 100
|
||||
}
|
||||
|
||||
// ServiceDependency represents a unit that the service depends on.
|
||||
type ServiceDependency struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description,omitempty"`
|
||||
ActiveState string `json:"activeState,omitempty"`
|
||||
SubState string `json:"subState,omitempty"`
|
||||
}
|
||||
|
||||
// ServiceDetails contains extended information about a systemd service.
|
||||
type ServiceDetails map[string]any
|
||||
113
internal/entities/systemd/systemd_test.go
Normal file
113
internal/entities/systemd/systemd_test.go
Normal file
@@ -0,0 +1,113 @@
|
||||
//go:build testing
|
||||
|
||||
package systemd_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/henrygd/beszel/internal/entities/systemd"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestParseServiceStatus(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
expected systemd.ServiceState
|
||||
}{
|
||||
{"active", systemd.StatusActive},
|
||||
{"inactive", systemd.StatusInactive},
|
||||
{"failed", systemd.StatusFailed},
|
||||
{"activating", systemd.StatusActivating},
|
||||
{"deactivating", systemd.StatusDeactivating},
|
||||
{"reloading", systemd.StatusReloading},
|
||||
{"unknown", systemd.StatusInactive}, // default case
|
||||
{"", systemd.StatusInactive}, // default case
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.input, func(t *testing.T) {
|
||||
result := systemd.ParseServiceStatus(test.input)
|
||||
assert.Equal(t, test.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseServiceSubState(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
expected systemd.ServiceSubState
|
||||
}{
|
||||
{"dead", systemd.SubStateDead},
|
||||
{"running", systemd.SubStateRunning},
|
||||
{"exited", systemd.SubStateExited},
|
||||
{"failed", systemd.SubStateFailed},
|
||||
{"unknown", systemd.SubStateUnknown},
|
||||
{"other", systemd.SubStateUnknown}, // default case
|
||||
{"", systemd.SubStateUnknown}, // default case
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.input, func(t *testing.T) {
|
||||
result := systemd.ParseServiceSubState(test.input)
|
||||
assert.Equal(t, test.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestServiceUpdateCPUPercent(t *testing.T) {
|
||||
t.Run("initial call sets CPU to 0", func(t *testing.T) {
|
||||
service := &systemd.Service{}
|
||||
service.UpdateCPUPercent(1000)
|
||||
assert.Equal(t, 0.0, service.Cpu)
|
||||
assert.Equal(t, uint64(1000), service.PrevCpuUsage)
|
||||
assert.False(t, service.PrevReadTime.IsZero())
|
||||
})
|
||||
|
||||
t.Run("subsequent call calculates CPU percentage", func(t *testing.T) {
|
||||
service := &systemd.Service{}
|
||||
service.PrevCpuUsage = 1000
|
||||
service.PrevReadTime = time.Now().Add(-time.Second)
|
||||
|
||||
service.UpdateCPUPercent(8000000000) // 8 seconds of CPU time
|
||||
|
||||
// CPU usage should be positive and reasonable
|
||||
assert.Greater(t, service.Cpu, 0.0, "CPU usage should be positive")
|
||||
assert.LessOrEqual(t, service.Cpu, 100.0, "CPU usage should not exceed 100%")
|
||||
assert.Equal(t, uint64(8000000000), service.PrevCpuUsage)
|
||||
assert.Greater(t, service.CpuPeak, 0.0, "CPU peak should be set")
|
||||
})
|
||||
|
||||
t.Run("CPU peak updates only when higher", func(t *testing.T) {
|
||||
service := &systemd.Service{}
|
||||
service.PrevCpuUsage = 1000
|
||||
service.PrevReadTime = time.Now().Add(-time.Second)
|
||||
service.UpdateCPUPercent(8000000000) // Set initial peak to ~50%
|
||||
initialPeak := service.CpuPeak
|
||||
|
||||
// Now try with much lower CPU usage - should not update peak
|
||||
service.PrevReadTime = time.Now().Add(-time.Second)
|
||||
service.UpdateCPUPercent(1000000) // Much lower usage
|
||||
assert.Equal(t, initialPeak, service.CpuPeak, "Peak should not update for lower CPU usage")
|
||||
})
|
||||
|
||||
t.Run("handles zero duration", func(t *testing.T) {
|
||||
service := &systemd.Service{}
|
||||
service.PrevCpuUsage = 1000
|
||||
now := time.Now()
|
||||
service.PrevReadTime = now
|
||||
// Mock time.Now() to return the same time to ensure zero duration
|
||||
// Since we can't mock time in Go easily, we'll check the logic manually
|
||||
// The zero duration case happens when duration <= 0
|
||||
assert.Equal(t, 0.0, service.Cpu, "CPU should start at 0")
|
||||
})
|
||||
|
||||
t.Run("handles CPU usage wraparound", func(t *testing.T) {
|
||||
service := &systemd.Service{}
|
||||
// Simulate wraparound where new usage is less than previous
|
||||
service.PrevCpuUsage = 1000
|
||||
service.PrevReadTime = time.Now().Add(-time.Second)
|
||||
service.UpdateCPUPercent(500) // Less than previous, should reset
|
||||
assert.Equal(t, 0.0, service.Cpu)
|
||||
})
|
||||
}
|
||||
@@ -268,8 +268,10 @@ func (h *Hub) registerApiRoutes(se *core.ServeEvent) error {
|
||||
// update / delete user alerts
|
||||
apiAuth.POST("/user-alerts", alerts.UpsertUserAlerts)
|
||||
apiAuth.DELETE("/user-alerts", alerts.DeleteUserAlerts)
|
||||
// get SMART data
|
||||
apiAuth.GET("/smart", h.getSmartData)
|
||||
// refresh SMART devices for a system
|
||||
apiAuth.POST("/smart/refresh", h.refreshSmartData)
|
||||
// get systemd service details
|
||||
apiAuth.GET("/systemd/info", h.getSystemdInfo)
|
||||
// /containers routes
|
||||
if enabled, _ := GetEnv("CONTAINER_DETAILS"); enabled != "false" {
|
||||
// get container logs
|
||||
@@ -342,22 +344,46 @@ func (h *Hub) getContainerInfo(e *core.RequestEvent) error {
|
||||
}, "info")
|
||||
}
|
||||
|
||||
// getSmartData handles GET /api/beszel/smart requests
|
||||
func (h *Hub) getSmartData(e *core.RequestEvent) error {
|
||||
systemID := e.Request.URL.Query().Get("system")
|
||||
if systemID == "" {
|
||||
return e.JSON(http.StatusBadRequest, map[string]string{"error": "system parameter is required"})
|
||||
// getSystemdInfo handles GET /api/beszel/systemd/info requests
|
||||
func (h *Hub) getSystemdInfo(e *core.RequestEvent) error {
|
||||
query := e.Request.URL.Query()
|
||||
systemID := query.Get("system")
|
||||
serviceName := query.Get("service")
|
||||
|
||||
if systemID == "" || serviceName == "" {
|
||||
return e.JSON(http.StatusBadRequest, map[string]string{"error": "system and service parameters are required"})
|
||||
}
|
||||
system, err := h.sm.GetSystem(systemID)
|
||||
if err != nil {
|
||||
return e.JSON(http.StatusNotFound, map[string]string{"error": "system not found"})
|
||||
}
|
||||
data, err := system.FetchSmartDataFromAgent()
|
||||
details, err := system.FetchSystemdInfoFromAgent(serviceName)
|
||||
if err != nil {
|
||||
return e.JSON(http.StatusNotFound, map[string]string{"error": err.Error()})
|
||||
}
|
||||
e.Response.Header().Set("Cache-Control", "public, max-age=60")
|
||||
return e.JSON(http.StatusOK, data)
|
||||
return e.JSON(http.StatusOK, map[string]any{"details": details})
|
||||
}
|
||||
|
||||
// refreshSmartData handles POST /api/beszel/smart/refresh requests
|
||||
// Fetches fresh SMART data from the agent and updates the collection
|
||||
func (h *Hub) refreshSmartData(e *core.RequestEvent) error {
|
||||
systemID := e.Request.URL.Query().Get("system")
|
||||
if systemID == "" {
|
||||
return e.JSON(http.StatusBadRequest, map[string]string{"error": "system parameter is required"})
|
||||
}
|
||||
|
||||
system, err := h.sm.GetSystem(systemID)
|
||||
if err != nil {
|
||||
return e.JSON(http.StatusNotFound, map[string]string{"error": "system not found"})
|
||||
}
|
||||
|
||||
// Fetch and save SMART devices
|
||||
if err := system.FetchAndSaveSmartDevices(); err != nil {
|
||||
return e.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
|
||||
}
|
||||
|
||||
return e.JSON(http.StatusOK, map[string]string{"status": "ok"})
|
||||
}
|
||||
|
||||
// generates key pair if it doesn't exist and returns signer
|
||||
|
||||
@@ -5,9 +5,11 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash/fnv"
|
||||
"math/rand"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/henrygd/beszel/internal/common"
|
||||
@@ -15,6 +17,7 @@ import (
|
||||
|
||||
"github.com/henrygd/beszel/internal/entities/container"
|
||||
"github.com/henrygd/beszel/internal/entities/system"
|
||||
"github.com/henrygd/beszel/internal/entities/systemd"
|
||||
|
||||
"github.com/henrygd/beszel"
|
||||
|
||||
@@ -38,6 +41,7 @@ type System struct {
|
||||
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
|
||||
}
|
||||
|
||||
func (sm *SystemManager) NewSystem(systemId string) *System {
|
||||
@@ -171,6 +175,14 @@ func (sys *System) createRecords(data *system.CombinedData) (*core.Record, error
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// add new systemd_stats record
|
||||
if len(data.SystemdServices) > 0 {
|
||||
if err := createSystemdStatsRecords(txApp, data.SystemdServices, sys.Id); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// update system record (do this last because it triggers alerts and we need above records to be inserted first)
|
||||
systemRecord.Set("status", up)
|
||||
|
||||
@@ -181,14 +193,53 @@ 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 createSystemdStatsRecords(app core.App, data []*systemd.Service, systemId string) error {
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
// shared params for all records
|
||||
params := dbx.Params{
|
||||
"system": systemId,
|
||||
"updated": time.Now().UTC().UnixMilli(),
|
||||
}
|
||||
|
||||
valueStrings := make([]string, 0, len(data))
|
||||
for i, service := range data {
|
||||
suffix := fmt.Sprintf("%d", i)
|
||||
valueStrings = append(valueStrings, fmt.Sprintf("({:id%[1]s}, {:system}, {:name%[1]s}, {:state%[1]s}, {:sub%[1]s}, {:cpu%[1]s}, {:cpuPeak%[1]s}, {:memory%[1]s}, {:memPeak%[1]s}, {:updated})", suffix))
|
||||
params["id"+suffix] = makeStableHashId(systemId, service.Name)
|
||||
params["name"+suffix] = service.Name
|
||||
params["state"+suffix] = service.State
|
||||
params["sub"+suffix] = service.Sub
|
||||
params["cpu"+suffix] = service.Cpu
|
||||
params["cpuPeak"+suffix] = service.CpuPeak
|
||||
params["memory"+suffix] = service.Mem
|
||||
params["memPeak"+suffix] = service.MemPeak
|
||||
}
|
||||
queryString := fmt.Sprintf(
|
||||
"INSERT INTO systemd_services (id, system, name, state, sub, cpu, cpuPeak, memory, memPeak, updated) VALUES %s ON CONFLICT(id) DO UPDATE SET system = excluded.system, name = excluded.name, state = excluded.state, sub = excluded.sub, cpu = excluded.cpu, cpuPeak = excluded.cpuPeak, memory = excluded.memory, memPeak = excluded.memPeak, updated = excluded.updated",
|
||||
strings.Join(valueStrings, ","),
|
||||
)
|
||||
_, err := app.DB().NewQuery(queryString).Bind(params).Execute()
|
||||
return err
|
||||
}
|
||||
|
||||
// createContainerRecords creates container records
|
||||
func createContainerRecords(app core.App, data []*container.Stats, systemId string) error {
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
// shared params for all records
|
||||
params := dbx.Params{
|
||||
"system": systemId,
|
||||
"updated": time.Now().UTC().UnixMilli(),
|
||||
@@ -340,16 +391,16 @@ func (sys *System) FetchContainerLogsFromAgent(containerID string) (string, erro
|
||||
return sys.fetchStringFromAgentViaSSH(common.GetContainerLogs, common.ContainerLogsRequest{ContainerID: containerID}, "no logs in response")
|
||||
}
|
||||
|
||||
// FetchSmartDataFromAgent fetches SMART data from the agent
|
||||
func (sys *System) FetchSmartDataFromAgent() (map[string]any, error) {
|
||||
// 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.RequestSmartData(ctx)
|
||||
return sys.WsConn.RequestSystemdInfo(ctx, serviceName)
|
||||
}
|
||||
// fetch via SSH
|
||||
var result map[string]any
|
||||
|
||||
var result systemd.ServiceDetails
|
||||
err := sys.runSSHOperation(5*time.Second, 1, func(session *ssh.Session) (bool, error) {
|
||||
stdout, err := session.StdoutPipe()
|
||||
if err != nil {
|
||||
@@ -362,23 +413,38 @@ func (sys *System) FetchSmartDataFromAgent() (map[string]any, error) {
|
||||
if err := session.Shell(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
req := common.HubRequest[any]{Action: common.GetSmartData}
|
||||
_ = cbor.NewEncoder(stdin).Encode(req)
|
||||
|
||||
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
|
||||
}
|
||||
// Convert to generic map for JSON response
|
||||
result = make(map[string]any, len(resp.SmartData))
|
||||
for k, v := range resp.SmartData {
|
||||
result[k] = v
|
||||
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
|
||||
})
|
||||
|
||||
return result, err
|
||||
}
|
||||
|
||||
func makeStableHashId(strings ...string) string {
|
||||
hash := fnv.New32a()
|
||||
for _, str := range strings {
|
||||
hash.Write([]byte(str))
|
||||
}
|
||||
return fmt.Sprintf("%x", hash.Sum32())
|
||||
}
|
||||
|
||||
// fetchDataViaSSH handles fetching data using SSH.
|
||||
// This function encapsulates the original SSH logic.
|
||||
// It updates sys.data directly upon successful fetch.
|
||||
|
||||
132
internal/hub/systems/system_smart.go
Normal file
132
internal/hub/systems/system_smart.go
Normal file
@@ -0,0 +1,132 @@
|
||||
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()
|
||||
if err != nil || len(smartData) == 0 {
|
||||
return err
|
||||
}
|
||||
return sys.saveSmartDevices(smartData)
|
||||
}
|
||||
|
||||
// saveSmartDevices saves SMART device data to the smart_devices collection
|
||||
func (sys *System) saveSmartDevices(smartData map[string]smart.SmartData) error {
|
||||
if len(smartData) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
hub := sys.manager.hub
|
||||
collection, err := hub.FindCachedCollectionByNameOrId("smart_devices")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for deviceKey, device := range smartData {
|
||||
if err := sys.upsertSmartDeviceRecord(collection, deviceKey, device); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sys *System) upsertSmartDeviceRecord(collection *core.Collection, deviceKey string, device smart.SmartData) error {
|
||||
hub := sys.manager.hub
|
||||
recordID := makeStableHashId(sys.Id, deviceKey)
|
||||
|
||||
record, err := hub.FindRecordById(collection, recordID)
|
||||
if err != nil {
|
||||
if !errors.Is(err, sql.ErrNoRows) {
|
||||
return err
|
||||
}
|
||||
record = core.NewRecord(collection)
|
||||
record.Set("id", recordID)
|
||||
}
|
||||
|
||||
name := device.DiskName
|
||||
if name == "" {
|
||||
name = deviceKey
|
||||
}
|
||||
|
||||
powerOnHours, powerCycles := extractPowerMetrics(device.Attributes)
|
||||
record.Set("system", sys.Id)
|
||||
record.Set("name", name)
|
||||
record.Set("model", device.ModelName)
|
||||
record.Set("state", device.SmartStatus)
|
||||
record.Set("capacity", device.Capacity)
|
||||
record.Set("temp", device.Temperature)
|
||||
record.Set("firmware", device.FirmwareVersion)
|
||||
record.Set("serial", device.SerialNumber)
|
||||
record.Set("type", device.DiskType)
|
||||
record.Set("hours", powerOnHours)
|
||||
record.Set("cycles", powerCycles)
|
||||
record.Set("attributes", device.Attributes)
|
||||
|
||||
return hub.SaveNoValidate(record)
|
||||
}
|
||||
|
||||
// extractPowerMetrics extracts power on hours and power cycles from SMART attributes
|
||||
func extractPowerMetrics(attributes []*smart.SmartAttribute) (powerOnHours, powerCycles uint64) {
|
||||
for _, attr := range attributes {
|
||||
nameLower := strings.ToLower(attr.Name)
|
||||
if powerOnHours == 0 && (strings.Contains(nameLower, "poweronhours") || strings.Contains(nameLower, "power_on_hours")) {
|
||||
powerOnHours = attr.RawValue
|
||||
}
|
||||
if powerCycles == 0 && ((strings.Contains(nameLower, "power") && strings.Contains(nameLower, "cycle")) || strings.Contains(nameLower, "startstopcycles")) {
|
||||
powerCycles = attr.RawValue
|
||||
}
|
||||
if powerOnHours > 0 && powerCycles > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
75
internal/hub/systems/system_systemd_test.go
Normal file
75
internal/hub/systems/system_systemd_test.go
Normal file
@@ -0,0 +1,75 @@
|
||||
//go:build testing
|
||||
|
||||
package systems
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetSystemdServiceId(t *testing.T) {
|
||||
t.Run("deterministic output", func(t *testing.T) {
|
||||
systemId := "sys-123"
|
||||
serviceName := "nginx.service"
|
||||
|
||||
// Call multiple times and ensure same result
|
||||
id1 := makeStableHashId(systemId, serviceName)
|
||||
id2 := makeStableHashId(systemId, serviceName)
|
||||
id3 := makeStableHashId(systemId, serviceName)
|
||||
|
||||
assert.Equal(t, id1, id2)
|
||||
assert.Equal(t, id2, id3)
|
||||
assert.NotEmpty(t, id1)
|
||||
})
|
||||
|
||||
t.Run("different inputs produce different ids", func(t *testing.T) {
|
||||
systemId1 := "sys-123"
|
||||
systemId2 := "sys-456"
|
||||
serviceName1 := "nginx.service"
|
||||
serviceName2 := "apache.service"
|
||||
|
||||
id1 := makeStableHashId(systemId1, serviceName1)
|
||||
id2 := makeStableHashId(systemId2, serviceName1)
|
||||
id3 := makeStableHashId(systemId1, serviceName2)
|
||||
id4 := makeStableHashId(systemId2, serviceName2)
|
||||
|
||||
// All IDs should be different
|
||||
assert.NotEqual(t, id1, id2)
|
||||
assert.NotEqual(t, id1, id3)
|
||||
assert.NotEqual(t, id1, id4)
|
||||
assert.NotEqual(t, id2, id3)
|
||||
assert.NotEqual(t, id2, id4)
|
||||
assert.NotEqual(t, id3, id4)
|
||||
})
|
||||
|
||||
t.Run("consistent length", func(t *testing.T) {
|
||||
testCases := []struct {
|
||||
systemId string
|
||||
serviceName string
|
||||
}{
|
||||
{"short", "short.service"},
|
||||
{"very-long-system-id-that-might-be-used-in-practice", "very-long-service-name.service"},
|
||||
{"", "empty-system.service"},
|
||||
{"empty-service", ""},
|
||||
{"", ""},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
id := makeStableHashId(tc.systemId, tc.serviceName)
|
||||
// FNV-32 produces 8 hex characters
|
||||
assert.Len(t, id, 8, "ID should be 8 characters for systemId='%s', serviceName='%s'", tc.systemId, tc.serviceName)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("hexadecimal output", func(t *testing.T) {
|
||||
id := makeStableHashId("test-system", "test-service")
|
||||
assert.NotEmpty(t, id)
|
||||
|
||||
// Should only contain hexadecimal characters
|
||||
for _, char := range id {
|
||||
assert.True(t, (char >= '0' && char <= '9') || (char >= 'a' && char <= 'f'),
|
||||
"ID should only contain hexadecimal characters, got: %s", id)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -6,7 +6,9 @@ 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"
|
||||
)
|
||||
@@ -115,8 +117,46 @@ func (ws *WsConn) RequestContainerInfo(ctx context.Context, containerID string)
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// 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]any, error) {
|
||||
func (ws *WsConn) RequestSmartData(ctx context.Context) (map[string]smart.SmartData, error) {
|
||||
if !ws.IsConnected() {
|
||||
return nil, gws.ErrConnClosed
|
||||
}
|
||||
@@ -124,7 +164,7 @@ func (ws *WsConn) RequestSmartData(ctx context.Context) (map[string]any, error)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var result map[string]any
|
||||
var result map[string]smart.SmartData
|
||||
handler := ResponseHandler(&smartDataHandler{result: &result})
|
||||
if err := ws.handleAgentRequest(req, handler); err != nil {
|
||||
return nil, err
|
||||
@@ -135,19 +175,14 @@ func (ws *WsConn) RequestSmartData(ctx context.Context) (map[string]any, error)
|
||||
// smartDataHandler parses SMART data map from AgentResponse
|
||||
type smartDataHandler struct {
|
||||
BaseHandler
|
||||
result *map[string]any
|
||||
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")
|
||||
}
|
||||
// convert to map[string]any for transport convenience in hub layer
|
||||
out := make(map[string]any, len(agentResponse.SmartData))
|
||||
for k, v := range agentResponse.SmartData {
|
||||
out[k] = v
|
||||
}
|
||||
*h.result = out
|
||||
*h.result = agentResponse.SmartData
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
75
internal/hub/ws/handlers_test.go
Normal file
75
internal/hub/ws/handlers_test.go
Normal file
@@ -0,0 +1,75 @@
|
||||
//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())
|
||||
}
|
||||
@@ -75,6 +75,7 @@ func init() {
|
||||
"Disk",
|
||||
"Temperature",
|
||||
"Bandwidth",
|
||||
"GPU",
|
||||
"LoadAvg1",
|
||||
"LoadAvg5",
|
||||
"LoadAvg15"
|
||||
@@ -718,7 +719,9 @@ func init() {
|
||||
"type": "autodate"
|
||||
}
|
||||
],
|
||||
"indexes": [],
|
||||
"indexes": [
|
||||
"CREATE INDEX ` + "`" + `idx_systems_status` + "`" + ` ON ` + "`" + `systems` + "`" + ` (` + "`" + `status` + "`" + `)"
|
||||
],
|
||||
"system": false
|
||||
},
|
||||
{
|
||||
@@ -1005,6 +1008,436 @@ func init() {
|
||||
"CREATE INDEX ` + "`" + `idx_r3Ja0rs102` + "`" + ` ON ` + "`" + `containers` + "`" + ` (` + "`" + `system` + "`" + `)"
|
||||
],
|
||||
"system": false
|
||||
},
|
||||
{
|
||||
"createRule": null,
|
||||
"deleteRule": null,
|
||||
"fields": [
|
||||
{
|
||||
"autogeneratePattern": "[a-z0-9]{10}",
|
||||
"hidden": false,
|
||||
"id": "text3208210256",
|
||||
"max": 10,
|
||||
"min": 6,
|
||||
"name": "id",
|
||||
"pattern": "^[a-z0-9]+$",
|
||||
"presentable": false,
|
||||
"primaryKey": true,
|
||||
"required": true,
|
||||
"system": true,
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"autogeneratePattern": "",
|
||||
"hidden": false,
|
||||
"id": "text1579384326",
|
||||
"max": 0,
|
||||
"min": 0,
|
||||
"name": "name",
|
||||
"pattern": "",
|
||||
"presentable": false,
|
||||
"primaryKey": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"cascadeDelete": true,
|
||||
"collectionId": "2hz5ncl8tizk5nx",
|
||||
"hidden": false,
|
||||
"id": "relation3377271179",
|
||||
"maxSelect": 1,
|
||||
"minSelect": 0,
|
||||
"name": "system",
|
||||
"presentable": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "relation"
|
||||
},
|
||||
{
|
||||
"hidden": false,
|
||||
"id": "number2063623452",
|
||||
"max": null,
|
||||
"min": null,
|
||||
"name": "state",
|
||||
"onlyInt": true,
|
||||
"presentable": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"hidden": false,
|
||||
"id": "number1476559580",
|
||||
"max": null,
|
||||
"min": null,
|
||||
"name": "sub",
|
||||
"onlyInt": true,
|
||||
"presentable": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"hidden": false,
|
||||
"id": "number3128971310",
|
||||
"max": null,
|
||||
"min": null,
|
||||
"name": "cpu",
|
||||
"onlyInt": false,
|
||||
"presentable": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"hidden": false,
|
||||
"id": "number1052053287",
|
||||
"max": null,
|
||||
"min": null,
|
||||
"name": "cpuPeak",
|
||||
"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": "number1828797201",
|
||||
"max": null,
|
||||
"min": null,
|
||||
"name": "memPeak",
|
||||
"onlyInt": false,
|
||||
"presentable": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"hidden": false,
|
||||
"id": "number3332085495",
|
||||
"max": null,
|
||||
"min": null,
|
||||
"name": "updated",
|
||||
"onlyInt": false,
|
||||
"presentable": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "number"
|
||||
}
|
||||
],
|
||||
"id": "pbc_3494996990",
|
||||
"indexes": [
|
||||
"CREATE INDEX ` + "`" + `idx_4Z7LuLNdQb` + "`" + ` ON ` + "`" + `systemd_services` + "`" + ` (` + "`" + `system` + "`" + `)",
|
||||
"CREATE INDEX ` + "`" + `idx_pBp1fF837e` + "`" + ` ON ` + "`" + `systemd_services` + "`" + ` (` + "`" + `updated` + "`" + `)"
|
||||
],
|
||||
"listRule": "@request.auth.id != \"\" && system.users.id ?= @request.auth.id",
|
||||
"name": "systemd_services",
|
||||
"system": false,
|
||||
"type": "base",
|
||||
"updateRule": null,
|
||||
"viewRule": null
|
||||
},
|
||||
{
|
||||
"createRule": "@request.auth.id != \"\" && user.id = @request.auth.id",
|
||||
"deleteRule": "@request.auth.id != \"\" && user.id = @request.auth.id",
|
||||
"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": false,
|
||||
"system": false,
|
||||
"type": "relation"
|
||||
},
|
||||
{
|
||||
"cascadeDelete": true,
|
||||
"collectionId": "2hz5ncl8tizk5nx",
|
||||
"hidden": false,
|
||||
"id": "relation3377271179",
|
||||
"maxSelect": 1,
|
||||
"minSelect": 0,
|
||||
"name": "system",
|
||||
"presentable": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "relation"
|
||||
},
|
||||
{
|
||||
"hidden": false,
|
||||
"id": "select2844932856",
|
||||
"maxSelect": 1,
|
||||
"name": "type",
|
||||
"presentable": false,
|
||||
"required": true,
|
||||
"system": false,
|
||||
"type": "select",
|
||||
"values": [
|
||||
"one-time",
|
||||
"daily"
|
||||
]
|
||||
},
|
||||
{
|
||||
"hidden": false,
|
||||
"id": "date2675529103",
|
||||
"max": "",
|
||||
"min": "",
|
||||
"name": "start",
|
||||
"presentable": false,
|
||||
"required": true,
|
||||
"system": false,
|
||||
"type": "date"
|
||||
},
|
||||
{
|
||||
"hidden": false,
|
||||
"id": "date16528305",
|
||||
"max": "",
|
||||
"min": "",
|
||||
"name": "end",
|
||||
"presentable": false,
|
||||
"required": true,
|
||||
"system": false,
|
||||
"type": "date"
|
||||
}
|
||||
],
|
||||
"id": "pbc_451525641",
|
||||
"indexes": [
|
||||
"CREATE INDEX ` + "`" + `idx_q0iKnRP9v8` + "`" + ` ON ` + "`" + `quiet_hours` + "`" + ` (\n ` + "`" + `user` + "`" + `,\n ` + "`" + `system` + "`" + `\n)",
|
||||
"CREATE INDEX ` + "`" + `idx_6T7ljT7FJd` + "`" + ` ON ` + "`" + `quiet_hours` + "`" + ` (\n ` + "`" + `type` + "`" + `,\n ` + "`" + `end` + "`" + `\n)"
|
||||
],
|
||||
"listRule": "@request.auth.id != \"\" && user.id = @request.auth.id",
|
||||
"name": "quiet_hours",
|
||||
"system": false,
|
||||
"type": "base",
|
||||
"updateRule": "@request.auth.id != \"\" && user.id = @request.auth.id",
|
||||
"viewRule": "@request.auth.id != \"\" && user.id = @request.auth.id"
|
||||
},
|
||||
{
|
||||
"createRule": null,
|
||||
"deleteRule": "@request.auth.id != \"\" && system.users.id ?= @request.auth.id",
|
||||
"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": "2hz5ncl8tizk5nx",
|
||||
"hidden": false,
|
||||
"id": "relation3377271179",
|
||||
"maxSelect": 1,
|
||||
"minSelect": 0,
|
||||
"name": "system",
|
||||
"presentable": false,
|
||||
"required": true,
|
||||
"system": false,
|
||||
"type": "relation"
|
||||
},
|
||||
{
|
||||
"autogeneratePattern": "",
|
||||
"hidden": false,
|
||||
"id": "text1579384326",
|
||||
"max": 0,
|
||||
"min": 0,
|
||||
"name": "name",
|
||||
"pattern": "",
|
||||
"presentable": false,
|
||||
"primaryKey": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"autogeneratePattern": "",
|
||||
"hidden": false,
|
||||
"id": "text3616895705",
|
||||
"max": 0,
|
||||
"min": 0,
|
||||
"name": "model",
|
||||
"pattern": "",
|
||||
"presentable": false,
|
||||
"primaryKey": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"autogeneratePattern": "",
|
||||
"hidden": false,
|
||||
"id": "text2744374011",
|
||||
"max": 0,
|
||||
"min": 0,
|
||||
"name": "state",
|
||||
"pattern": "",
|
||||
"presentable": false,
|
||||
"primaryKey": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"hidden": false,
|
||||
"id": "number3051925876",
|
||||
"max": null,
|
||||
"min": null,
|
||||
"name": "capacity",
|
||||
"onlyInt": false,
|
||||
"presentable": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"hidden": false,
|
||||
"id": "number190023114",
|
||||
"max": null,
|
||||
"min": null,
|
||||
"name": "temp",
|
||||
"onlyInt": false,
|
||||
"presentable": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"autogeneratePattern": "",
|
||||
"hidden": false,
|
||||
"id": "text3589068740",
|
||||
"max": 0,
|
||||
"min": 0,
|
||||
"name": "firmware",
|
||||
"pattern": "",
|
||||
"presentable": false,
|
||||
"primaryKey": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"autogeneratePattern": "",
|
||||
"hidden": false,
|
||||
"id": "text3547646428",
|
||||
"max": 0,
|
||||
"min": 0,
|
||||
"name": "serial",
|
||||
"pattern": "",
|
||||
"presentable": false,
|
||||
"primaryKey": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"autogeneratePattern": "",
|
||||
"hidden": false,
|
||||
"id": "text2363381545",
|
||||
"max": 0,
|
||||
"min": 0,
|
||||
"name": "type",
|
||||
"pattern": "",
|
||||
"presentable": false,
|
||||
"primaryKey": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"hidden": false,
|
||||
"id": "number1234567890",
|
||||
"max": null,
|
||||
"min": null,
|
||||
"name": "hours",
|
||||
"onlyInt": true,
|
||||
"presentable": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"hidden": false,
|
||||
"id": "number0987654321",
|
||||
"max": null,
|
||||
"min": null,
|
||||
"name": "cycles",
|
||||
"onlyInt": true,
|
||||
"presentable": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"hidden": false,
|
||||
"id": "json832282224",
|
||||
"maxSize": 0,
|
||||
"name": "attributes",
|
||||
"presentable": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "json"
|
||||
},
|
||||
{
|
||||
"hidden": false,
|
||||
"id": "autodate3332085495",
|
||||
"name": "updated",
|
||||
"onCreate": true,
|
||||
"onUpdate": true,
|
||||
"presentable": false,
|
||||
"system": false,
|
||||
"type": "autodate"
|
||||
}
|
||||
],
|
||||
"id": "pbc_2571630677",
|
||||
"indexes": [
|
||||
"CREATE INDEX ` + "`" + `idx_DZ9yhvgl44` + "`" + ` ON ` + "`" + `smart_devices` + "`" + ` (` + "`" + `system` + "`" + `)"
|
||||
],
|
||||
"listRule": "@request.auth.id != \"\" && system.users.id ?= @request.auth.id",
|
||||
"name": "smart_devices",
|
||||
"system": false,
|
||||
"type": "base",
|
||||
"updateRule": null,
|
||||
"viewRule": "@request.auth.id != \"\" && system.users.id ?= @request.auth.id"
|
||||
}
|
||||
]`
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"github.com/henrygd/beszel/internal/entities/system"
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
m "github.com/pocketbase/pocketbase/migrations"
|
||||
)
|
||||
|
||||
// This can be deleted after Nov 2025 or so
|
||||
|
||||
func init() {
|
||||
m.Register(func(app core.App) error {
|
||||
app.RunInTransaction(func(txApp core.App) error {
|
||||
var systemIds []string
|
||||
txApp.DB().NewQuery("SELECT id FROM systems").Column(&systemIds)
|
||||
|
||||
for _, systemId := range systemIds {
|
||||
var statRecordIds []string
|
||||
txApp.DB().NewQuery("SELECT id FROM system_stats WHERE system = {:system} AND created > {:created}").Bind(map[string]any{"system": systemId, "created": "2025-09-21"}).Column(&statRecordIds)
|
||||
|
||||
for _, statRecordId := range statRecordIds {
|
||||
statRecord, err := txApp.FindRecordById("system_stats", statRecordId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var systemStats system.Stats
|
||||
err = statRecord.UnmarshalJSONField("stats", &systemStats)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// if mem buff cache is less than total mem, we don't need to fix it
|
||||
if systemStats.MemBuffCache < systemStats.Mem {
|
||||
continue
|
||||
}
|
||||
systemStats.MemBuffCache = 0
|
||||
statRecord.Set("stats", systemStats)
|
||||
err = txApp.SaveNoValidate(statRecord)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
return nil
|
||||
}, func(app core.App) error {
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -177,6 +177,10 @@ func (rm *RecordManager) AverageSystemStats(db dbx.Builder, records RecordIds) *
|
||||
stats := &tempStats
|
||||
// necessary because uint8 is not big enough for the sum
|
||||
batterySum := 0
|
||||
// accumulate per-core usage across records
|
||||
var cpuCoresSums []uint64
|
||||
// accumulate cpu breakdown [user, system, iowait, steal, idle]
|
||||
var cpuBreakdownSums []float64
|
||||
|
||||
count := float64(len(records))
|
||||
tempCount := float64(0)
|
||||
@@ -194,6 +198,15 @@ func (rm *RecordManager) AverageSystemStats(db dbx.Builder, records RecordIds) *
|
||||
}
|
||||
|
||||
sum.Cpu += stats.Cpu
|
||||
// accumulate cpu time breakdowns if present
|
||||
if stats.CpuBreakdown != nil {
|
||||
if len(cpuBreakdownSums) < len(stats.CpuBreakdown) {
|
||||
cpuBreakdownSums = append(cpuBreakdownSums, make([]float64, len(stats.CpuBreakdown)-len(cpuBreakdownSums))...)
|
||||
}
|
||||
for i, v := range stats.CpuBreakdown {
|
||||
cpuBreakdownSums[i] += v
|
||||
}
|
||||
}
|
||||
sum.Mem += stats.Mem
|
||||
sum.MemUsed += stats.MemUsed
|
||||
sum.MemPct += stats.MemPct
|
||||
@@ -217,6 +230,17 @@ func (rm *RecordManager) AverageSystemStats(db dbx.Builder, records RecordIds) *
|
||||
sum.DiskIO[1] += stats.DiskIO[1]
|
||||
batterySum += int(stats.Battery[0])
|
||||
sum.Battery[1] = stats.Battery[1]
|
||||
|
||||
// accumulate per-core usage if present
|
||||
if stats.CpuCoresUsage != nil {
|
||||
if len(cpuCoresSums) < len(stats.CpuCoresUsage) {
|
||||
// extend slices to accommodate core count
|
||||
cpuCoresSums = append(cpuCoresSums, make([]uint64, len(stats.CpuCoresUsage)-len(cpuCoresSums))...)
|
||||
}
|
||||
for i, v := range stats.CpuCoresUsage {
|
||||
cpuCoresSums[i] += uint64(v)
|
||||
}
|
||||
}
|
||||
// Set peak values
|
||||
sum.MaxCpu = max(sum.MaxCpu, stats.MaxCpu, stats.Cpu)
|
||||
sum.MaxMem = max(sum.MaxMem, stats.MaxMem, stats.MemUsed)
|
||||
@@ -385,6 +409,25 @@ func (rm *RecordManager) AverageSystemStats(db dbx.Builder, records RecordIds) *
|
||||
sum.GPUData[id] = gpu
|
||||
}
|
||||
}
|
||||
|
||||
// Average per-core usage
|
||||
if len(cpuCoresSums) > 0 {
|
||||
avg := make(system.Uint8Slice, len(cpuCoresSums))
|
||||
for i := range cpuCoresSums {
|
||||
v := math.Round(float64(cpuCoresSums[i]) / count)
|
||||
avg[i] = uint8(v)
|
||||
}
|
||||
sum.CpuCoresUsage = avg
|
||||
}
|
||||
|
||||
// Average CPU breakdown
|
||||
if len(cpuBreakdownSums) > 0 {
|
||||
avg := make([]float64, len(cpuBreakdownSums))
|
||||
for i := range cpuBreakdownSums {
|
||||
avg[i] = twoDecimals(cpuBreakdownSums[i] / count)
|
||||
}
|
||||
sum.CpuBreakdown = avg
|
||||
}
|
||||
}
|
||||
|
||||
return sum
|
||||
@@ -447,10 +490,18 @@ func (rm *RecordManager) DeleteOldRecords() {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = deleteOldSystemdServiceRecords(txApp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = deleteOldAlertsHistory(txApp, 200, 250)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = deleteOldQuietHours(txApp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -516,6 +567,20 @@ func deleteOldSystemStats(app core.App) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Deletes systemd service records that haven't been updated in the last 20 minutes
|
||||
func deleteOldSystemdServiceRecords(app core.App) error {
|
||||
now := time.Now().UTC()
|
||||
twentyMinutesAgo := now.Add(-20 * time.Minute)
|
||||
|
||||
// Delete systemd service records where updated < twentyMinutesAgo
|
||||
_, err := app.DB().NewQuery("DELETE FROM systemd_services WHERE updated < {:updated}").Bind(dbx.Params{"updated": twentyMinutesAgo.UnixMilli()}).Execute()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to delete old systemd service records: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Deletes container records that haven't been updated in the last 10 minutes
|
||||
func deleteOldContainerRecords(app core.App) error {
|
||||
now := time.Now().UTC()
|
||||
@@ -530,6 +595,17 @@ func deleteOldContainerRecords(app core.App) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Deletes old quiet hours records where end date has passed
|
||||
func deleteOldQuietHours(app core.App) error {
|
||||
now := time.Now().UTC()
|
||||
_, err := app.DB().NewQuery("DELETE FROM quiet_hours WHERE type = 'one-time' AND end < {:now}").Bind(dbx.Params{"now": now}).Execute()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
/* Round float to two decimals */
|
||||
func twoDecimals(value float64) float64 {
|
||||
return math.Round(value*100) / 100
|
||||
|
||||
@@ -351,6 +351,83 @@ func TestDeleteOldAlertsHistoryEdgeCases(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
// TestDeleteOldSystemdServiceRecords tests systemd service cleanup via DeleteOldRecords
|
||||
func TestDeleteOldSystemdServiceRecords(t *testing.T) {
|
||||
hub, err := tests.NewTestHub(t.TempDir())
|
||||
require.NoError(t, err)
|
||||
defer hub.Cleanup()
|
||||
|
||||
rm := records.NewRecordManager(hub)
|
||||
|
||||
// Create test user and system
|
||||
user, err := tests.CreateUser(hub, "test@example.com", "testtesttest")
|
||||
require.NoError(t, err)
|
||||
|
||||
system, err := tests.CreateRecord(hub, "systems", map[string]any{
|
||||
"name": "test-system",
|
||||
"host": "localhost",
|
||||
"port": "45876",
|
||||
"status": "up",
|
||||
"users": []string{user.Id},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
now := time.Now().UTC()
|
||||
|
||||
// Create old systemd service records that should be deleted (older than 20 minutes)
|
||||
oldRecord, err := tests.CreateRecord(hub, "systemd_services", map[string]any{
|
||||
"system": system.Id,
|
||||
"name": "nginx.service",
|
||||
"state": 0, // Active
|
||||
"sub": 1, // Running
|
||||
"cpu": 5.0,
|
||||
"cpuPeak": 10.0,
|
||||
"memory": 1024000,
|
||||
"memPeak": 2048000,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
// Set updated time to 25 minutes ago (should be deleted)
|
||||
oldRecord.SetRaw("updated", now.Add(-25*time.Minute).UnixMilli())
|
||||
err = hub.SaveNoValidate(oldRecord)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create recent systemd service record that should be kept (within 20 minutes)
|
||||
recentRecord, err := tests.CreateRecord(hub, "systemd_services", map[string]any{
|
||||
"system": system.Id,
|
||||
"name": "apache.service",
|
||||
"state": 1, // Inactive
|
||||
"sub": 0, // Dead
|
||||
"cpu": 2.0,
|
||||
"cpuPeak": 3.0,
|
||||
"memory": 512000,
|
||||
"memPeak": 1024000,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
// Set updated time to 10 minutes ago (should be kept)
|
||||
recentRecord.SetRaw("updated", now.Add(-10*time.Minute).UnixMilli())
|
||||
err = hub.SaveNoValidate(recentRecord)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Count records before deletion
|
||||
countBefore, err := hub.CountRecords("systemd_services")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int64(2), countBefore, "Should have 2 systemd service records initially")
|
||||
|
||||
// Run deletion via RecordManager
|
||||
rm.DeleteOldRecords()
|
||||
|
||||
// Count records after deletion
|
||||
countAfter, err := hub.CountRecords("systemd_services")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int64(1), countAfter, "Should have 1 systemd service record after deletion")
|
||||
|
||||
// Verify the correct record was kept
|
||||
remainingRecords, err := hub.FindRecordsByFilter("systemd_services", "", "", 10, 0, nil)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, remainingRecords, 1, "Should have exactly 1 record remaining")
|
||||
assert.Equal(t, "apache.service", remainingRecords[0].Get("name"), "The recent record should be kept")
|
||||
}
|
||||
|
||||
// TestRecordManagerCreation tests RecordManager creation
|
||||
func TestRecordManagerCreation(t *testing.T) {
|
||||
hub, err := tests.NewTestHub(t.TempDir())
|
||||
|
||||
@@ -17,6 +17,9 @@
|
||||
"enabled": true,
|
||||
"rules": {
|
||||
"recommended": true,
|
||||
"a11y": {
|
||||
"useButtonType": "off"
|
||||
},
|
||||
"complexity": {
|
||||
"noUselessStringConcat": "error",
|
||||
"noUselessUndefinedInitialization": "error",
|
||||
@@ -30,13 +33,17 @@
|
||||
"noUnusedFunctionParameters": "error",
|
||||
"noUnusedPrivateClassMembers": "error",
|
||||
"useExhaustiveDependencies": {
|
||||
"level": "error",
|
||||
"level": "warn",
|
||||
"options": {
|
||||
"reportUnnecessaryDependencies": false
|
||||
}
|
||||
},
|
||||
"useUniqueElementIds": "off",
|
||||
"noUnusedVariables": "error"
|
||||
},
|
||||
"security": {
|
||||
"noDangerouslySetInnerHtml": "warn"
|
||||
},
|
||||
"style": {
|
||||
"noParameterProperties": "error",
|
||||
"noYodaExpression": "error",
|
||||
@@ -47,7 +54,8 @@
|
||||
},
|
||||
"suspicious": {
|
||||
"useAwait": "error",
|
||||
"noEvolvingTypes": "error"
|
||||
"noEvolvingTypes": "error",
|
||||
"noArrayIndexKey": "off"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
1008
internal/site/bun.lock
Normal file
1008
internal/site/bun.lock
Normal file
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -2,7 +2,7 @@
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="manifest" href="./static/manifest.json" />
|
||||
<link rel="manifest" href="./static/manifest.json" crossorigin="use-credentials" />
|
||||
<link rel="icon" type="image/svg+xml" href="./static/icon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0,maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
|
||||
<meta name="robots" content="noindex, nofollow" />
|
||||
|
||||
317
internal/site/package-lock.json
generated
317
internal/site/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "beszel",
|
||||
"version": "0.15.2",
|
||||
"version": "0.17.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "beszel",
|
||||
"version": "0.15.2",
|
||||
"version": "0.17.0",
|
||||
"dependencies": {
|
||||
"@henrygd/queue": "^1.0.7",
|
||||
"@henrygd/semaphore": "^0.0.2",
|
||||
@@ -111,6 +111,7 @@
|
||||
"integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@ampproject/remapping": "^2.2.0",
|
||||
"@babel/code-frame": "^7.27.1",
|
||||
@@ -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",
|
||||
@@ -4807,9 +4776,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/js-yaml": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
||||
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
|
||||
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -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"
|
||||
},
|
||||
@@ -5735,6 +5749,7 @@
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-19.1.1.tgz",
|
||||
"integrity": "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -5744,6 +5759,7 @@
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.1.tgz",
|
||||
"integrity": "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"scheduler": "^0.26.0"
|
||||
},
|
||||
@@ -6171,6 +6187,16 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/string-width-cjs/node_modules/ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/string-width-cjs/node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
@@ -6191,16 +6217,6 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/string-width-cjs/node_modules/strip-ansi/node_modules/ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/stringify-entities": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz",
|
||||
@@ -6216,9 +6232,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/strip-ansi": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
|
||||
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz",
|
||||
"integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -6283,7 +6299,8 @@
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.12.tgz",
|
||||
"integrity": "sha512-DzFtxOi+7NsFf7DBtI3BJsynR+0Yp6etH+nRPTbpWnS2pZBaSksv/JGctNwSWzbFjp0vxSqknaUylseZqMDGrA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/tapable": {
|
||||
"version": "2.2.3",
|
||||
@@ -6405,6 +6422,7 @@
|
||||
"integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
@@ -6639,6 +6657,7 @@
|
||||
"integrity": "sha512-uzcxnSDVjAopEUjljkWh8EIrg6tlzrjFUfMcR1EVsRDGwf/ccef0qQPRyOrROwhrTDaApueq+ja+KLPlzR/zdg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.25.0",
|
||||
"fdir": "^6.5.0",
|
||||
@@ -6790,6 +6809,23 @@
|
||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs/node_modules/ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs/node_modules/string-width": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
@@ -6805,13 +6841,6 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs/node_modules/string-width/node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs/node_modules/strip-ansi": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
@@ -6825,20 +6854,10 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs/node_modules/strip-ansi/node_modules/ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi/node_modules/ansi-styles": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
|
||||
"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
|
||||
"version": "6.2.3",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
|
||||
"integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "beszel",
|
||||
"private": true,
|
||||
"version": "0.15.2",
|
||||
"version": "0.17.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite --host",
|
||||
|
||||
@@ -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,6 +36,9 @@ 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) {
|
||||
@@ -45,10 +48,7 @@ export function AddSystemButton({ className }: { className?: string }) {
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
className={cn("flex gap-1 max-xs:h-[2.4rem]", className, isReadOnlyUser() && "hidden")}
|
||||
>
|
||||
<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>
|
||||
@@ -124,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"
|
||||
@@ -134,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>
|
||||
|
||||
@@ -11,12 +11,14 @@ import {
|
||||
import { chartMargin, cn, formatShortDate } from "@/lib/utils"
|
||||
import type { ChartData, SystemStatsRecord } from "@/types"
|
||||
import { useYAxisWidth } from "./hooks"
|
||||
import { AxisDomain } from "recharts/types/util/types"
|
||||
|
||||
export type DataPoint = {
|
||||
label: string
|
||||
dataKey: (data: SystemStatsRecord) => number | undefined
|
||||
color: number | string
|
||||
opacity: number
|
||||
stackId?: string | number
|
||||
}
|
||||
|
||||
export default function AreaChartDefault({
|
||||
@@ -29,19 +31,25 @@ export default function AreaChartDefault({
|
||||
domain,
|
||||
legend,
|
||||
itemSorter,
|
||||
showTotal = false,
|
||||
reverseStackOrder = false,
|
||||
hideYAxis = false,
|
||||
}: // logRender = false,
|
||||
{
|
||||
chartData: ChartData
|
||||
max?: number
|
||||
maxToggled?: boolean
|
||||
tickFormatter: (value: number, index: number) => string
|
||||
contentFormatter: ({ value, payload }: { value: number; payload: SystemStatsRecord }) => string
|
||||
dataPoints?: DataPoint[]
|
||||
domain?: [number, number]
|
||||
legend?: boolean
|
||||
itemSorter?: (a: any, b: any) => number
|
||||
// logRender?: boolean
|
||||
}) {
|
||||
{
|
||||
chartData: ChartData
|
||||
max?: number
|
||||
maxToggled?: boolean
|
||||
tickFormatter: (value: number, index: number) => string
|
||||
contentFormatter: ({ value, payload }: { value: number; payload: SystemStatsRecord }) => string
|
||||
dataPoints?: DataPoint[]
|
||||
domain?: AxisDomain
|
||||
legend?: boolean
|
||||
showTotal?: boolean
|
||||
itemSorter?: (a: any, b: any) => number
|
||||
reverseStackOrder?: boolean
|
||||
hideYAxis?: boolean
|
||||
// logRender?: boolean
|
||||
}) {
|
||||
const { yAxisWidth, updateYAxisWidth } = useYAxisWidth()
|
||||
|
||||
// biome-ignore lint/correctness/useExhaustiveDependencies: ignore
|
||||
@@ -56,21 +64,29 @@ export default function AreaChartDefault({
|
||||
<div>
|
||||
<ChartContainer
|
||||
className={cn("h-full w-full absolute aspect-auto bg-card opacity-0 transition-opacity", {
|
||||
"opacity-100": yAxisWidth,
|
||||
"opacity-100": yAxisWidth || hideYAxis,
|
||||
"ps-4": hideYAxis,
|
||||
})}
|
||||
>
|
||||
<AreaChart accessibilityLayer data={chartData.systemStats} margin={chartMargin}>
|
||||
<AreaChart
|
||||
reverseStackOrder={reverseStackOrder}
|
||||
accessibilityLayer
|
||||
data={chartData.systemStats}
|
||||
margin={hideYAxis ? { ...chartMargin, left: 5 } : chartMargin}
|
||||
>
|
||||
<CartesianGrid vertical={false} />
|
||||
<YAxis
|
||||
direction="ltr"
|
||||
orientation={chartData.orientation}
|
||||
className="tracking-tighter"
|
||||
width={yAxisWidth}
|
||||
domain={domain ?? [0, max ?? "auto"]}
|
||||
tickFormatter={(value, index) => updateYAxisWidth(tickFormatter(value, index))}
|
||||
tickLine={false}
|
||||
axisLine={false}
|
||||
/>
|
||||
{!hideYAxis && (
|
||||
<YAxis
|
||||
direction="ltr"
|
||||
orientation={chartData.orientation}
|
||||
className="tracking-tighter"
|
||||
width={yAxisWidth}
|
||||
domain={domain ?? [0, max ?? "auto"]}
|
||||
tickFormatter={(value, index) => updateYAxisWidth(tickFormatter(value, index))}
|
||||
tickLine={false}
|
||||
axisLine={false}
|
||||
/>
|
||||
)}
|
||||
{xAxis(chartData)}
|
||||
<ChartTooltip
|
||||
animationEasing="ease-out"
|
||||
@@ -81,6 +97,7 @@ export default function AreaChartDefault({
|
||||
<ChartTooltipContent
|
||||
labelFormatter={(_, data) => formatShortDate(data[0].payload.created)}
|
||||
contentFormatter={contentFormatter}
|
||||
showTotal={showTotal}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
@@ -99,13 +116,14 @@ export default function AreaChartDefault({
|
||||
fillOpacity={dataPoint.opacity}
|
||||
stroke={color}
|
||||
isAnimationActive={false}
|
||||
stackId={dataPoint.stackId}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
{legend && <ChartLegend content={<ChartLegendContent />} />}
|
||||
{legend && <ChartLegend content={<ChartLegendContent reverse={reverseStackOrder} />} />}
|
||||
</AreaChart>
|
||||
</ChartContainer>
|
||||
</div>
|
||||
)
|
||||
}, [chartData.systemStats.at(-1), yAxisWidth, maxToggled])
|
||||
}, [chartData.systemStats.at(-1), yAxisWidth, maxToggled, showTotal])
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { useStore } from "@nanostores/react"
|
||||
import { memo, useMemo } from "react"
|
||||
import { Area, AreaChart, CartesianGrid, YAxis } from "recharts"
|
||||
import { type ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent, xAxis } from "@/components/ui/chart"
|
||||
import { type ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent, pinnedAxisDomain, xAxis } from "@/components/ui/chart"
|
||||
import { ChartType, Unit } from "@/lib/enums"
|
||||
import { $containerFilter, $userSettings } from "@/lib/stores"
|
||||
import { chartMargin, cn, decimalString, formatBytes, formatShortDate, toFixedFloat } from "@/lib/utils"
|
||||
@@ -41,7 +41,7 @@ export default memo(function ContainerChart({
|
||||
// tick formatter
|
||||
if (chartType === ChartType.CPU) {
|
||||
obj.tickFormatter = (value) => {
|
||||
const val = toFixedFloat(value, 2) + unit
|
||||
const val = `${toFixedFloat(value, 2)}%`
|
||||
return updateYAxisWidth(val)
|
||||
}
|
||||
} else {
|
||||
@@ -78,7 +78,7 @@ export default memo(function ContainerChart({
|
||||
return `${decimalString(value)} ${unit}`
|
||||
}
|
||||
} else {
|
||||
obj.toolTipFormatter = (item: any) => `${decimalString(item.value)} ${unit}`
|
||||
obj.toolTipFormatter = (item: any) => `${decimalString(item.value)}${unit}`
|
||||
}
|
||||
// data function
|
||||
if (isNetChart) {
|
||||
@@ -124,6 +124,7 @@ export default memo(function ContainerChart({
|
||||
<CartesianGrid vertical={false} />
|
||||
<YAxis
|
||||
direction="ltr"
|
||||
domain={pinnedAxisDomain()}
|
||||
orientation={chartData.orientation}
|
||||
className="tracking-tighter"
|
||||
width={yAxisWidth}
|
||||
@@ -139,7 +140,7 @@ export default memo(function ContainerChart({
|
||||
labelFormatter={(_, data) => formatShortDate(data[0].payload.created)}
|
||||
// @ts-expect-error
|
||||
itemSorter={(a, b) => b.value - a.value}
|
||||
content={<ChartTooltipContent filter={filter} contentFormatter={toolTipFormatter} />}
|
||||
content={<ChartTooltipContent filter={filter} contentFormatter={toolTipFormatter} showTotal={true} />}
|
||||
/>
|
||||
{Object.keys(chartConfig).map((key) => {
|
||||
const filtered = filteredKeys.has(key)
|
||||
|
||||
@@ -69,7 +69,7 @@ export function useContainerChartConfigs(containerData: ChartData["containerData
|
||||
const hue = ((i * 360) / count) % 360
|
||||
chartConfig[containerName] = {
|
||||
label: containerName,
|
||||
color: `hsl(${hue}, 60%, 55%)`,
|
||||
color: `hsl(${hue}, var(--chart-saturation), var(--chart-lightness))`,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@ export default memo(function MemChart({ chartData, showMax }: { chartData: Chart
|
||||
const { value: convertedValue, unit } = formatBytes(value * 1024, false, Unit.Bytes, true)
|
||||
return decimalString(convertedValue, convertedValue >= 100 ? 1 : 2) + " " + unit
|
||||
}}
|
||||
showTotal={true}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -8,10 +8,11 @@ import {
|
||||
ContainerIcon,
|
||||
DatabaseBackupIcon,
|
||||
FingerprintIcon,
|
||||
LayoutDashboard,
|
||||
HardDriveIcon,
|
||||
LogsIcon,
|
||||
MailIcon,
|
||||
Server,
|
||||
ServerIcon,
|
||||
SettingsIcon,
|
||||
UsersIcon,
|
||||
} from "lucide-react"
|
||||
@@ -81,15 +82,15 @@ export default memo(function CommandPalette({ open, setOpen }: { open: boolean;
|
||||
)}
|
||||
<CommandGroup heading={t`Pages / Settings`}>
|
||||
<CommandItem
|
||||
keywords={["home", t`All Systems`]}
|
||||
keywords={["home"]}
|
||||
onSelect={() => {
|
||||
navigate(basePath)
|
||||
setOpen(false)
|
||||
}}
|
||||
>
|
||||
<LayoutDashboard className="me-2 size-4" />
|
||||
<ServerIcon className="me-2 size-4" />
|
||||
<span>
|
||||
<Trans>Dashboard</Trans>
|
||||
<Trans>All Systems</Trans>
|
||||
</span>
|
||||
<CommandShortcut>
|
||||
<Trans>Page</Trans>
|
||||
@@ -109,6 +110,18 @@ export default memo(function CommandPalette({ open, setOpen }: { open: boolean;
|
||||
<Trans>Page</Trans>
|
||||
</CommandShortcut>
|
||||
</CommandItem>
|
||||
<CommandItem
|
||||
onSelect={() => {
|
||||
navigate(getPagePath($router, "smart"))
|
||||
setOpen(false)
|
||||
}}
|
||||
>
|
||||
<HardDriveIcon className="me-2 size-4" />
|
||||
<span>S.M.A.R.T.</span>
|
||||
<CommandShortcut>
|
||||
<Trans>Page</Trans>
|
||||
</CommandShortcut>
|
||||
</CommandItem>
|
||||
<CommandItem
|
||||
onSelect={() => {
|
||||
navigate(getPagePath($router, "settings", { name: "general" }))
|
||||
|
||||
@@ -26,7 +26,7 @@ import { Sheet, SheetTitle, SheetHeader, SheetContent, SheetDescription } from "
|
||||
import { Dialog, DialogContent, DialogTitle } from "../ui/dialog"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { $allSystemsById } from "@/lib/stores"
|
||||
import { MaximizeIcon, RefreshCwIcon } from "lucide-react"
|
||||
import { LoaderCircleIcon, MaximizeIcon, RefreshCwIcon, XIcon } from "lucide-react"
|
||||
import { Separator } from "../ui/separator"
|
||||
import { $router, Link } from "../router"
|
||||
import { listenKeys } from "nanostores"
|
||||
@@ -35,7 +35,8 @@ import { getPagePath } from "@nanostores/router"
|
||||
const syntaxTheme = "github-dark-dimmed"
|
||||
|
||||
export default function ContainersTable({ systemId }: { systemId?: string }) {
|
||||
const [data, setData] = useState<ContainerRecord[]>([])
|
||||
const loadTime = Date.now()
|
||||
const [data, setData] = useState<ContainerRecord[] | undefined>(undefined)
|
||||
const [sorting, setSorting] = useBrowserStorage<SortingState>(
|
||||
`sort-c-${systemId ? 1 : 0}`,
|
||||
[{ id: systemId ? "name" : "system", desc: false }],
|
||||
@@ -47,56 +48,57 @@ export default function ContainersTable({ systemId }: { systemId?: string }) {
|
||||
const [globalFilter, setGlobalFilter] = useState("")
|
||||
|
||||
useEffect(() => {
|
||||
const pbOptions = {
|
||||
fields: "id,name,image,cpu,memory,net,health,status,system,updated",
|
||||
}
|
||||
|
||||
const fetchData = (lastXMs: number) => {
|
||||
const updated = Date.now() - lastXMs
|
||||
let filter: string
|
||||
if (systemId) {
|
||||
filter = pb.filter("system={:system} && updated > {:updated}", { system: systemId, updated })
|
||||
} else {
|
||||
filter = pb.filter("updated > {:updated}", { updated })
|
||||
}
|
||||
function fetchData(systemId?: string) {
|
||||
pb.collection<ContainerRecord>("containers")
|
||||
.getList(0, 2000, {
|
||||
...pbOptions,
|
||||
filter,
|
||||
fields: "id,name,image,cpu,memory,net,health,status,system,updated",
|
||||
filter: systemId ? pb.filter("system={:system}", { system: systemId }) : undefined,
|
||||
})
|
||||
.then(({ items }) => setData((curItems) => {
|
||||
const containerIds = new Set(items.map(item => item.id))
|
||||
const now = Date.now()
|
||||
for (const item of curItems) {
|
||||
if (!containerIds.has(item.id) && now - item.updated < 70_000) {
|
||||
items.push(item)
|
||||
}
|
||||
}
|
||||
return items
|
||||
}))
|
||||
.then(
|
||||
({ items }) =>
|
||||
items.length &&
|
||||
setData((curItems) => {
|
||||
const lastUpdated = Math.max(items[0].updated, items.at(-1)?.updated ?? 0)
|
||||
const containerIds = new Set()
|
||||
const newItems = []
|
||||
for (const item of items) {
|
||||
if (Math.abs(lastUpdated - item.updated) < 70_000) {
|
||||
containerIds.add(item.id)
|
||||
newItems.push(item)
|
||||
}
|
||||
}
|
||||
for (const item of curItems ?? []) {
|
||||
if (!containerIds.has(item.id) && lastUpdated - item.updated < 70_000) {
|
||||
newItems.push(item)
|
||||
}
|
||||
}
|
||||
return newItems
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
// initial load
|
||||
fetchData(70_000)
|
||||
fetchData(systemId)
|
||||
|
||||
// if no systemId, poll every 10 seconds
|
||||
// if no systemId, pull system containers after every system update
|
||||
if (!systemId) {
|
||||
// poll every 10 seconds
|
||||
const intervalId = setInterval(() => fetchData(10_500), 10_000)
|
||||
// clear interval on unmount
|
||||
return () => clearInterval(intervalId)
|
||||
return $allSystemsById.listen((_value, _oldValue, systemId) => {
|
||||
// exclude initial load of systems
|
||||
if (Date.now() - loadTime > 500) {
|
||||
fetchData(systemId)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// if systemId, fetch containers after the system is updated
|
||||
return listenKeys($allSystemsById, [systemId], (_newSystems) => {
|
||||
const changeTime = Date.now()
|
||||
setTimeout(() => fetchData(Date.now() - changeTime + 1000), 100)
|
||||
fetchData(systemId)
|
||||
})
|
||||
}, [])
|
||||
|
||||
const table = useReactTable({
|
||||
data,
|
||||
columns: containerChartCols.filter(col => systemId ? col.id !== "system" : true),
|
||||
data: data ?? [],
|
||||
columns: containerChartCols.filter((col) => (systemId ? col.id !== "system" : true)),
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
@@ -149,92 +151,113 @@ export default function ContainersTable({ systemId }: { systemId?: string }) {
|
||||
<Trans>Click on a container to view more information.</Trans>
|
||||
</CardDescription>
|
||||
</div>
|
||||
<Input
|
||||
placeholder={t`Filter...`}
|
||||
value={globalFilter}
|
||||
onChange={(e) => setGlobalFilter(e.target.value)}
|
||||
className="ms-auto px-4 w-full max-w-full md:w-64"
|
||||
/>
|
||||
<div className="relative ms-auto w-full max-w-full md:w-64">
|
||||
<Input
|
||||
placeholder={t`Filter...`}
|
||||
value={globalFilter}
|
||||
onChange={(e) => setGlobalFilter(e.target.value)}
|
||||
className="ps-4 pe-10 w-full"
|
||||
/>
|
||||
{globalFilter && (
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
aria-label={t`Clear`}
|
||||
className="absolute right-1 top-1/2 -translate-y-1/2 h-7 w-7 text-muted-foreground"
|
||||
onClick={() => setGlobalFilter("")}
|
||||
>
|
||||
<XIcon className="h-4 w-4" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<div className="rounded-md">
|
||||
<AllContainersTable table={table} rows={rows} colLength={visibleColumns.length} />
|
||||
<AllContainersTable table={table} rows={rows} colLength={visibleColumns.length} data={data} />
|
||||
</div>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
const AllContainersTable = memo(
|
||||
function AllContainersTable({ table, rows, colLength }: { table: TableType<ContainerRecord>; rows: Row<ContainerRecord>[]; colLength: number }) {
|
||||
// The virtualizer will need a reference to the scrollable container element
|
||||
const scrollRef = useRef<HTMLDivElement>(null)
|
||||
const activeContainer = useRef<ContainerRecord | null>(null)
|
||||
const [sheetOpen, setSheetOpen] = useState(false)
|
||||
const openSheet = (container: ContainerRecord) => {
|
||||
activeContainer.current = container
|
||||
setSheetOpen(true)
|
||||
}
|
||||
|
||||
const virtualizer = useVirtualizer<HTMLDivElement, HTMLTableRowElement>({
|
||||
count: rows.length,
|
||||
estimateSize: () => 54,
|
||||
getScrollElement: () => scrollRef.current,
|
||||
overscan: 5,
|
||||
})
|
||||
const virtualRows = virtualizer.getVirtualItems()
|
||||
|
||||
const paddingTop = Math.max(0, virtualRows[0]?.start ?? 0 - virtualizer.options.scrollMargin)
|
||||
const paddingBottom = Math.max(0, virtualizer.getTotalSize() - (virtualRows[virtualRows.length - 1]?.end ?? 0))
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"h-min max-h-[calc(100dvh-17rem)] max-w-full relative overflow-auto border rounded-md",
|
||||
// don't set min height if there are less than 2 rows, do set if we need to display the empty state
|
||||
(!rows.length || rows.length > 2) && "min-h-50"
|
||||
)}
|
||||
ref={scrollRef}
|
||||
>
|
||||
{/* add header height to table size */}
|
||||
<div style={{ height: `${virtualizer.getTotalSize() + 48}px`, paddingTop, paddingBottom }}>
|
||||
<table className="text-sm w-full h-full text-nowrap">
|
||||
<ContainersTableHead table={table} />
|
||||
<TableBody>
|
||||
{rows.length ? (
|
||||
virtualRows.map((virtualRow) => {
|
||||
const row = rows[virtualRow.index]
|
||||
return (
|
||||
<ContainerTableRow
|
||||
key={row.id}
|
||||
row={row}
|
||||
virtualRow={virtualRow}
|
||||
openSheet={openSheet}
|
||||
/>
|
||||
)
|
||||
})
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell colSpan={colLength} className="h-37 text-center pointer-events-none">
|
||||
<Trans>No results.</Trans>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</table>
|
||||
</div>
|
||||
<ContainerSheet sheetOpen={sheetOpen} setSheetOpen={setSheetOpen} activeContainer={activeContainer} />
|
||||
</div>
|
||||
)
|
||||
const AllContainersTable = memo(function AllContainersTable({
|
||||
table,
|
||||
rows,
|
||||
colLength,
|
||||
data,
|
||||
}: {
|
||||
table: TableType<ContainerRecord>
|
||||
rows: Row<ContainerRecord>[]
|
||||
colLength: number
|
||||
data: ContainerRecord[] | undefined
|
||||
}) {
|
||||
// The virtualizer will need a reference to the scrollable container element
|
||||
const scrollRef = useRef<HTMLDivElement>(null)
|
||||
const activeContainer = useRef<ContainerRecord | null>(null)
|
||||
const [sheetOpen, setSheetOpen] = useState(false)
|
||||
const openSheet = (container: ContainerRecord) => {
|
||||
activeContainer.current = container
|
||||
setSheetOpen(true)
|
||||
}
|
||||
)
|
||||
|
||||
const virtualizer = useVirtualizer<HTMLDivElement, HTMLTableRowElement>({
|
||||
count: rows.length,
|
||||
estimateSize: () => 54,
|
||||
getScrollElement: () => scrollRef.current,
|
||||
overscan: 5,
|
||||
})
|
||||
const virtualRows = virtualizer.getVirtualItems()
|
||||
|
||||
const paddingTop = Math.max(0, virtualRows[0]?.start ?? 0 - virtualizer.options.scrollMargin)
|
||||
const paddingBottom = Math.max(0, virtualizer.getTotalSize() - (virtualRows[virtualRows.length - 1]?.end ?? 0))
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"h-min max-h-[calc(100dvh-17rem)] max-w-full relative overflow-auto border rounded-md",
|
||||
// don't set min height if there are less than 2 rows, do set if we need to display the empty state
|
||||
(!rows.length || rows.length > 2) && "min-h-50"
|
||||
)}
|
||||
ref={scrollRef}
|
||||
>
|
||||
{/* add header height to table size */}
|
||||
<div style={{ height: `${virtualizer.getTotalSize() + 48}px`, paddingTop, paddingBottom }}>
|
||||
<table className="text-sm w-full h-full text-nowrap">
|
||||
<ContainersTableHead table={table} />
|
||||
<TableBody>
|
||||
{rows.length ? (
|
||||
virtualRows.map((virtualRow) => {
|
||||
const row = rows[virtualRow.index]
|
||||
return <ContainerTableRow key={row.id} row={row} virtualRow={virtualRow} openSheet={openSheet} />
|
||||
})
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell colSpan={colLength} className="h-37 text-center pointer-events-none">
|
||||
{data ? (
|
||||
<Trans>No results.</Trans>
|
||||
) : (
|
||||
<LoaderCircleIcon className="animate-spin size-10 opacity-60 mx-auto" />
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</table>
|
||||
</div>
|
||||
<ContainerSheet sheetOpen={sheetOpen} setSheetOpen={setSheetOpen} activeContainer={activeContainer} />
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
async function getLogsHtml(container: ContainerRecord): Promise<string> {
|
||||
try {
|
||||
const [{ highlighter }, logsHtml] = await Promise.all([import('@/lib/shiki'), pb.send<{ logs: string }>("/api/beszel/containers/logs", {
|
||||
system: container.system,
|
||||
container: container.id,
|
||||
})])
|
||||
const [{ highlighter }, logsHtml] = await Promise.all([
|
||||
import("@/lib/shiki"),
|
||||
pb.send<{ logs: string }>("/api/beszel/containers/logs", {
|
||||
system: container.system,
|
||||
container: container.id,
|
||||
}),
|
||||
])
|
||||
return logsHtml.logs ? highlighter.codeToHtml(logsHtml.logs, { lang: "log", theme: syntaxTheme }) : t`No results.`
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
@@ -244,13 +267,16 @@ async function getLogsHtml(container: ContainerRecord): Promise<string> {
|
||||
|
||||
async function getInfoHtml(container: ContainerRecord): Promise<string> {
|
||||
try {
|
||||
let [{ highlighter }, { info }] = await Promise.all([import('@/lib/shiki'), pb.send<{ info: string }>("/api/beszel/containers/info", {
|
||||
system: container.system,
|
||||
container: container.id,
|
||||
})])
|
||||
let [{ highlighter }, { info }] = await Promise.all([
|
||||
import("@/lib/shiki"),
|
||||
pb.send<{ info: string }>("/api/beszel/containers/info", {
|
||||
system: container.system,
|
||||
container: container.id,
|
||||
}),
|
||||
])
|
||||
try {
|
||||
info = JSON.stringify(JSON.parse(info), null, 2)
|
||||
} catch (_) { }
|
||||
} catch (_) {}
|
||||
return info ? highlighter.codeToHtml(info, { lang: "json", theme: syntaxTheme }) : t`No results.`
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
@@ -258,7 +284,15 @@ async function getInfoHtml(container: ContainerRecord): Promise<string> {
|
||||
}
|
||||
}
|
||||
|
||||
function ContainerSheet({ sheetOpen, setSheetOpen, activeContainer }: { sheetOpen: boolean, setSheetOpen: (open: boolean) => void, activeContainer: RefObject<ContainerRecord | null> }) {
|
||||
function ContainerSheet({
|
||||
sheetOpen,
|
||||
setSheetOpen,
|
||||
activeContainer,
|
||||
}: {
|
||||
sheetOpen: boolean
|
||||
setSheetOpen: (open: boolean) => void
|
||||
activeContainer: RefObject<ContainerRecord | null>
|
||||
}) {
|
||||
const container = activeContainer.current
|
||||
if (!container) return null
|
||||
|
||||
@@ -297,9 +331,9 @@ function ContainerSheet({ sheetOpen, setSheetOpen, activeContainer }: { sheetOpe
|
||||
|
||||
useEffect(() => {
|
||||
setLogsDisplay("")
|
||||
setInfoDisplay("");
|
||||
setInfoDisplay("")
|
||||
if (!container) return
|
||||
(async () => {
|
||||
;(async () => {
|
||||
const [logsHtml, infoHtml] = await Promise.all([getLogsHtml(container), getInfoHtml(container)])
|
||||
setLogsDisplay(logsHtml)
|
||||
setInfoDisplay(infoHtml)
|
||||
@@ -328,7 +362,9 @@ function ContainerSheet({ sheetOpen, setSheetOpen, activeContainer }: { sheetOpe
|
||||
<SheetHeader>
|
||||
<SheetTitle>{container.name}</SheetTitle>
|
||||
<SheetDescription className="flex flex-wrap items-center gap-x-2 gap-y-1">
|
||||
<Link className="hover:underline" href={getPagePath($router, "system", { id: container.system })}>{$allSystemsById.get()[container.system]?.name ?? ""}</Link>
|
||||
<Link className="hover:underline" href={getPagePath($router, "system", { id: container.system })}>
|
||||
{$allSystemsById.get()[container.system]?.name ?? ""}
|
||||
</Link>
|
||||
<Separator orientation="vertical" className="h-2.5 bg-muted-foreground opacity-70" />
|
||||
{container.status}
|
||||
<Separator orientation="vertical" className="h-2.5 bg-muted-foreground opacity-70" />
|
||||
@@ -350,19 +386,20 @@ function ContainerSheet({ sheetOpen, setSheetOpen, activeContainer }: { sheetOpe
|
||||
disabled={isRefreshingLogs}
|
||||
>
|
||||
<RefreshCwIcon
|
||||
className={`size-4 transition-transform duration-300 ${isRefreshingLogs ? 'animate-spin' : ''}`}
|
||||
className={`size-4 transition-transform duration-300 ${isRefreshingLogs ? "animate-spin" : ""}`}
|
||||
/>
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => setLogsFullscreenOpen(true)}
|
||||
className="h-8 w-8 p-0"
|
||||
>
|
||||
<Button variant="ghost" size="sm" onClick={() => setLogsFullscreenOpen(true)} className="h-8 w-8 p-0">
|
||||
<MaximizeIcon className="size-4" />
|
||||
</Button>
|
||||
</div>
|
||||
<div ref={logsContainerRef} className={cn("max-h-[calc(50dvh-10rem)] w-full overflow-auto p-3 rounded-md bg-gh-dark text-white text-sm", !logsDisplay && ["animate-pulse", "h-full"])}>
|
||||
<div
|
||||
ref={logsContainerRef}
|
||||
className={cn(
|
||||
"max-h-[calc(50dvh-10rem)] w-full overflow-auto p-3 rounded-md bg-gh-dark text-white text-sm",
|
||||
!logsDisplay && ["animate-pulse", "h-full"]
|
||||
)}
|
||||
>
|
||||
<div dangerouslySetInnerHTML={{ __html: logsDisplay }} />
|
||||
</div>
|
||||
<div className="flex items-center w-full">
|
||||
@@ -376,15 +413,18 @@ function ContainerSheet({ sheetOpen, setSheetOpen, activeContainer }: { sheetOpe
|
||||
<MaximizeIcon className="size-4" />
|
||||
</Button>
|
||||
</div>
|
||||
<div className={cn("grow h-[calc(50dvh-4rem)] w-full overflow-auto p-3 rounded-md bg-gh-dark text-white text-sm", !infoDisplay && "animate-pulse")}>
|
||||
<div
|
||||
className={cn(
|
||||
"grow h-[calc(50dvh-4rem)] w-full overflow-auto p-3 rounded-md bg-gh-dark text-white text-sm",
|
||||
!infoDisplay && "animate-pulse"
|
||||
)}
|
||||
>
|
||||
<div dangerouslySetInnerHTML={{ __html: infoDisplay }} />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
</>
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
@@ -406,39 +446,51 @@ function ContainersTableHead({ table }: { table: TableType<ContainerRecord> }) {
|
||||
)
|
||||
}
|
||||
|
||||
const ContainerTableRow = memo(
|
||||
function ContainerTableRow({
|
||||
row,
|
||||
virtualRow,
|
||||
openSheet,
|
||||
}: {
|
||||
row: Row<ContainerRecord>
|
||||
virtualRow: VirtualItem
|
||||
openSheet: (container: ContainerRecord) => void
|
||||
}) {
|
||||
return (
|
||||
<TableRow
|
||||
data-state={row.getIsSelected() && "selected"}
|
||||
className="cursor-pointer transition-opacity"
|
||||
onClick={() => openSheet(row.original)}
|
||||
>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<TableCell
|
||||
key={cell.id}
|
||||
className="py-0"
|
||||
style={{
|
||||
height: virtualRow.size,
|
||||
}}
|
||||
>
|
||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
)
|
||||
}
|
||||
)
|
||||
const ContainerTableRow = memo(function ContainerTableRow({
|
||||
row,
|
||||
virtualRow,
|
||||
openSheet,
|
||||
}: {
|
||||
row: Row<ContainerRecord>
|
||||
virtualRow: VirtualItem
|
||||
openSheet: (container: ContainerRecord) => void
|
||||
}) {
|
||||
return (
|
||||
<TableRow
|
||||
data-state={row.getIsSelected() && "selected"}
|
||||
className="cursor-pointer transition-opacity"
|
||||
onClick={() => openSheet(row.original)}
|
||||
>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<TableCell
|
||||
key={cell.id}
|
||||
className="py-0"
|
||||
style={{
|
||||
height: virtualRow.size,
|
||||
}}
|
||||
>
|
||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
)
|
||||
})
|
||||
|
||||
function LogsFullscreenDialog({ open, onOpenChange, logsDisplay, containerName, onRefresh, isRefreshing }: { open: boolean, onOpenChange: (open: boolean) => void, logsDisplay: string, containerName: string, onRefresh: () => void | Promise<void>, isRefreshing: boolean }) {
|
||||
function LogsFullscreenDialog({
|
||||
open,
|
||||
onOpenChange,
|
||||
logsDisplay,
|
||||
containerName,
|
||||
onRefresh,
|
||||
isRefreshing,
|
||||
}: {
|
||||
open: boolean
|
||||
onOpenChange: (open: boolean) => void
|
||||
logsDisplay: string
|
||||
containerName: string
|
||||
onRefresh: () => void | Promise<void>
|
||||
isRefreshing: boolean
|
||||
}) {
|
||||
const outerContainerRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
@@ -463,24 +515,30 @@ function LogsFullscreenDialog({ open, onOpenChange, logsDisplay, containerName,
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => {
|
||||
void onRefresh()
|
||||
}}
|
||||
onClick={onRefresh}
|
||||
className="absolute top-3 right-11 opacity-60 hover:opacity-100 p-1"
|
||||
disabled={isRefreshing}
|
||||
title={t`Refresh`}
|
||||
aria-label={t`Refresh`}
|
||||
>
|
||||
<RefreshCwIcon
|
||||
className={`size-4 transition-transform duration-300 ${isRefreshing ? 'animate-spin' : ''}`}
|
||||
/>
|
||||
<RefreshCwIcon className={`size-4 transition-transform duration-300 ${isRefreshing ? "animate-spin" : ""}`} />
|
||||
</button>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
|
||||
function InfoFullscreenDialog({ open, onOpenChange, infoDisplay, containerName }: { open: boolean, onOpenChange: (open: boolean) => void, infoDisplay: string, containerName: string }) {
|
||||
function InfoFullscreenDialog({
|
||||
open,
|
||||
onOpenChange,
|
||||
infoDisplay,
|
||||
containerName,
|
||||
}: {
|
||||
open: boolean
|
||||
onOpenChange: (open: boolean) => void
|
||||
infoDisplay: string
|
||||
containerName: string
|
||||
}) {
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="w-[calc(100vw-20px)] h-[calc(100dvh-20px)] max-w-none p-0 bg-gh-dark border-0 text-white">
|
||||
|
||||
@@ -3,6 +3,7 @@ import { getPagePath } from "@nanostores/router"
|
||||
import {
|
||||
ContainerIcon,
|
||||
DatabaseBackupIcon,
|
||||
HardDriveIcon,
|
||||
LogOutIcon,
|
||||
LogsIcon,
|
||||
SearchIcon,
|
||||
@@ -29,6 +30,7 @@ import { LangToggle } from "./lang-toggle"
|
||||
import { Logo } from "./logo"
|
||||
import { ModeToggle } from "./mode-toggle"
|
||||
import { $router, basePath, Link, prependBasePath } from "./router"
|
||||
import { t } from "@lingui/core/macro"
|
||||
|
||||
const CommandPalette = lazy(() => import("./command-palette"))
|
||||
|
||||
@@ -55,6 +57,13 @@ export default function Navbar() {
|
||||
>
|
||||
<ContainerIcon className="h-[1.2rem] w-[1.2rem]" strokeWidth={1.5} />
|
||||
</Link>
|
||||
<Link
|
||||
href={getPagePath($router, "smart")}
|
||||
className={cn("hidden md:grid", buttonVariants({ variant: "ghost", size: "icon" }))}
|
||||
aria-label="S.M.A.R.T."
|
||||
>
|
||||
<HardDriveIcon className="h-[1.2rem] w-[1.2rem]" strokeWidth={1.5} />
|
||||
</Link>
|
||||
<LangToggle />
|
||||
<ModeToggle />
|
||||
<Link
|
||||
|
||||
@@ -3,6 +3,7 @@ import { createRouter } from "@nanostores/router"
|
||||
const routes = {
|
||||
home: "/",
|
||||
containers: "/containers",
|
||||
smart: "/smart",
|
||||
system: `/system/:id`,
|
||||
settings: `/settings/:name?`,
|
||||
forgot_password: `/forgot-password`,
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
getFilteredRowModel,
|
||||
getPaginationRowModel,
|
||||
getSortedRowModel,
|
||||
type PaginationState,
|
||||
type SortingState,
|
||||
useReactTable,
|
||||
type VisibilityState,
|
||||
@@ -40,7 +41,7 @@ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@
|
||||
import { useToast } from "@/components/ui/use-toast"
|
||||
import { alertInfo } from "@/lib/alerts"
|
||||
import { pb } from "@/lib/api"
|
||||
import { cn, formatDuration, formatShortDate } from "@/lib/utils"
|
||||
import { cn, formatDuration, formatShortDate, useBrowserStorage } from "@/lib/utils"
|
||||
import type { AlertsHistoryRecord } from "@/types"
|
||||
import { alertsHistoryColumns } from "../../alerts-history-columns"
|
||||
|
||||
@@ -66,6 +67,12 @@ export default function AlertsHistoryDataTable() {
|
||||
const [globalFilter, setGlobalFilter] = useState("")
|
||||
const { toast } = useToast()
|
||||
const [deleteOpen, setDeleteDialogOpen] = useState(false)
|
||||
|
||||
// Store pagination preference in local storage
|
||||
const [pagination, setPagination] = useBrowserStorage<PaginationState>("ah-pagination", {
|
||||
pageIndex: 0,
|
||||
pageSize: 10,
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
let unsubscribe: (() => void) | undefined
|
||||
@@ -136,12 +143,14 @@ export default function AlertsHistoryDataTable() {
|
||||
onColumnFiltersChange: setColumnFilters,
|
||||
onColumnVisibilityChange: setColumnVisibility,
|
||||
onRowSelectionChange: setRowSelection,
|
||||
onPaginationChange: setPagination,
|
||||
state: {
|
||||
sorting,
|
||||
columnFilters,
|
||||
columnVisibility,
|
||||
rowSelection,
|
||||
globalFilter,
|
||||
pagination,
|
||||
},
|
||||
onGlobalFilterChange: setGlobalFilter,
|
||||
globalFilterFn: (row, _columnId, filterValue) => {
|
||||
@@ -318,10 +327,10 @@ export default function AlertsHistoryDataTable() {
|
||||
<Select
|
||||
value={`${table.getState().pagination.pageSize}`}
|
||||
onValueChange={(value) => {
|
||||
table.setPageSize(Number(value))
|
||||
table.setPageSize(Number(value));
|
||||
}}
|
||||
>
|
||||
<SelectTrigger className="w-[4.8em]" id="rows-per-page">
|
||||
<SelectTrigger className="w-18" id="rows-per-page">
|
||||
<SelectValue placeholder={table.getState().pagination.pageSize} />
|
||||
</SelectTrigger>
|
||||
<SelectContent side="top">
|
||||
|
||||
@@ -2,14 +2,17 @@
|
||||
import { Trans, useLingui } from "@lingui/react/macro"
|
||||
import { LanguagesIcon, LoaderCircleIcon, SaveIcon } from "lucide-react"
|
||||
import { useState } from "react"
|
||||
import { useStore } from "@nanostores/react"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Label } from "@/components/ui/label"
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
|
||||
import { Separator } from "@/components/ui/separator"
|
||||
import Slider from "@/components/ui/slider"
|
||||
import { HourFormat, Unit } from "@/lib/enums"
|
||||
import { dynamicActivate } from "@/lib/i18n"
|
||||
import languages from "@/lib/languages"
|
||||
import { $userSettings } from "@/lib/stores"
|
||||
import { chartTimeData, currentHour12 } from "@/lib/utils"
|
||||
import type { UserSettings } from "@/types"
|
||||
import { saveSettings } from "./layout"
|
||||
@@ -17,6 +20,8 @@ import { saveSettings } from "./layout"
|
||||
export default function SettingsProfilePage({ userSettings }: { userSettings: UserSettings }) {
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const { i18n } = useLingui()
|
||||
const currentUserSettings = useStore($userSettings)
|
||||
const layoutWidth = currentUserSettings.layoutWidth ?? 1500
|
||||
|
||||
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
|
||||
e.preventDefault()
|
||||
@@ -73,6 +78,27 @@ export default function SettingsProfilePage({ userSettings }: { userSettings: Us
|
||||
</Select>
|
||||
</div>
|
||||
<Separator />
|
||||
<div className="grid gap-2">
|
||||
<div className="mb-2">
|
||||
<h3 className="mb-1 text-lg font-medium">
|
||||
<Trans>Layout width</Trans>
|
||||
</h3>
|
||||
<Label htmlFor="layoutWidth" className="text-sm text-muted-foreground leading-relaxed">
|
||||
<Trans>Adjust the width of the main layout</Trans> ({layoutWidth}px)
|
||||
</Label>
|
||||
</div>
|
||||
<Slider
|
||||
id="layoutWidth"
|
||||
name="layoutWidth"
|
||||
value={[layoutWidth]}
|
||||
onValueChange={(val) => $userSettings.setKey("layoutWidth", val[0])}
|
||||
min={1000}
|
||||
max={2000}
|
||||
step={10}
|
||||
className="w-full mb-1"
|
||||
/>
|
||||
</div>
|
||||
<Separator />
|
||||
<div className="grid gap-2">
|
||||
<div className="mb-2">
|
||||
<h3 className="mb-1 text-lg font-medium">
|
||||
|
||||
@@ -14,6 +14,7 @@ import { toast } from "@/components/ui/use-toast"
|
||||
import { isAdmin, pb } from "@/lib/api"
|
||||
import type { UserSettings } from "@/types"
|
||||
import { saveSettings } from "./layout"
|
||||
import { QuietHours } from "./quiet-hours"
|
||||
|
||||
interface ShoutrrrUrlCardProps {
|
||||
url: string
|
||||
@@ -120,19 +121,32 @@ const SettingsNotificationsPage = ({ userSettings }: { userSettings: UserSetting
|
||||
</div>
|
||||
<Separator />
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<h3 className="mb-1 text-lg font-medium">
|
||||
<Trans>Webhook / Push notifications</Trans>
|
||||
</h3>
|
||||
<p className="text-sm text-muted-foreground leading-relaxed">
|
||||
<Trans>
|
||||
Beszel uses{" "}
|
||||
<a href="https://beszel.dev/guide/notifications" target="_blank" className="link" rel="noopener">
|
||||
Shoutrrr
|
||||
</a>{" "}
|
||||
to integrate with popular notification services.
|
||||
</Trans>
|
||||
</p>
|
||||
<div className="grid grid-cols-1 sm:flex items-center justify-between gap-4">
|
||||
<div>
|
||||
<h3 className="mb-1 text-lg font-medium">
|
||||
<Trans>Webhook / Push notifications</Trans>
|
||||
</h3>
|
||||
<p className="text-sm text-muted-foreground leading-relaxed">
|
||||
<Trans>
|
||||
Beszel uses{" "}
|
||||
<a href="https://beszel.dev/guide/notifications" target="_blank" className="link" rel="noopener">
|
||||
Shoutrrr
|
||||
</a>{" "}
|
||||
to integrate with popular notification services.
|
||||
</Trans>
|
||||
</p>
|
||||
</div>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
className="h-10 shrink-0"
|
||||
onClick={addWebhook}
|
||||
>
|
||||
<PlusIcon className="size-4" />
|
||||
<span className="ms-1">
|
||||
<Trans>Add URL</Trans>
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
{webhooks.length > 0 && (
|
||||
<div className="grid gap-2.5" id="webhooks">
|
||||
@@ -146,16 +160,10 @@ const SettingsNotificationsPage = ({ userSettings }: { userSettings: UserSetting
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="mt-2 flex items-center gap-1"
|
||||
onClick={addWebhook}
|
||||
>
|
||||
<PlusIcon className="h-4 w-4 -ms-0.5" />
|
||||
<Trans>Add URL</Trans>
|
||||
</Button>
|
||||
</div>
|
||||
<Separator />
|
||||
<div className="space-y-3">
|
||||
<QuietHours />
|
||||
</div>
|
||||
<Separator />
|
||||
<Button
|
||||
@@ -194,7 +202,7 @@ const ShoutrrrUrlCard = ({ url, onUrlChange, onRemove }: ShoutrrrUrlCardProps) =
|
||||
}
|
||||
|
||||
return (
|
||||
<Card className="bg-muted/40 p-2 md:p-3">
|
||||
<Card className="bg-table-header p-2 md:p-3">
|
||||
<div className="flex items-center gap-1">
|
||||
<Input
|
||||
type="url"
|
||||
|
||||
534
internal/site/src/components/routes/settings/quiet-hours.tsx
Normal file
534
internal/site/src/components/routes/settings/quiet-hours.tsx
Normal file
@@ -0,0 +1,534 @@
|
||||
import { t } from "@lingui/core/macro"
|
||||
import { Trans } from "@lingui/react/macro"
|
||||
import { useStore } from "@nanostores/react"
|
||||
import {
|
||||
MoreHorizontalIcon,
|
||||
PlusIcon,
|
||||
Trash2Icon,
|
||||
ServerIcon,
|
||||
ClockIcon,
|
||||
CalendarIcon,
|
||||
ActivityIcon,
|
||||
PenSquareIcon,
|
||||
} from "lucide-react"
|
||||
import { useEffect, useState } from "react"
|
||||
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog"
|
||||
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"
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
||||
import { useToast } from "@/components/ui/use-toast"
|
||||
import { pb } from "@/lib/api"
|
||||
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 = {
|
||||
expand: "system",
|
||||
fields: "id,user,system,type,start,end,expand.system.name",
|
||||
}
|
||||
// Initial load
|
||||
pb.collection<QuietHoursRecord>("quiet_hours")
|
||||
.getList(0, 200, {
|
||||
...pbOptions,
|
||||
sort: "system",
|
||||
})
|
||||
.then(({ items }) => setData(items))
|
||||
|
||||
// Subscribe to changes
|
||||
;(async () => {
|
||||
unsubscribe = await pb.collection("quiet_hours").subscribe(
|
||||
"*",
|
||||
(e) => {
|
||||
if (e.action === "create") {
|
||||
setData((current) => [e.record as QuietHoursRecord, ...current])
|
||||
}
|
||||
if (e.action === "update") {
|
||||
setData((current) => current.map((r) => (r.id === e.record.id ? (e.record as QuietHoursRecord) : r)))
|
||||
}
|
||||
if (e.action === "delete") {
|
||||
setData((current) => current.filter((r) => r.id !== e.record.id))
|
||||
}
|
||||
},
|
||||
pbOptions
|
||||
)
|
||||
})()
|
||||
// Unsubscribe on unmount
|
||||
return () => unsubscribe?.()
|
||||
}, [])
|
||||
|
||||
const handleDelete = async (id: string) => {
|
||||
try {
|
||||
await pb.collection("quiet_hours").delete(id)
|
||||
} catch (e: unknown) {
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: t`Error`,
|
||||
description: (e as Error).message || "Failed to delete quiet hours.",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const openEditDialog = (record: QuietHoursRecord) => {
|
||||
setEditingRecord(record)
|
||||
setDialogOpen(true)
|
||||
}
|
||||
|
||||
const closeDialog = () => {
|
||||
setDialogOpen(false)
|
||||
setEditingRecord(null)
|
||||
}
|
||||
|
||||
const formatDateTime = (record: QuietHoursRecord) => {
|
||||
if (record.type === "daily") {
|
||||
// For daily windows, show only time
|
||||
const startTime = new Date(record.start).toLocaleTimeString([], { hour: "numeric", minute: "2-digit" })
|
||||
const endTime = new Date(record.end).toLocaleTimeString([], { hour: "numeric", minute: "2-digit" })
|
||||
return `${startTime} - ${endTime}`
|
||||
}
|
||||
// For one-time windows, show full date and time
|
||||
const start = formatShortDate(record.start)
|
||||
const end = formatShortDate(record.end)
|
||||
return `${start} - ${end}`
|
||||
}
|
||||
|
||||
const getWindowState = (record: QuietHoursRecord): "active" | "past" | "inactive" => {
|
||||
const now = new Date()
|
||||
|
||||
if (record.type === "daily") {
|
||||
// For daily windows, check if current time is within the window
|
||||
const startDate = new Date(record.start)
|
||||
const endDate = new Date(record.end)
|
||||
|
||||
// Get current time in local timezone
|
||||
const currentMinutes = now.getHours() * 60 + now.getMinutes()
|
||||
const startMinutes = startDate.getUTCHours() * 60 + startDate.getUTCMinutes()
|
||||
const endMinutes = endDate.getUTCHours() * 60 + endDate.getUTCMinutes()
|
||||
|
||||
// Convert UTC to local time offset
|
||||
const offset = now.getTimezoneOffset()
|
||||
const localStartMinutes = (startMinutes - offset + 1440) % 1440
|
||||
const localEndMinutes = (endMinutes - offset + 1440) % 1440
|
||||
|
||||
// Handle cases where window spans midnight
|
||||
if (localStartMinutes <= localEndMinutes) {
|
||||
return currentMinutes >= localStartMinutes && currentMinutes < localEndMinutes ? "active" : "inactive"
|
||||
} else {
|
||||
return currentMinutes >= localStartMinutes || currentMinutes < localEndMinutes ? "active" : "inactive"
|
||||
}
|
||||
} else {
|
||||
// For one-time windows
|
||||
const startDate = new Date(record.start)
|
||||
const endDate = new Date(record.end)
|
||||
|
||||
if (now >= startDate && now < endDate) {
|
||||
return "active"
|
||||
} else if (now >= endDate) {
|
||||
return "past"
|
||||
} else {
|
||||
return "inactive"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<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">{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.
|
||||
</Trans>
|
||||
</p>
|
||||
</div>
|
||||
<Dialog open={dialogOpen} onOpenChange={setDialogOpen}>
|
||||
<DialogTrigger asChild>
|
||||
<Button variant="outline" className="h-10 shrink-0" onClick={() => setEditingRecord(null)}>
|
||||
<PlusIcon className="size-4" />
|
||||
<span className="ms-1">
|
||||
<Trans>Add {{ foo: quietHoursTranslation }}</Trans>
|
||||
</span>
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<QuietHoursDialog editingRecord={editingRecord} systems={systems} onClose={closeDialog} toast={toast} />
|
||||
</Dialog>
|
||||
</div>
|
||||
{data.length > 0 && (
|
||||
<div className="rounded-md border overflow-x-auto whitespace-nowrap">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow className="border-border/50">
|
||||
<TableHead className="px-4">
|
||||
<span className="flex items-center gap-2">
|
||||
<ServerIcon className="size-4" />
|
||||
<Trans>System</Trans>
|
||||
</span>
|
||||
</TableHead>
|
||||
<TableHead className="px-4">
|
||||
<span className="flex items-center gap-2">
|
||||
<ClockIcon className="size-4" />
|
||||
<Trans>Type</Trans>
|
||||
</span>
|
||||
</TableHead>
|
||||
<TableHead className="px-4">
|
||||
<span className="flex items-center gap-2">
|
||||
<CalendarIcon className="size-4" />
|
||||
<Trans>Schedule</Trans>
|
||||
</span>
|
||||
</TableHead>
|
||||
<TableHead className="px-4">
|
||||
<span className="flex items-center gap-2">
|
||||
<ActivityIcon className="size-4" />
|
||||
<Trans>State</Trans>
|
||||
</span>
|
||||
</TableHead>
|
||||
<TableHead className="px-4 text-right sr-only">
|
||||
<Trans>Actions</Trans>
|
||||
</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{data.map((record) => (
|
||||
<TableRow key={record.id}>
|
||||
<TableCell className="px-4 py-3">
|
||||
{record.system ? record.expand?.system?.name || record.system : <Trans>All Systems</Trans>}
|
||||
</TableCell>
|
||||
<TableCell className="px-4 py-3">
|
||||
{record.type === "daily" ? <Trans>Daily</Trans> : <Trans>One-time</Trans>}
|
||||
</TableCell>
|
||||
<TableCell className="px-4 py-3">{formatDateTime(record)}</TableCell>
|
||||
<TableCell className="px-4 py-3">
|
||||
{(() => {
|
||||
const state = getWindowState(record)
|
||||
const stateConfig = {
|
||||
active: { label: <Trans>Active</Trans>, variant: "success" as const },
|
||||
past: { label: <Trans>Past</Trans>, variant: "danger" as const },
|
||||
inactive: { label: <Trans>Inactive</Trans>, variant: "default" as const },
|
||||
}
|
||||
const config = stateConfig[state]
|
||||
return <Badge variant={config.variant}>{config.label}</Badge>
|
||||
})()}
|
||||
</TableCell>
|
||||
<TableCell className="px-4 py-3 text-right">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost" size="icon" className="size-8">
|
||||
<span className="sr-only">
|
||||
<Trans>Open menu</Trans>
|
||||
</span>
|
||||
<MoreHorizontalIcon className="size-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem onClick={() => openEditDialog(record)}>
|
||||
<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>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
// Helper function to format Date as datetime-local string (YYYY-MM-DDTHH:mm) in local time
|
||||
function formatDateTimeLocal(date: Date): string {
|
||||
const year = date.getFullYear()
|
||||
const month = String(date.getMonth() + 1).padStart(2, "0")
|
||||
const day = String(date.getDate()).padStart(2, "0")
|
||||
const hours = String(date.getHours()).padStart(2, "0")
|
||||
const minutes = String(date.getMinutes()).padStart(2, "0")
|
||||
return `${year}-${month}-${day}T${hours}:${minutes}`
|
||||
}
|
||||
|
||||
function QuietHoursDialog({
|
||||
editingRecord,
|
||||
systems,
|
||||
onClose,
|
||||
toast,
|
||||
}: {
|
||||
editingRecord: QuietHoursRecord | null
|
||||
systems: SystemRecord[]
|
||||
onClose: () => void
|
||||
toast: ReturnType<typeof useToast>["toast"]
|
||||
}) {
|
||||
const [selectedSystem, setSelectedSystem] = useState(editingRecord?.system || "")
|
||||
const [isGlobal, setIsGlobal] = useState(!editingRecord?.system)
|
||||
const [windowType, setWindowType] = useState<"one-time" | "daily">(editingRecord?.type || "one-time")
|
||||
const [startDateTime, setStartDateTime] = useState("")
|
||||
const [endDateTime, setEndDateTime] = useState("")
|
||||
const [startTime, setStartTime] = useState("")
|
||||
const [endTime, setEndTime] = useState("")
|
||||
|
||||
useEffect(() => {
|
||||
if (editingRecord) {
|
||||
setSelectedSystem(editingRecord.system || "")
|
||||
setIsGlobal(!editingRecord.system)
|
||||
setWindowType(editingRecord.type)
|
||||
if (editingRecord.type === "daily") {
|
||||
// Extract time from datetime
|
||||
const start = new Date(editingRecord.start)
|
||||
const end = editingRecord.end ? new Date(editingRecord.end) : null
|
||||
setStartTime(start.toTimeString().slice(0, 5))
|
||||
setEndTime(end ? end.toTimeString().slice(0, 5) : "")
|
||||
} else {
|
||||
// For one-time, format as datetime-local (local time, not UTC)
|
||||
const startDate = new Date(editingRecord.start)
|
||||
const endDate = editingRecord.end ? new Date(editingRecord.end) : null
|
||||
|
||||
setStartDateTime(formatDateTimeLocal(startDate))
|
||||
setEndDateTime(endDate ? formatDateTimeLocal(endDate) : "")
|
||||
}
|
||||
} else {
|
||||
// Reset form with default dates: today at 12pm and 1pm
|
||||
const today = new Date()
|
||||
const noon = new Date(today)
|
||||
noon.setHours(12, 0, 0, 0)
|
||||
const onePm = new Date(today)
|
||||
onePm.setHours(13, 0, 0, 0)
|
||||
|
||||
setSelectedSystem("")
|
||||
setIsGlobal(true)
|
||||
setWindowType("one-time")
|
||||
setStartDateTime(formatDateTimeLocal(noon))
|
||||
setEndDateTime(formatDateTimeLocal(onePm))
|
||||
setStartTime("12:00")
|
||||
setEndTime("13:00")
|
||||
}
|
||||
}, [editingRecord])
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
|
||||
try {
|
||||
let startValue: string
|
||||
let endValue: string | undefined
|
||||
|
||||
if (windowType === "daily") {
|
||||
// For daily windows, convert local time to UTC
|
||||
// Create a date with the time in local timezone, then convert to UTC
|
||||
const startDate = new Date(`2000-01-01T${startTime}:00`)
|
||||
startValue = startDate.toISOString()
|
||||
|
||||
if (endTime) {
|
||||
const endDate = new Date(`2000-01-01T${endTime}:00`)
|
||||
endValue = endDate.toISOString()
|
||||
}
|
||||
} else {
|
||||
// For one-time windows, use the datetime values
|
||||
startValue = new Date(startDateTime).toISOString()
|
||||
endValue = endDateTime ? new Date(endDateTime).toISOString() : undefined
|
||||
}
|
||||
|
||||
const data = {
|
||||
user: pb.authStore.record?.id,
|
||||
system: isGlobal ? undefined : selectedSystem,
|
||||
type: windowType,
|
||||
start: startValue,
|
||||
end: endValue,
|
||||
}
|
||||
|
||||
if (editingRecord) {
|
||||
await pb.collection("quiet_hours").update(editingRecord.id, data)
|
||||
} else {
|
||||
await pb.collection("quiet_hours").create(data)
|
||||
}
|
||||
|
||||
onClose()
|
||||
} catch (e) {
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: t`Error`,
|
||||
description: t`Failed to save settings`,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
{editingRecord ? (
|
||||
<Trans>Edit {{ foo: quietHoursTranslation }}</Trans>
|
||||
) : (
|
||||
<Trans>Add {{ foo: quietHoursTranslation }}</Trans>
|
||||
)}
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
<Trans>Schedule quiet hours where notifications will not be sent.</Trans>
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<Tabs value={isGlobal ? "global" : "system"} onValueChange={(value) => setIsGlobal(value === "global")}>
|
||||
<TabsList className="grid w-full grid-cols-2">
|
||||
<TabsTrigger value="global">
|
||||
<Trans>Global</Trans>
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="system">
|
||||
<Trans>System</Trans>
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="system" className="mt-4 space-y-4">
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="system">
|
||||
<Trans>System</Trans>
|
||||
</Label>
|
||||
<Select value={selectedSystem} onValueChange={setSelectedSystem}>
|
||||
<SelectTrigger id="system">
|
||||
<SelectValue placeholder={t`Select ${{ foo: t`System`.toLocaleLowerCase() }}`} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{systems.map((system) => (
|
||||
<SelectItem key={system.id} value={system.id}>
|
||||
{system.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
{/* Hidden input for native form validation */}
|
||||
<input
|
||||
className="sr-only"
|
||||
type="text"
|
||||
tabIndex={-1}
|
||||
autoComplete="off"
|
||||
value={selectedSystem}
|
||||
onChange={() => {}}
|
||||
required={!isGlobal}
|
||||
/>
|
||||
</div>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="type">
|
||||
<Trans>Type</Trans>
|
||||
</Label>
|
||||
<Select value={windowType} onValueChange={(value: "one-time" | "daily") => setWindowType(value)}>
|
||||
<SelectTrigger id="type">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="one-time">
|
||||
<Trans>One-time</Trans>
|
||||
</SelectItem>
|
||||
<SelectItem value="daily">
|
||||
<Trans>Daily</Trans>
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{windowType === "one-time" ? (
|
||||
<>
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="start-datetime">
|
||||
<Trans>Start Time</Trans>
|
||||
</Label>
|
||||
<Input
|
||||
id="start-datetime"
|
||||
type="datetime-local"
|
||||
value={startDateTime}
|
||||
onChange={(e) => setStartDateTime(e.target.value)}
|
||||
min={formatDateTimeLocal(new Date(new Date().setHours(0, 0, 0, 0)))}
|
||||
required
|
||||
className="tabular-nums tracking-tighter"
|
||||
/>
|
||||
</div>
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="end-datetime">
|
||||
<Trans>End Time</Trans>
|
||||
</Label>
|
||||
<Input
|
||||
id="end-datetime"
|
||||
type="datetime-local"
|
||||
value={endDateTime}
|
||||
onChange={(e) => setEndDateTime(e.target.value)}
|
||||
min={startDateTime || formatDateTimeLocal(new Date())}
|
||||
required
|
||||
className="tabular-nums tracking-tighter"
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div className="grid gap-2 grid-cols-2">
|
||||
<div>
|
||||
<Label htmlFor="start-time">
|
||||
<Trans>Start Time</Trans>
|
||||
</Label>
|
||||
<Input
|
||||
className="tabular-nums tracking-tighter"
|
||||
id="start-time"
|
||||
type="time"
|
||||
value={startTime}
|
||||
onChange={(e) => setStartTime(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="end-time">
|
||||
<Trans>End Time</Trans>
|
||||
</Label>
|
||||
<Input
|
||||
className="tabular-nums tracking-tighter"
|
||||
id="end-time"
|
||||
type="time"
|
||||
value={endTime}
|
||||
onChange={(e) => setEndTime(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<DialogFooter>
|
||||
<Button type="button" variant="outline" onClick={onClose}>
|
||||
<Trans>Cancel</Trans>
|
||||
</Button>
|
||||
<Button type="submit">{editingRecord ? <Trans>Update</Trans> : <Trans>Create</Trans>}</Button>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
</DialogContent>
|
||||
)
|
||||
}
|
||||
20
internal/site/src/components/routes/smart.tsx
Normal file
20
internal/site/src/components/routes/smart.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import { useEffect } from "react"
|
||||
import SmartTable from "@/components/routes/system/smart-table"
|
||||
import { ActiveAlerts } from "@/components/active-alerts"
|
||||
import { FooterRepoLink } from "@/components/footer-repo-link"
|
||||
|
||||
export default function Smart() {
|
||||
useEffect(() => {
|
||||
document.title = `S.M.A.R.T. / Beszel`
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="grid gap-4">
|
||||
<ActiveAlerts />
|
||||
<SmartTable />
|
||||
</div>
|
||||
<FooterRepoLink />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { t } from "@lingui/core/macro"
|
||||
import { Plural, Trans, useLingui } from "@lingui/react/macro"
|
||||
import { Trans, useLingui } from "@lingui/react/macro"
|
||||
import { useStore } from "@nanostores/react"
|
||||
import { getPagePath } from "@nanostores/router"
|
||||
import { timeTicks } from "d3-time"
|
||||
@@ -42,7 +42,6 @@ import {
|
||||
chartTimeData,
|
||||
cn,
|
||||
compareSemVer,
|
||||
debounce,
|
||||
decimalString,
|
||||
formatBytes,
|
||||
secondsToString,
|
||||
@@ -73,9 +72,9 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from ".
|
||||
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"
|
||||
|
||||
type ChartTimeData = {
|
||||
time: number
|
||||
@@ -97,8 +96,8 @@ function getTimeData(chartTime: ChartTimes, lastCreated: number) {
|
||||
}
|
||||
}
|
||||
|
||||
const buffer = chartTime === "1m" ? 400 : 20_000
|
||||
const now = new Date(Date.now() + buffer)
|
||||
// const buffer = chartTime === "1m" ? 400 : 20_000
|
||||
const now = new Date(Date.now())
|
||||
const startTime = chartTimeData[chartTime].getOffset(now)
|
||||
const ticks = timeTicks(startTime, now, chartTimeData[chartTime].ticks ?? 12).map((date) => date.getTime())
|
||||
const data = {
|
||||
@@ -585,7 +584,12 @@ export default memo(function SystemDetail({ id }: { id: string }) {
|
||||
grid={grid}
|
||||
title={t`CPU Usage`}
|
||||
description={t`Average system-wide CPU utilization`}
|
||||
cornerEl={maxValSelect}
|
||||
cornerEl={
|
||||
<div className="flex gap-2">
|
||||
{maxValSelect}
|
||||
<CpuCoresSheet chartData={chartData} dataEmpty={dataEmpty} grid={grid} maxValues={maxValues} />
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<AreaChartDefault
|
||||
chartData={chartData}
|
||||
@@ -600,6 +604,7 @@ export default memo(function SystemDetail({ id }: { id: string }) {
|
||||
]}
|
||||
tickFormatter={(val) => `${toFixedFloat(val, 2)}%`}
|
||||
contentFormatter={({ value }) => `${decimalString(value)}%`}
|
||||
domain={pinnedAxisDomain()}
|
||||
/>
|
||||
</ChartCard>
|
||||
|
||||
@@ -693,6 +698,7 @@ export default memo(function SystemDetail({ id }: { id: string }) {
|
||||
const { value: convertedValue, unit } = formatBytes(value, true, userSettings.unitDisk, false)
|
||||
return `${decimalString(convertedValue, convertedValue >= 100 ? 1 : 2)} ${unit}`
|
||||
}}
|
||||
showTotal={true}
|
||||
/>
|
||||
</ChartCard>
|
||||
|
||||
@@ -746,6 +752,7 @@ export default memo(function SystemDetail({ id }: { id: string }) {
|
||||
const { value, unit } = formatBytes(data.value, true, userSettings.unitNet, false)
|
||||
return `${decimalString(value, value >= 100 ? 1 : 2)} ${unit}`
|
||||
}}
|
||||
showTotal={true}
|
||||
/>
|
||||
</ChartCard>
|
||||
|
||||
@@ -1001,7 +1008,11 @@ export default memo(function SystemDetail({ id }: { id: string }) {
|
||||
)}
|
||||
|
||||
{containerData.length > 0 && compareSemVer(chartData.agentVersion, parseSemVer("0.14.0")) >= 0 && (
|
||||
<LazyContainersTable systemId={id} />
|
||||
<LazyContainersTable systemId={system.id} />
|
||||
)}
|
||||
|
||||
{system.info?.os === Os.Linux && compareSemVer(chartData.agentVersion, parseSemVer("0.16.0")) >= 0 && (
|
||||
<LazySystemdTable systemId={system.id} />
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -1034,32 +1045,51 @@ function GpuEnginesChart({ chartData }: { chartData: ChartData }) {
|
||||
}
|
||||
|
||||
function FilterBar({ store = $containerFilter }: { store?: typeof $containerFilter }) {
|
||||
const containerFilter = useStore(store)
|
||||
const storeValue = useStore(store)
|
||||
const [inputValue, setInputValue] = useState(storeValue)
|
||||
const { t } = useLingui()
|
||||
|
||||
const debouncedStoreSet = useMemo(() => debounce((value: string) => store.set(value), 80), [store])
|
||||
useEffect(() => {
|
||||
setInputValue(storeValue)
|
||||
}, [storeValue])
|
||||
|
||||
useEffect(() => {
|
||||
if (inputValue === storeValue) {
|
||||
return
|
||||
}
|
||||
const handle = window.setTimeout(() => store.set(inputValue), 80)
|
||||
return () => clearTimeout(handle)
|
||||
}, [inputValue, storeValue, store])
|
||||
|
||||
const handleChange = useCallback(
|
||||
(e: React.ChangeEvent<HTMLInputElement>) => debouncedStoreSet(e.target.value),
|
||||
[debouncedStoreSet]
|
||||
(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const value = e.target.value
|
||||
setInputValue(value)
|
||||
},
|
||||
[]
|
||||
)
|
||||
|
||||
const handleClear = useCallback(() => {
|
||||
setInputValue("")
|
||||
store.set("")
|
||||
}, [store])
|
||||
|
||||
return (
|
||||
<>
|
||||
<Input
|
||||
placeholder={t`Filter...`}
|
||||
className="ps-4 pe-8 w-full sm:w-44"
|
||||
onChange={handleChange}
|
||||
value={containerFilter}
|
||||
value={inputValue}
|
||||
/>
|
||||
{containerFilter && (
|
||||
{inputValue && (
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
aria-label="Clear"
|
||||
className="absolute right-1 top-1/2 -translate-y-1/2 h-7 w-7 text-gray-500 hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-100"
|
||||
onClick={() => store.set("")}
|
||||
onClick={handleClear}
|
||||
>
|
||||
<XIcon className="h-4 w-4" />
|
||||
</Button>
|
||||
@@ -1119,7 +1149,7 @@ export function ChartCard({
|
||||
<CardDescription>{description}</CardDescription>
|
||||
{cornerEl && <div className="py-1 grid sm:justify-end sm:absolute sm:top-3.5 sm:end-3.5">{cornerEl}</div>}
|
||||
</CardHeader>
|
||||
<div className={cn("ps-0 w-[calc(100%-1.5em)] relative group", legend ? "h-54 md:h-56" : "h-48 md:h-52")}>
|
||||
<div className={cn("ps-0 w-[calc(100%-1.3em)] relative group", legend ? "h-54 md:h-56" : "h-48 md:h-52")}>
|
||||
{
|
||||
<Spinner
|
||||
msg={empty ? t`Waiting for enough records to display` : undefined}
|
||||
@@ -1153,4 +1183,15 @@ function LazySmartTable({ systemId }: { systemId: string }) {
|
||||
{isIntersecting && <SmartTable systemId={systemId} />}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const SystemdTable = lazy(() => import("../systemd-table/systemd-table"))
|
||||
|
||||
function LazySystemdTable({ systemId }: { systemId: string }) {
|
||||
const { isIntersecting, ref } = useIntersectionObserver()
|
||||
return (
|
||||
<div ref={ref} className={cn(isIntersecting && "contents")}>
|
||||
{isIntersecting && <SystemdTable systemId={systemId} />}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
195
internal/site/src/components/routes/system/cpu-sheet.tsx
Normal file
195
internal/site/src/components/routes/system/cpu-sheet.tsx
Normal file
@@ -0,0 +1,195 @@
|
||||
import { t } from "@lingui/core/macro"
|
||||
import { MoreHorizontalIcon } from "lucide-react"
|
||||
import { memo, useRef, useState } from "react"
|
||||
import AreaChartDefault, { DataPoint } from "@/components/charts/area-chart"
|
||||
import ChartTimeSelect from "@/components/charts/chart-time-select"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet"
|
||||
import { DialogTitle } from "@/components/ui/dialog"
|
||||
import { compareSemVer, decimalString, parseSemVer, toFixedFloat } from "@/lib/utils"
|
||||
import type { ChartData, SystemStatsRecord } from "@/types"
|
||||
import { ChartCard } from "../system"
|
||||
|
||||
const minAgentVersion = parseSemVer("0.15.3")
|
||||
|
||||
export default memo(function CpuCoresSheet({
|
||||
chartData,
|
||||
dataEmpty,
|
||||
grid,
|
||||
maxValues,
|
||||
}: {
|
||||
chartData: ChartData
|
||||
dataEmpty: boolean
|
||||
grid: boolean
|
||||
maxValues: boolean
|
||||
}) {
|
||||
const [cpuCoresOpen, setCpuCoresOpen] = useState(false)
|
||||
const hasOpened = useRef(false)
|
||||
|
||||
const supportsBreakdown = compareSemVer(chartData.agentVersion, minAgentVersion) >= 0
|
||||
|
||||
if (!supportsBreakdown) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (cpuCoresOpen && !hasOpened.current) {
|
||||
hasOpened.current = true
|
||||
}
|
||||
|
||||
// Latest stats snapshot
|
||||
const latest = chartData.systemStats.at(-1)?.stats
|
||||
const cpus = latest?.cpus ?? []
|
||||
const numCores = cpus.length
|
||||
const hasBreakdown = (latest?.cpub?.length ?? 0) > 0
|
||||
|
||||
const breakdownDataPoints = [
|
||||
{
|
||||
label: "System",
|
||||
dataKey: ({ stats }: SystemStatsRecord) => stats?.cpub?.[1],
|
||||
color: 3,
|
||||
opacity: 0.35,
|
||||
stackId: "a"
|
||||
},
|
||||
{
|
||||
label: "User",
|
||||
dataKey: ({ stats }: SystemStatsRecord) => stats?.cpub?.[0],
|
||||
color: 1,
|
||||
opacity: 0.35,
|
||||
stackId: "a"
|
||||
},
|
||||
{
|
||||
label: "IOWait",
|
||||
dataKey: ({ stats }: SystemStatsRecord) => stats?.cpub?.[2],
|
||||
color: 4,
|
||||
opacity: 0.35,
|
||||
stackId: "a"
|
||||
},
|
||||
{
|
||||
label: "Steal",
|
||||
dataKey: ({ stats }: SystemStatsRecord) => stats?.cpub?.[3],
|
||||
color: 5,
|
||||
opacity: 0.35,
|
||||
stackId: "a"
|
||||
},
|
||||
{
|
||||
label: "Idle",
|
||||
dataKey: ({ stats }: SystemStatsRecord) => stats?.cpub?.[4],
|
||||
color: 2,
|
||||
opacity: 0.35,
|
||||
stackId: "a"
|
||||
},
|
||||
{
|
||||
label: t`Other`,
|
||||
dataKey: ({ stats }: SystemStatsRecord) => {
|
||||
const total = stats?.cpub?.reduce((acc, curr) => acc + curr, 0) ?? 0
|
||||
return total > 0 ? 100 - total : null
|
||||
},
|
||||
color: `hsl(80, 65%, 52%)`,
|
||||
opacity: 0.35,
|
||||
stackId: "a"
|
||||
},
|
||||
] as DataPoint[]
|
||||
|
||||
|
||||
return (
|
||||
<Sheet open={cpuCoresOpen} onOpenChange={setCpuCoresOpen}>
|
||||
<DialogTitle className="sr-only">{t`CPU Usage`}</DialogTitle>
|
||||
<SheetTrigger asChild>
|
||||
<Button
|
||||
title={t`View more`}
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className="shrink-0 max-sm:absolute max-sm:top-3 max-sm:end-3"
|
||||
>
|
||||
<MoreHorizontalIcon />
|
||||
</Button>
|
||||
</SheetTrigger>
|
||||
{hasOpened.current && (
|
||||
<SheetContent aria-describedby={undefined} className="overflow-auto w-200 !max-w-full p-4 sm:p-6">
|
||||
<ChartTimeSelect className="w-[calc(100%-2em)] bg-card" agentVersion={chartData.agentVersion} />
|
||||
{hasBreakdown && (
|
||||
<ChartCard
|
||||
key="cpu-breakdown"
|
||||
empty={dataEmpty}
|
||||
grid={grid}
|
||||
title={t`CPU Time Breakdown`}
|
||||
description={t`Percentage of time spent in each state`}
|
||||
legend={true}
|
||||
className="min-h-auto"
|
||||
>
|
||||
<AreaChartDefault
|
||||
chartData={chartData}
|
||||
maxToggled={maxValues}
|
||||
legend={true}
|
||||
dataPoints={breakdownDataPoints}
|
||||
tickFormatter={(val) => `${toFixedFloat(val, 2)}%`}
|
||||
contentFormatter={({ value }) => `${decimalString(value)}%`}
|
||||
reverseStackOrder={true}
|
||||
itemSorter={() => 1}
|
||||
domain={[0, 100]}
|
||||
/>
|
||||
</ChartCard>
|
||||
)}
|
||||
|
||||
{numCores > 0 && (
|
||||
<ChartCard
|
||||
key="cpu-cores-all"
|
||||
empty={dataEmpty}
|
||||
grid={grid}
|
||||
title={t`CPU Cores`}
|
||||
legend={numCores < 10}
|
||||
description={t`Per-core average utilization`}
|
||||
className="min-h-auto"
|
||||
>
|
||||
<AreaChartDefault
|
||||
hideYAxis={true}
|
||||
chartData={chartData}
|
||||
maxToggled={maxValues}
|
||||
legend={numCores < 10}
|
||||
dataPoints={Array.from({ length: numCores }).map((_, i) => ({
|
||||
label: `CPU ${i}`,
|
||||
dataKey: ({ stats }: SystemStatsRecord) => stats?.cpus?.[i] ?? 1 / (stats?.cpus?.length ?? 1),
|
||||
color: `hsl(${226 + (((i * 360) / Math.max(1, numCores)) % 360)}, var(--chart-saturation), var(--chart-lightness))`,
|
||||
opacity: 0.35,
|
||||
stackId: "a"
|
||||
}))}
|
||||
tickFormatter={(val) => `${val}%`}
|
||||
contentFormatter={({ value }) => `${value}%`}
|
||||
reverseStackOrder={true}
|
||||
itemSorter={() => 1}
|
||||
/>
|
||||
</ChartCard>
|
||||
)}
|
||||
|
||||
{Array.from({ length: numCores }).map((_, i) => (
|
||||
<ChartCard
|
||||
key={`cpu-core-${i}`}
|
||||
empty={dataEmpty}
|
||||
grid={grid}
|
||||
title={`CPU ${i}`}
|
||||
description={t`Per-core average utilization`}
|
||||
legend={false}
|
||||
className="min-h-auto"
|
||||
>
|
||||
<AreaChartDefault
|
||||
chartData={chartData}
|
||||
maxToggled={maxValues}
|
||||
legend={false}
|
||||
dataPoints={[
|
||||
{
|
||||
label: t`Usage`,
|
||||
dataKey: ({ stats }: SystemStatsRecord) => stats?.cpus?.[i],
|
||||
color: `hsl(${226 + (((i * 360) / Math.max(1, numCores)) % 360)}, 65%, 52%)`,
|
||||
opacity: 0.35,
|
||||
},
|
||||
]}
|
||||
tickFormatter={(val) => `${val}%`}
|
||||
contentFormatter={({ value }) => `${value}%`}
|
||||
/>
|
||||
</ChartCard>
|
||||
))}
|
||||
</SheetContent>
|
||||
)}
|
||||
</Sheet>
|
||||
)
|
||||
})
|
||||
@@ -53,7 +53,7 @@ export default memo(function NetworkSheet({
|
||||
</SheetTrigger>
|
||||
{hasOpened.current && (
|
||||
<SheetContent aria-describedby={undefined} className="overflow-auto w-200 !max-w-full p-4 sm:p-6">
|
||||
<ChartTimeSelect className="w-[calc(100%-2em)]" agentVersion={chartData.agentVersion} />
|
||||
<ChartTimeSelect className="w-[calc(100%-2em)] bg-card" agentVersion={chartData.agentVersion} />
|
||||
<ChartCard
|
||||
empty={dataEmpty}
|
||||
grid={grid}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,200 @@
|
||||
import type { Column, ColumnDef } from "@tanstack/react-table"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { cn, decimalString, formatBytes, hourWithSeconds } from "@/lib/utils"
|
||||
import type { SystemdRecord } from "@/types"
|
||||
import { ServiceStatus, ServiceStatusLabels, ServiceSubState, ServiceSubStateLabels } from "@/lib/enums"
|
||||
import {
|
||||
ActivityIcon,
|
||||
ArrowUpDownIcon,
|
||||
ClockIcon,
|
||||
CpuIcon,
|
||||
MemoryStickIcon,
|
||||
TerminalSquareIcon,
|
||||
} from "lucide-react"
|
||||
import { Badge } from "../ui/badge"
|
||||
import { t } from "@lingui/core/macro"
|
||||
// import { $allSystemsById } from "@/lib/stores"
|
||||
// import { useStore } from "@nanostores/react"
|
||||
|
||||
function getSubStateColor(subState: ServiceSubState) {
|
||||
switch (subState) {
|
||||
case ServiceSubState.Running:
|
||||
return "bg-green-500"
|
||||
case ServiceSubState.Failed:
|
||||
return "bg-red-500"
|
||||
case ServiceSubState.Dead:
|
||||
return "bg-yellow-500"
|
||||
default:
|
||||
return "bg-zinc-500"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export const systemdTableCols: ColumnDef<SystemdRecord>[] = [
|
||||
{
|
||||
id: "name",
|
||||
sortingFn: (a, b) => a.original.name.localeCompare(b.original.name),
|
||||
accessorFn: (record) => record.name,
|
||||
header: ({ column }) => <HeaderButton column={column} name={t`Name`} Icon={TerminalSquareIcon} />,
|
||||
cell: ({ getValue }) => {
|
||||
return <span className="ms-1.5 xl:w-50 block truncate">{getValue() as string}</span>
|
||||
},
|
||||
},
|
||||
// {
|
||||
// id: "system",
|
||||
// accessorFn: (record) => record.system,
|
||||
// sortingFn: (a, b) => {
|
||||
// const allSystems = $allSystemsById.get()
|
||||
// const systemNameA = allSystems[a.original.system]?.name ?? ""
|
||||
// const systemNameB = allSystems[b.original.system]?.name ?? ""
|
||||
// return systemNameA.localeCompare(systemNameB)
|
||||
// },
|
||||
// header: ({ column }) => <HeaderButton column={column} name={t`System`} Icon={ServerIcon} />,
|
||||
// cell: ({ getValue }) => {
|
||||
// const allSystems = useStore($allSystemsById)
|
||||
// return <span className="ms-1.5 xl:w-34 block truncate">{allSystems[getValue() as string]?.name ?? ""}</span>
|
||||
// },
|
||||
// },
|
||||
{
|
||||
id: "state",
|
||||
accessorFn: (record) => record.state,
|
||||
header: ({ column }) => <HeaderButton column={column} name={t`State`} Icon={ActivityIcon} />,
|
||||
cell: ({ getValue }) => {
|
||||
const statusValue = getValue() as ServiceStatus
|
||||
const statusLabel = ServiceStatusLabels[statusValue] || "Unknown"
|
||||
return (
|
||||
<Badge variant="outline" className="dark:border-white/12">
|
||||
<span className={cn("size-2 me-1.5 rounded-full", getStatusColor(statusValue))} />
|
||||
{statusLabel}
|
||||
</Badge>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "sub",
|
||||
accessorFn: (record) => record.sub,
|
||||
header: ({ column }) => <HeaderButton column={column} name={t`Sub State`} Icon={ActivityIcon} />,
|
||||
cell: ({ getValue }) => {
|
||||
const subState = getValue() as ServiceSubState
|
||||
const subStateLabel = ServiceSubStateLabels[subState] || "Unknown"
|
||||
return (
|
||||
<Badge variant="outline" className="dark:border-white/12 text-xs capitalize">
|
||||
<span className={cn("size-2 me-1.5 rounded-full", getSubStateColor(subState))} />
|
||||
{subStateLabel}
|
||||
</Badge>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "cpu",
|
||||
accessorFn: (record) => {
|
||||
if (record.sub !== ServiceSubState.Running) {
|
||||
return -1
|
||||
}
|
||||
return record.cpu
|
||||
},
|
||||
invertSorting: true,
|
||||
header: ({ column }) => <HeaderButton column={column} name={`${t`CPU`} (10m)`} Icon={CpuIcon} />,
|
||||
cell: ({ getValue }) => {
|
||||
const val = getValue() as number
|
||||
if (val < 0) {
|
||||
return <span className="ms-1.5 text-muted-foreground">N/A</span>
|
||||
}
|
||||
return <span className="ms-1.5 tabular-nums">{`${decimalString(val, val >= 10 ? 1 : 2)}%`}</span>
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "cpuPeak",
|
||||
accessorFn: (record) => {
|
||||
if (record.sub !== ServiceSubState.Running) {
|
||||
return -1
|
||||
}
|
||||
return record.cpuPeak ?? 0
|
||||
},
|
||||
invertSorting: true,
|
||||
header: ({ column }) => <HeaderButton column={column} name={t`CPU Peak`} Icon={CpuIcon} />,
|
||||
cell: ({ getValue }) => {
|
||||
const val = getValue() as number
|
||||
if (val < 0) {
|
||||
return <span className="ms-1.5 text-muted-foreground">N/A</span>
|
||||
}
|
||||
return <span className="ms-1.5 tabular-nums">{`${decimalString(val, val >= 10 ? 1 : 2)}%`}</span>
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "memory",
|
||||
accessorFn: (record) => record.memory,
|
||||
invertSorting: true,
|
||||
header: ({ column }) => <HeaderButton column={column} name={t`Memory`} Icon={MemoryStickIcon} />,
|
||||
cell: ({ getValue }) => {
|
||||
const val = getValue() as number
|
||||
if (!val) {
|
||||
return <span className="ms-1.5 text-muted-foreground">N/A</span>
|
||||
}
|
||||
const formatted = formatBytes(val, false, undefined, false)
|
||||
return (
|
||||
<span className="ms-1.5 tabular-nums">{`${decimalString(formatted.value, formatted.value >= 10 ? 1 : 2)} ${formatted.unit}`}</span>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "memPeak",
|
||||
accessorFn: (record) => record.memPeak,
|
||||
invertSorting: true,
|
||||
header: ({ column }) => <HeaderButton column={column} name={t`Memory Peak`} Icon={MemoryStickIcon} />,
|
||||
cell: ({ getValue }) => {
|
||||
const val = getValue() as number
|
||||
if (!val) {
|
||||
return <span className="ms-1.5 text-muted-foreground">N/A</span>
|
||||
}
|
||||
const formatted = formatBytes(val, false, undefined, false)
|
||||
return (
|
||||
<span className="ms-1.5 tabular-nums">{`${decimalString(formatted.value, formatted.value >= 10 ? 1 : 2)} ${formatted.unit}`}</span>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "updated",
|
||||
invertSorting: true,
|
||||
accessorFn: (record) => record.updated,
|
||||
header: ({ column }) => <HeaderButton column={column} name={t`Updated`} Icon={ClockIcon} />,
|
||||
cell: ({ getValue }) => {
|
||||
const timestamp = getValue() as number
|
||||
return (
|
||||
<span className="ms-1.5 tabular-nums">
|
||||
{hourWithSeconds(new Date(timestamp).toISOString())}
|
||||
</span>
|
||||
)
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
function HeaderButton({ column, name, Icon }: { column: Column<SystemdRecord>; name: string; Icon: React.ElementType }) {
|
||||
const isSorted = column.getIsSorted()
|
||||
return (
|
||||
<Button
|
||||
className={cn("h-9 px-3 flex items-center gap-2 duration-50", isSorted && "bg-accent/70 light:bg-accent text-accent-foreground/90")}
|
||||
variant="ghost"
|
||||
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
||||
>
|
||||
{Icon && <Icon className="size-4" />}
|
||||
{name}
|
||||
<ArrowUpDownIcon className="size-4" />
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
export function getStatusColor(status: ServiceStatus) {
|
||||
switch (status) {
|
||||
case ServiceStatus.Active:
|
||||
return "bg-green-500"
|
||||
case ServiceStatus.Failed:
|
||||
return "bg-red-500"
|
||||
case ServiceStatus.Reloading:
|
||||
case ServiceStatus.Activating:
|
||||
case ServiceStatus.Deactivating:
|
||||
return "bg-yellow-500"
|
||||
default:
|
||||
return "bg-zinc-500"
|
||||
}
|
||||
}
|
||||
667
internal/site/src/components/systemd-table/systemd-table.tsx
Normal file
667
internal/site/src/components/systemd-table/systemd-table.tsx
Normal file
@@ -0,0 +1,667 @@
|
||||
import { t } from "@lingui/core/macro"
|
||||
import { Trans } from "@lingui/react/macro"
|
||||
import {
|
||||
type ColumnFiltersState,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getFilteredRowModel,
|
||||
getSortedRowModel,
|
||||
type Row,
|
||||
type SortingState,
|
||||
type Table as TableType,
|
||||
useReactTable,
|
||||
type VisibilityState,
|
||||
} from "@tanstack/react-table"
|
||||
import { useVirtualizer, type VirtualItem } from "@tanstack/react-virtual"
|
||||
import { LoaderCircleIcon } from "lucide-react"
|
||||
import { listenKeys } from "nanostores"
|
||||
import { memo, type ReactNode, useEffect, useMemo, useRef, useState } from "react"
|
||||
import { getStatusColor, systemdTableCols } from "@/components/systemd-table/systemd-table-columns"
|
||||
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"
|
||||
import { Card, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Sheet, SheetContent, SheetHeader, SheetTitle } from "@/components/ui/sheet"
|
||||
import { TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
|
||||
import { pb } from "@/lib/api"
|
||||
import { ServiceStatus, ServiceStatusLabels, type ServiceSubState, ServiceSubStateLabels } from "@/lib/enums"
|
||||
import { $allSystemsById } from "@/lib/stores"
|
||||
import { cn, decimalString, formatBytes, useBrowserStorage } from "@/lib/utils"
|
||||
import type { SystemdRecord, SystemdServiceDetails } from "@/types"
|
||||
import { Separator } from "../ui/separator"
|
||||
|
||||
export default function SystemdTable({ systemId }: { systemId?: string }) {
|
||||
const loadTime = Date.now()
|
||||
const [data, setData] = useState<SystemdRecord[]>([])
|
||||
const [sorting, setSorting] = useBrowserStorage<SortingState>(
|
||||
`sort-sd-${systemId ? 1 : 0}`,
|
||||
[{ id: systemId ? "name" : "system", desc: false }],
|
||||
sessionStorage
|
||||
)
|
||||
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([])
|
||||
const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({})
|
||||
const [globalFilter, setGlobalFilter] = useState("")
|
||||
|
||||
// clear old data when systemId changes
|
||||
useEffect(() => {
|
||||
return setData([])
|
||||
}, [systemId])
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const lastUpdated = data[0]?.updated ?? 0
|
||||
|
||||
function fetchData(systemId?: string) {
|
||||
pb.collection<SystemdRecord>("systemd_services")
|
||||
.getList(0, 2000, {
|
||||
fields: "name,state,sub,cpu,cpuPeak,memory,memPeak,updated",
|
||||
filter: systemId ? pb.filter("system={:system}", { system: systemId }) : undefined,
|
||||
})
|
||||
.then(
|
||||
({ items }) =>
|
||||
items.length &&
|
||||
setData((curItems) => {
|
||||
const lastUpdated = Math.max(items[0].updated, items.at(-1)?.updated ?? 0)
|
||||
const systemdNames = new Set()
|
||||
const newItems: SystemdRecord[] = []
|
||||
for (const item of items) {
|
||||
if (Math.abs(lastUpdated - item.updated) < 70_000) {
|
||||
systemdNames.add(item.name)
|
||||
newItems.push(item)
|
||||
}
|
||||
}
|
||||
for (const item of curItems) {
|
||||
if (!systemdNames.has(item.name) && lastUpdated - item.updated < 70_000) {
|
||||
newItems.push(item)
|
||||
}
|
||||
}
|
||||
return newItems
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
// initial load
|
||||
fetchData(systemId)
|
||||
|
||||
// if no systemId, pull system containers after every system update
|
||||
if (!systemId) {
|
||||
return $allSystemsById.listen((_value, _oldValue, systemId) => {
|
||||
// exclude initial load of systems
|
||||
if (Date.now() - loadTime > 500) {
|
||||
fetchData(systemId)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// if systemId, fetch containers after the system is updated
|
||||
return listenKeys($allSystemsById, [systemId], (_newSystems) => {
|
||||
// don't fetch data if the last update is less than 9.5 minutes
|
||||
if (lastUpdated > Date.now() - 9.5 * 60 * 1000) {
|
||||
return
|
||||
}
|
||||
fetchData(systemId)
|
||||
})
|
||||
}, [systemId])
|
||||
|
||||
const table = useReactTable({
|
||||
data,
|
||||
// columns: systemdTableCols.filter((col) => (systemId ? col.id !== "system" : true)),
|
||||
columns: systemdTableCols,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
onSortingChange: setSorting,
|
||||
onColumnFiltersChange: setColumnFilters,
|
||||
onColumnVisibilityChange: setColumnVisibility,
|
||||
defaultColumn: {
|
||||
sortUndefined: "last",
|
||||
size: 100,
|
||||
minSize: 0,
|
||||
},
|
||||
state: {
|
||||
sorting,
|
||||
columnFilters,
|
||||
columnVisibility,
|
||||
globalFilter,
|
||||
},
|
||||
onGlobalFilterChange: setGlobalFilter,
|
||||
globalFilterFn: (row, _columnId, filterValue) => {
|
||||
const service = row.original
|
||||
const systemName = $allSystemsById.get()[service.system]?.name ?? ""
|
||||
const name = service.name ?? ""
|
||||
const statusLabel = ServiceStatusLabels[service.state as ServiceStatus] ?? ""
|
||||
const subState = service.sub ?? ""
|
||||
const searchString = `${systemName} ${name} ${statusLabel} ${subState}`.toLowerCase()
|
||||
|
||||
return (filterValue as string)
|
||||
.toLowerCase()
|
||||
.split(" ")
|
||||
.every((term) => searchString.includes(term))
|
||||
},
|
||||
})
|
||||
|
||||
const rows = table.getRowModel().rows
|
||||
const visibleColumns = table.getVisibleLeafColumns()
|
||||
|
||||
const statusTotals = useMemo(() => {
|
||||
const totals = [0, 0, 0, 0, 0, 0]
|
||||
for (const service of data) {
|
||||
totals[service.state]++
|
||||
}
|
||||
return totals
|
||||
}, [data])
|
||||
|
||||
if (!data.length && !globalFilter) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<Card className="p-6 @container w-full">
|
||||
<CardHeader className="p-0 mb-4">
|
||||
<div className="grid md:flex gap-5 w-full items-end">
|
||||
<div className="px-2 sm:px-1">
|
||||
<CardTitle className="mb-2">
|
||||
<Trans>Systemd Services</Trans>
|
||||
</CardTitle>
|
||||
<CardDescription className="flex items-center">
|
||||
<Trans>Total: {data.length}</Trans>
|
||||
<Separator orientation="vertical" className="h-4 mx-2 bg-primary/40" />
|
||||
<Trans>Failed: {statusTotals[ServiceStatus.Failed]}</Trans>
|
||||
<Separator orientation="vertical" className="h-4 mx-2 bg-primary/40" />
|
||||
<Trans>Updated every 10 minutes.</Trans>
|
||||
</CardDescription>
|
||||
</div>
|
||||
<Input
|
||||
placeholder={t`Filter...`}
|
||||
value={globalFilter}
|
||||
onChange={(e) => setGlobalFilter(e.target.value)}
|
||||
className="ms-auto px-4 w-full max-w-full md:w-64"
|
||||
/>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<div className="rounded-md">
|
||||
<AllSystemdTable table={table} rows={rows} colLength={visibleColumns.length} systemId={systemId} />
|
||||
</div>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
const AllSystemdTable = memo(function AllSystemdTable({
|
||||
table,
|
||||
rows,
|
||||
colLength,
|
||||
systemId,
|
||||
}: {
|
||||
table: TableType<SystemdRecord>
|
||||
rows: Row<SystemdRecord>[]
|
||||
colLength: number
|
||||
systemId?: string
|
||||
}) {
|
||||
// The virtualizer will need a reference to the scrollable container element
|
||||
const scrollRef = useRef<HTMLDivElement>(null)
|
||||
const activeService = useRef<SystemdRecord | null>(null)
|
||||
const [sheetOpen, setSheetOpen] = useState(false)
|
||||
const openSheet = (service: SystemdRecord) => {
|
||||
activeService.current = service
|
||||
setSheetOpen(true)
|
||||
}
|
||||
|
||||
const virtualizer = useVirtualizer<HTMLDivElement, HTMLTableRowElement>({
|
||||
count: rows.length,
|
||||
estimateSize: () => 54,
|
||||
getScrollElement: () => scrollRef.current,
|
||||
overscan: 5,
|
||||
})
|
||||
const virtualRows = virtualizer.getVirtualItems()
|
||||
|
||||
const paddingTop = Math.max(0, virtualRows[0]?.start ?? 0 - virtualizer.options.scrollMargin)
|
||||
const paddingBottom = Math.max(0, virtualizer.getTotalSize() - (virtualRows[virtualRows.length - 1]?.end ?? 0))
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"h-min max-h-[calc(100dvh-17rem)] max-w-full relative overflow-auto border rounded-md",
|
||||
// don't set min height if there are less than 2 rows, do set if we need to display the empty state
|
||||
(!rows.length || rows.length > 2) && "min-h-50"
|
||||
)}
|
||||
ref={scrollRef}
|
||||
>
|
||||
{/* add header height to table size */}
|
||||
<div style={{ height: `${virtualizer.getTotalSize() + 48}px`, paddingTop, paddingBottom }}>
|
||||
<table className="text-sm w-full h-full text-nowrap">
|
||||
<SystemdTableHead table={table} />
|
||||
<TableBody>
|
||||
{rows.length ? (
|
||||
virtualRows.map((virtualRow) => {
|
||||
const row = rows[virtualRow.index]
|
||||
return <SystemdTableRow key={row.id} row={row} virtualRow={virtualRow} openSheet={openSheet} />
|
||||
})
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell colSpan={colLength} className="h-37 text-center pointer-events-none">
|
||||
<Trans>No results.</Trans>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</table>
|
||||
</div>
|
||||
<SystemdSheet
|
||||
sheetOpen={sheetOpen}
|
||||
setSheetOpen={setSheetOpen}
|
||||
activeService={activeService}
|
||||
systemId={systemId}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
function SystemdSheet({
|
||||
sheetOpen,
|
||||
setSheetOpen,
|
||||
activeService,
|
||||
systemId,
|
||||
}: {
|
||||
sheetOpen: boolean
|
||||
setSheetOpen: (open: boolean) => void
|
||||
activeService: React.RefObject<SystemdRecord | null>
|
||||
systemId?: string
|
||||
}) {
|
||||
const service = activeService.current
|
||||
const [details, setDetails] = useState<SystemdServiceDetails | null>(null)
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (!sheetOpen || !service) {
|
||||
return
|
||||
}
|
||||
|
||||
setError(null)
|
||||
|
||||
let cancelled = false
|
||||
setDetails(null)
|
||||
setIsLoading(true)
|
||||
|
||||
pb.send<{ details: SystemdServiceDetails }>("/api/beszel/systemd/info", {
|
||||
query: {
|
||||
system: systemId,
|
||||
service: service.name,
|
||||
},
|
||||
})
|
||||
.then(({ details }) => {
|
||||
if (cancelled) return
|
||||
if (details) {
|
||||
setDetails(details)
|
||||
} else {
|
||||
setDetails(null)
|
||||
setError(t`No results found.`)
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
if (cancelled) return
|
||||
setError(err?.message ?? "Failed to load service details")
|
||||
setDetails(null)
|
||||
})
|
||||
.finally(() => {
|
||||
if (!cancelled) {
|
||||
setIsLoading(false)
|
||||
}
|
||||
})
|
||||
|
||||
return () => {
|
||||
cancelled = true
|
||||
}
|
||||
}, [sheetOpen, service, systemId])
|
||||
|
||||
if (!service) return null
|
||||
|
||||
const statusLabel = ServiceStatusLabels[service.state as ServiceStatus] ?? ""
|
||||
const subStateLabel = ServiceSubStateLabels[service.sub as ServiceSubState] ?? ""
|
||||
|
||||
const notAvailable = <span className="text-muted-foreground">N/A</span>
|
||||
|
||||
const formatMemory = (value?: number | null) => {
|
||||
if (value === undefined || value === null) {
|
||||
return value === null ? t`Unlimited` : undefined
|
||||
}
|
||||
const { value: convertedValue, unit } = formatBytes(value, false, undefined, false)
|
||||
const digits = convertedValue >= 10 ? 1 : 2
|
||||
return `${decimalString(convertedValue, digits)} ${unit}`
|
||||
}
|
||||
|
||||
const formatCpuTime = (ns?: number) => {
|
||||
if (!ns) return undefined
|
||||
const seconds = ns / 1_000_000_000
|
||||
if (seconds >= 3600) {
|
||||
const hours = Math.floor(seconds / 3600)
|
||||
const minutes = Math.floor((seconds % 3600) / 60)
|
||||
const secs = Math.floor(seconds % 60)
|
||||
return [hours ? `${hours}h` : null, minutes ? `${minutes}m` : null, secs ? `${secs}s` : null]
|
||||
.filter(Boolean)
|
||||
.join(" ")
|
||||
}
|
||||
if (seconds >= 60) {
|
||||
const minutes = Math.floor(seconds / 60)
|
||||
const secs = Math.floor(seconds % 60)
|
||||
return `${minutes}m ${secs}s`
|
||||
}
|
||||
if (seconds >= 1) {
|
||||
return `${decimalString(seconds, 2)}s`
|
||||
}
|
||||
return `${decimalString(seconds * 1000, 2)}ms`
|
||||
}
|
||||
|
||||
const formatTasks = (current?: number, max?: number) => {
|
||||
const hasCurrent = typeof current === "number" && current >= 0
|
||||
const hasMax = typeof max === "number" && max > 0 && max !== null
|
||||
if (!hasCurrent && !hasMax) {
|
||||
return undefined
|
||||
}
|
||||
return (
|
||||
<>
|
||||
{hasCurrent ? current : notAvailable}
|
||||
{hasMax && (
|
||||
<span className="text-muted-foreground ms-1.5">
|
||||
{`(${t`limit`}: ${max})`}
|
||||
</span>
|
||||
)}
|
||||
{max === null && (
|
||||
<span className="text-muted-foreground ms-1.5">
|
||||
{`(${t`limit`}: ${t`Unlimited`.toLowerCase()})`}
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const formatTimestamp = (timestamp?: number) => {
|
||||
if (!timestamp) return undefined
|
||||
// systemd timestamps are in microseconds, convert to milliseconds for JavaScript Date
|
||||
const date = new Date(timestamp / 1000)
|
||||
if (Number.isNaN(date.getTime())) return undefined
|
||||
return date.toLocaleString()
|
||||
}
|
||||
|
||||
const activeStateValue = (() => {
|
||||
const stateText = details?.ActiveState
|
||||
? details.SubState
|
||||
? `${details.ActiveState} (${details.SubState})`
|
||||
: details.ActiveState
|
||||
: subStateLabel
|
||||
? `${statusLabel} (${subStateLabel})`
|
||||
: statusLabel
|
||||
|
||||
for (const [index, status] of ServiceStatusLabels.entries()) {
|
||||
if (details?.ActiveState?.toLowerCase() === status.toLowerCase()) {
|
||||
service.state = index as ServiceStatus
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<div className={cn("w-2 h-2 rounded-full flex-shrink-0", getStatusColor(service.state))} />
|
||||
{stateText}
|
||||
</div>
|
||||
)
|
||||
})()
|
||||
|
||||
const statusTextValue = details?.Result
|
||||
|
||||
const cpuTime = formatCpuTime(details?.CPUUsageNSec)
|
||||
const tasks = formatTasks(details?.TasksCurrent, details?.TasksMax)
|
||||
const memoryCurrent = formatMemory(details?.MemoryCurrent)
|
||||
const memoryPeak = formatMemory(details?.MemoryPeak)
|
||||
const memoryLimit = formatMemory(details?.MemoryLimit)
|
||||
const restartsValue = typeof details?.NRestarts === "number" ? details.NRestarts : undefined
|
||||
const mainPidValue = typeof details?.MainPID === "number" && details.MainPID > 0 ? details.MainPID : undefined
|
||||
const execMainPidValue =
|
||||
typeof details?.ExecMainPID === "number" && details.ExecMainPID > 0 && details.ExecMainPID !== details?.MainPID
|
||||
? details.ExecMainPID
|
||||
: undefined
|
||||
const activeEnterTimestamp = formatTimestamp(details?.ActiveEnterTimestamp)
|
||||
const activeExitTimestamp = formatTimestamp(details?.ActiveExitTimestamp)
|
||||
const inactiveEnterTimestamp = formatTimestamp(details?.InactiveEnterTimestamp)
|
||||
const execMainStartTimestamp = undefined // Property not available in current systemd interface
|
||||
|
||||
const renderRow = (key: string, label: ReactNode, value?: ReactNode, alwaysShow = false) => {
|
||||
if (!alwaysShow && (value === undefined || value === null || value === "")) {
|
||||
return null
|
||||
}
|
||||
return (
|
||||
<tr key={key} className="border-b last:border-b-0">
|
||||
<td className="px-3 py-2 font-medium bg-muted dark:bg-muted/40 align-top w-35">{label}</td>
|
||||
<td className="px-3 py-2">{value ?? notAvailable}</td>
|
||||
</tr>
|
||||
)
|
||||
}
|
||||
|
||||
const capitalize = (str: string) => `${str.charAt(0).toUpperCase()}${str.slice(1).toLowerCase()}`
|
||||
|
||||
return (
|
||||
<Sheet open={sheetOpen} onOpenChange={setSheetOpen}>
|
||||
<SheetContent className="w-full sm:max-w-220 p-6 overflow-y-auto">
|
||||
<SheetHeader className="p-0">
|
||||
<SheetTitle>
|
||||
<Trans>Service Details</Trans>
|
||||
</SheetTitle>
|
||||
</SheetHeader>
|
||||
<div className="grid gap-6">
|
||||
{isLoading && (
|
||||
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
||||
<LoaderCircleIcon className="size-4 animate-spin" />
|
||||
<Trans>Loading...</Trans>
|
||||
</div>
|
||||
)}
|
||||
{error && (
|
||||
<Alert className="border-destructive/50 text-destructive dark:border-destructive/60 dark:text-destructive">
|
||||
<AlertTitle>
|
||||
<Trans>Error</Trans>
|
||||
</AlertTitle>
|
||||
<AlertDescription>{error}</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<div>
|
||||
<div className="border rounded-md">
|
||||
<table className="w-full text-sm">
|
||||
<tbody>
|
||||
{renderRow("name", t`Name`, service.name, true)}
|
||||
{renderRow("description", t`Description`, details?.Description, true)}
|
||||
{renderRow("loadState", t`Load state`, details?.LoadState, true)}
|
||||
{renderRow(
|
||||
"bootState",
|
||||
t`Boot state`,
|
||||
<div className="flex items-center">
|
||||
{details?.UnitFileState}
|
||||
{details?.UnitFilePreset && (
|
||||
<span className="text-muted-foreground ms-1.5">(preset: {details?.UnitFilePreset})</span>
|
||||
)}
|
||||
</div>,
|
||||
true
|
||||
)}
|
||||
{renderRow("unitFile", t`Unit file`, details?.FragmentPath, true)}
|
||||
{renderRow("active", t`Active state`, activeStateValue, true)}
|
||||
{renderRow("status", t`Status`, statusTextValue, true)}
|
||||
{renderRow(
|
||||
"documentation",
|
||||
t`Documentation`,
|
||||
Array.isArray(details?.Documentation) && details.Documentation.length > 0
|
||||
? details.Documentation.join(", ")
|
||||
: undefined
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="text-sm font-medium mb-3">
|
||||
<Trans>Runtime Metrics</Trans>
|
||||
</h3>
|
||||
<div className="border rounded-md">
|
||||
<table className="w-full text-sm">
|
||||
<tbody>
|
||||
{renderRow("mainPid", t`Main PID`, mainPidValue, true)}
|
||||
{renderRow("execMainPid", t`Exec main PID`, execMainPidValue)}
|
||||
{renderRow("tasks", t`Tasks`, tasks, true)}
|
||||
{renderRow("cpuTime", t`CPU time`, cpuTime)}
|
||||
{renderRow("memory", t`Memory`, memoryCurrent, true)}
|
||||
{renderRow("memoryPeak", capitalize(t`Memory Peak`), memoryPeak)}
|
||||
{renderRow("memoryLimit", t`Memory limit`, memoryLimit)}
|
||||
{renderRow("restarts", t`Restarts`, restartsValue, true)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="hidden has-[tr]:block">
|
||||
<h3 className="text-sm font-medium mb-3">
|
||||
<Trans>Relationships</Trans>
|
||||
</h3>
|
||||
<div className="border rounded-md">
|
||||
<table className="w-full text-sm">
|
||||
<tbody>
|
||||
{renderRow(
|
||||
"wants",
|
||||
t`Wants`,
|
||||
Array.isArray(details?.Wants) && details.Wants.length > 0 ? details.Wants.join(", ") : undefined
|
||||
)}
|
||||
{renderRow(
|
||||
"requires",
|
||||
t`Requires`,
|
||||
Array.isArray(details?.Requires) && details.Requires.length > 0
|
||||
? details.Requires.join(", ")
|
||||
: undefined
|
||||
)}
|
||||
{renderRow(
|
||||
"requiredBy",
|
||||
t`Required by`,
|
||||
Array.isArray(details?.RequiredBy) && details.RequiredBy.length > 0
|
||||
? details.RequiredBy.join(", ")
|
||||
: undefined
|
||||
)}
|
||||
{renderRow(
|
||||
"conflicts",
|
||||
t`Conflicts`,
|
||||
Array.isArray(details?.Conflicts) && details.Conflicts.length > 0
|
||||
? details.Conflicts.join(", ")
|
||||
: undefined
|
||||
)}
|
||||
{renderRow(
|
||||
"before",
|
||||
t`Before`,
|
||||
Array.isArray(details?.Before) && details.Before.length > 0 ? details.Before.join(", ") : undefined
|
||||
)}
|
||||
{renderRow(
|
||||
"after",
|
||||
t`After`,
|
||||
Array.isArray(details?.After) && details.After.length > 0 ? details.After.join(", ") : undefined
|
||||
)}
|
||||
{renderRow(
|
||||
"triggers",
|
||||
t`Triggers`,
|
||||
Array.isArray(details?.Triggers) && details.Triggers.length > 0
|
||||
? details.Triggers.join(", ")
|
||||
: undefined
|
||||
)}
|
||||
{renderRow(
|
||||
"triggeredBy",
|
||||
t`Triggered by`,
|
||||
Array.isArray(details?.TriggeredBy) && details.TriggeredBy.length > 0
|
||||
? details.TriggeredBy.join(", ")
|
||||
: undefined
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="hidden has-[tr]:block">
|
||||
<h3 className="text-sm font-medium mb-3">
|
||||
<Trans>Lifecycle</Trans>
|
||||
</h3>
|
||||
<div className="border rounded-md">
|
||||
<table className="w-full text-sm">
|
||||
<tbody>
|
||||
{renderRow("activeSince", t`Became active`, activeEnterTimestamp)}
|
||||
{service.state !== ServiceStatus.Active &&
|
||||
renderRow("lastActive", t`Exited active`, activeExitTimestamp)}
|
||||
{renderRow("inactiveSince", t`Became inactive`, inactiveEnterTimestamp)}
|
||||
{renderRow("execMainStart", t`Process started`, execMainStartTimestamp)}
|
||||
{/* {renderRow("invocationId", t`Invocation ID`, details?.InvocationID)} */}
|
||||
{/* {renderRow("freezerState", t`Freezer State`, details?.FreezerState)} */}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="hidden has-[tr]:block">
|
||||
<h3 className="text-sm font-medium mb-3">
|
||||
<Trans>Capabilities</Trans>
|
||||
</h3>
|
||||
<div className="border rounded-md">
|
||||
<table className="w-full text-sm">
|
||||
<tbody>
|
||||
{renderRow("canStart", t`Can start`, details?.CanStart ? t`Yes` : t`No`)}
|
||||
{renderRow("canStop", t`Can stop`, details?.CanStop ? t`Yes` : t`No`)}
|
||||
{renderRow("canReload", t`Can reload`, details?.CanReload ? t`Yes` : t`No`)}
|
||||
{/* {renderRow("refuseManualStart", t`Refuse Manual Start`, details?.RefuseManualStart ? t`Yes` : t`No`)}
|
||||
{renderRow("refuseManualStop", t`Refuse Manual Stop`, details?.RefuseManualStop ? t`Yes` : t`No`)} */}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
)
|
||||
}
|
||||
|
||||
function SystemdTableHead({ table }: { table: TableType<SystemdRecord> }) {
|
||||
return (
|
||||
<TableHeader className="sticky top-0 z-50 w-full border-b-2">
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<tr key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => {
|
||||
return (
|
||||
<TableHead className="px-2" key={header.id}>
|
||||
{header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}
|
||||
</TableHead>
|
||||
)
|
||||
})}
|
||||
</tr>
|
||||
))}
|
||||
</TableHeader>
|
||||
)
|
||||
}
|
||||
|
||||
const SystemdTableRow = memo(function SystemdTableRow({
|
||||
row,
|
||||
virtualRow,
|
||||
openSheet,
|
||||
}: {
|
||||
row: Row<SystemdRecord>
|
||||
virtualRow: VirtualItem
|
||||
openSheet: (service: SystemdRecord) => void
|
||||
}) {
|
||||
return (
|
||||
<TableRow
|
||||
data-state={row.getIsSelected() && "selected"}
|
||||
className="cursor-pointer transition-opacity"
|
||||
onClick={() => openSheet(row.original)}
|
||||
>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<TableCell
|
||||
key={cell.id}
|
||||
className="py-0"
|
||||
style={{
|
||||
height: virtualRow.size,
|
||||
}}
|
||||
>
|
||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
)
|
||||
})
|
||||
@@ -1,3 +1,4 @@
|
||||
/** biome-ignore-all lint/correctness/useHookAtTopLevel: <explanation> */
|
||||
import { t } from "@lingui/core/macro"
|
||||
import { Trans, useLingui } from "@lingui/react/macro"
|
||||
import { useStore } from "@nanostores/react"
|
||||
@@ -16,10 +17,12 @@ import {
|
||||
PenBoxIcon,
|
||||
PlayCircleIcon,
|
||||
ServerIcon,
|
||||
TerminalSquareIcon,
|
||||
Trash2Icon,
|
||||
WifiIcon,
|
||||
} from "lucide-react"
|
||||
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 { $longestSystemNameLen, $userSettings } from "@/lib/stores"
|
||||
@@ -68,7 +71,7 @@ const STATUS_COLORS = {
|
||||
* @param viewMode - "table" or "grid"
|
||||
* @returns - Column definitions for the systems table
|
||||
*/
|
||||
export default function SystemsTableColumns(viewMode: "table" | "grid"): ColumnDef<SystemRecord>[] {
|
||||
export function SystemsTableColumns(viewMode: "table" | "grid"): ColumnDef<SystemRecord>[] {
|
||||
return [
|
||||
{
|
||||
// size: 200,
|
||||
@@ -133,7 +136,7 @@ export default function SystemsTableColumns(viewMode: "table" | "grid"): ColumnD
|
||||
header: sortableHeader,
|
||||
},
|
||||
{
|
||||
accessorFn: ({ info }) => info.cpu,
|
||||
accessorFn: ({ info }) => info.cpu || undefined,
|
||||
id: "cpu",
|
||||
name: () => t`CPU`,
|
||||
cell: TableCellWithMeter,
|
||||
@@ -142,7 +145,7 @@ export default function SystemsTableColumns(viewMode: "table" | "grid"): ColumnD
|
||||
},
|
||||
{
|
||||
// accessorKey: "info.mp",
|
||||
accessorFn: ({ info }) => info.mp,
|
||||
accessorFn: ({ info }) => info.mp || undefined,
|
||||
id: "memory",
|
||||
name: () => t`Memory`,
|
||||
cell: TableCellWithMeter,
|
||||
@@ -150,15 +153,15 @@ export default function SystemsTableColumns(viewMode: "table" | "grid"): ColumnD
|
||||
header: sortableHeader,
|
||||
},
|
||||
{
|
||||
accessorFn: ({ info }) => info.dp,
|
||||
accessorFn: ({ info }) => info.dp || undefined,
|
||||
id: "disk",
|
||||
name: () => t`Disk`,
|
||||
cell: TableCellWithMeter,
|
||||
cell: DiskCellWithMultiple,
|
||||
Icon: HardDriveIcon,
|
||||
header: sortableHeader,
|
||||
},
|
||||
{
|
||||
accessorFn: ({ info }) => info.g,
|
||||
accessorFn: ({ info }) => info.g || undefined,
|
||||
id: "gpu",
|
||||
name: () => "GPU",
|
||||
cell: TableCellWithMeter,
|
||||
@@ -171,9 +174,9 @@ export default function SystemsTableColumns(viewMode: "table" | "grid"): ColumnD
|
||||
const sum = info.la?.reduce((acc, curr) => acc + curr, 0)
|
||||
// TODO: remove this in future release in favor of la array
|
||||
if (!sum) {
|
||||
return (info.l1 ?? 0) + (info.l5 ?? 0) + (info.l15 ?? 0)
|
||||
return (info.l1 ?? 0) + (info.l5 ?? 0) + (info.l15 ?? 0) || undefined
|
||||
}
|
||||
return sum
|
||||
return sum || undefined
|
||||
},
|
||||
name: () => t({ message: "Load Avg", comment: "Short label for load average" }),
|
||||
size: 0,
|
||||
@@ -216,7 +219,7 @@ export default function SystemsTableColumns(viewMode: "table" | "grid"): ColumnD
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorFn: ({ info }) => info.bb || (info.b || 0) * 1024 * 1024,
|
||||
accessorFn: ({ info }) => info.bb || (info.b || 0) * 1024 * 1024 || undefined,
|
||||
id: "net",
|
||||
name: () => t`Net`,
|
||||
size: 0,
|
||||
@@ -228,7 +231,7 @@ export default function SystemsTableColumns(viewMode: "table" | "grid"): ColumnD
|
||||
if (sys.status === SystemStatus.Paused) {
|
||||
return null
|
||||
}
|
||||
const { value, unit } = formatBytes(info.getValue() as number, true, userSettings.unitNet, false)
|
||||
const { value, unit } = formatBytes((info.getValue() || 0) as number, true, userSettings.unitNet, false)
|
||||
return (
|
||||
<span className="tabular-nums whitespace-nowrap">
|
||||
{decimalString(value, value >= 100 ? 1 : 2)} {unit}
|
||||
@@ -258,11 +261,49 @@ export default function SystemsTableColumns(viewMode: "table" | "grid"): ColumnD
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorFn: ({ info }) => info.sv?.[0],
|
||||
id: "services",
|
||||
name: () => t`Services`,
|
||||
size: 50,
|
||||
Icon: TerminalSquareIcon,
|
||||
header: sortableHeader,
|
||||
hideSort: true,
|
||||
sortingFn: (a, b) => {
|
||||
// sort priorities: 1) failed services, 2) total services
|
||||
const [totalCountA, numFailedA] = a.original.info.sv ?? [0, 0]
|
||||
const [totalCountB, numFailedB] = b.original.info.sv ?? [0, 0]
|
||||
if (numFailedA !== numFailedB) {
|
||||
return numFailedA - numFailedB
|
||||
}
|
||||
return totalCountA - totalCountB
|
||||
},
|
||||
cell(info) {
|
||||
const sys = info.row.original
|
||||
const [totalCount, numFailed] = sys.info.sv ?? [0, 0]
|
||||
if (sys.status !== SystemStatus.Up || totalCount === 0) {
|
||||
return null
|
||||
}
|
||||
return (
|
||||
<span className="tabular-nums whitespace-nowrap flex gap-1.5 items-center">
|
||||
<span
|
||||
className={cn("block size-2 rounded-full", {
|
||||
[STATUS_COLORS[SystemStatus.Down]]: numFailed > 0,
|
||||
[STATUS_COLORS[SystemStatus.Up]]: numFailed === 0,
|
||||
})}
|
||||
/>
|
||||
{totalCount}{" "}
|
||||
<span className="text-muted-foreground text-sm -ms-0.5">
|
||||
({t`Failed`.toLowerCase()}: {numFailed})
|
||||
</span>
|
||||
</span>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorFn: ({ info }) => info.v,
|
||||
id: "agent",
|
||||
name: () => t`Agent`,
|
||||
// invertSorting: true,
|
||||
size: 50,
|
||||
Icon: WifiIcon,
|
||||
hideSort: true,
|
||||
@@ -354,6 +395,101 @@ function TableCellWithMeter(info: CellContext<SystemRecord, unknown>) {
|
||||
)
|
||||
}
|
||||
|
||||
function DiskCellWithMultiple(info: CellContext<SystemRecord, unknown>) {
|
||||
const { info: sysInfo, status, id } = info.row.original
|
||||
const extraFs = Object.entries(sysInfo.efs ?? {})
|
||||
|
||||
if (extraFs.length === 0) {
|
||||
return TableCellWithMeter(info)
|
||||
}
|
||||
|
||||
const rootDiskPct = sysInfo.dp
|
||||
|
||||
// sort extra disks by percentage descending
|
||||
extraFs.sort((a, b) => b[1] - a[1])
|
||||
|
||||
function getIndicatorColor(pct: number) {
|
||||
const threshold = getMeterState(pct)
|
||||
return (
|
||||
(status !== SystemStatus.Up && STATUS_COLORS.paused) ||
|
||||
(threshold === MeterState.Good && STATUS_COLORS.up) ||
|
||||
(threshold === MeterState.Warn && STATUS_COLORS.pending) ||
|
||||
STATUS_COLORS.down
|
||||
)
|
||||
}
|
||||
|
||||
function getMeterClass(pct: number) {
|
||||
return cn("h-full", getIndicatorColor(pct))
|
||||
}
|
||||
|
||||
// Extra disk indicators (max 3 dots - one per state if any disk exists in range)
|
||||
const stateColors = [STATUS_COLORS.up, STATUS_COLORS.pending, STATUS_COLORS.down]
|
||||
const extraDiskIndicators =
|
||||
status !== SystemStatus.Up
|
||||
? []
|
||||
: [...new Set(extraFs.map(([, pct]) => getMeterState(pct)))].sort().map((state) => stateColors[state])
|
||||
|
||||
return (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Link
|
||||
href={getPagePath($router, "system", { id })}
|
||||
tabIndex={-1}
|
||||
className="flex flex-col gap-0.5 w-full relative z-10"
|
||||
>
|
||||
<div className="flex gap-2 items-center tabular-nums tracking-tight">
|
||||
<span className="min-w-8 shrink-0">{decimalString(rootDiskPct, rootDiskPct >= 10 ? 1 : 2)}%</span>
|
||||
<span className="flex-1 min-w-8 flex items-center gap-0.5 px-1 justify-end bg-muted h-[1em] rounded-sm overflow-hidden relative">
|
||||
{/* Root disk */}
|
||||
<span
|
||||
className={cn("absolute inset-0", getMeterClass(rootDiskPct))}
|
||||
style={{ width: `${rootDiskPct}%` }}
|
||||
></span>
|
||||
{/* Extra disk indicators */}
|
||||
{extraDiskIndicators.map((color) => (
|
||||
<span
|
||||
key={color}
|
||||
className={cn("size-1.5 rounded-full shrink-0 outline-[0.5px] outline-muted", color)}
|
||||
/>
|
||||
))}
|
||||
</span>
|
||||
</div>
|
||||
</Link>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="right" className="max-w-xs pb-2">
|
||||
<div className="grid gap-1">
|
||||
<div className="grid gap-0.5">
|
||||
<div className="text-[0.65rem] text-muted-foreground uppercase tracking-wide tabular-nums">
|
||||
<Trans context="Root disk label">Root</Trans>
|
||||
</div>
|
||||
<div className="flex gap-2 items-center tabular-nums text-xs">
|
||||
<span className="min-w-7">{decimalString(rootDiskPct, rootDiskPct >= 10 ? 1 : 2)}%</span>
|
||||
<span className="flex-1 min-w-12 grid bg-muted h-2.5 rounded-sm overflow-hidden">
|
||||
<span className={getMeterClass(rootDiskPct)} style={{ width: `${rootDiskPct}%` }}></span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{extraFs.map(([name, pct]) => {
|
||||
return (
|
||||
<div key={name} className="grid gap-0.5">
|
||||
<div className="text-[0.65rem] max-w-40 text-muted-foreground uppercase tracking-wide truncate">
|
||||
{name}
|
||||
</div>
|
||||
<div className="flex gap-2 items-center tabular-nums text-xs">
|
||||
<span className="min-w-7">{decimalString(pct, pct >= 10 ? 1 : 2)}%</span>
|
||||
<span className="flex-1 min-w-12 grid bg-muted h-2.5 rounded-sm overflow-hidden">
|
||||
<span className={getMeterClass(pct)} style={{ width: `${pct}%` }}></span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
|
||||
export function IndicatorDot({ system, className }: { system: SystemRecord; className?: ClassValue }) {
|
||||
className ||= STATUS_COLORS[system.status as keyof typeof STATUS_COLORS] || ""
|
||||
return (
|
||||
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
LayoutGridIcon,
|
||||
LayoutListIcon,
|
||||
Settings2Icon,
|
||||
XIcon,
|
||||
} from "lucide-react"
|
||||
import { memo, useEffect, useMemo, useRef, useState } from "react"
|
||||
import { Button } from "@/components/ui/button"
|
||||
@@ -47,7 +48,7 @@ import type { SystemRecord } from "@/types"
|
||||
import AlertButton from "../alerts/alert-button"
|
||||
import { $router, Link } from "../router"
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../ui/card"
|
||||
import SystemsTableColumns, { ActionsButton, IndicatorDot } from "./systems-table-columns"
|
||||
import { SystemsTableColumns, ActionsButton, IndicatorDot } from "./systems-table-columns"
|
||||
|
||||
type ViewMode = "table" | "grid"
|
||||
type StatusFilter = "all" | SystemRecord["status"]
|
||||
@@ -60,7 +61,7 @@ export default function SystemsTable() {
|
||||
const upSystems = $upSystems.get()
|
||||
const pausedSystems = $pausedSystems.get()
|
||||
const { i18n, t } = useLingui()
|
||||
const [filter, setFilter] = useState<string>()
|
||||
const [filter, setFilter] = useState<string>("")
|
||||
const [statusFilter, setStatusFilter] = useState<StatusFilter>("all")
|
||||
const [sorting, setSorting] = useBrowserStorage<SortingState>(
|
||||
"sortMode",
|
||||
@@ -145,7 +146,26 @@ export default function SystemsTable() {
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2 ms-auto w-full md:w-80">
|
||||
<Input placeholder={t`Filter...`} onChange={(e) => setFilter(e.target.value)} className="px-4" />
|
||||
<div className="relative flex-1">
|
||||
<Input
|
||||
placeholder={t`Filter...`}
|
||||
onChange={(e) => setFilter(e.target.value)}
|
||||
value={filter}
|
||||
className="ps-4 pe-10 w-full"
|
||||
/>
|
||||
{filter && (
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
aria-label={t`Clear`}
|
||||
className="absolute right-1 top-1/2 -translate-y-1/2 h-7 w-7 text-muted-foreground"
|
||||
onClick={() => setFilter("")}
|
||||
>
|
||||
<XIcon className="h-4 w-4" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="outline">
|
||||
@@ -278,6 +298,7 @@ export default function SystemsTable() {
|
||||
upSystemsLength,
|
||||
downSystemsLength,
|
||||
pausedSystemsLength,
|
||||
filter,
|
||||
])
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import type { JSX } from "react"
|
||||
import { useLingui } from "@lingui/react/macro"
|
||||
import * as React from "react"
|
||||
import * as RechartsPrimitive from "recharts"
|
||||
import { chartTimeData, cn } from "@/lib/utils"
|
||||
import type { ChartData } from "@/types"
|
||||
import { Separator } from "./separator"
|
||||
import { AxisDomain } from "recharts/types/util/types"
|
||||
|
||||
// Format: { THEME_NAME: CSS_SELECTOR }
|
||||
const THEMES = { light: "", dark: ".dark" } as const
|
||||
@@ -100,6 +103,8 @@ const ChartTooltipContent = React.forwardRef<
|
||||
filter?: string
|
||||
contentFormatter?: (item: any, key: string) => React.ReactNode | string
|
||||
truncate?: boolean
|
||||
showTotal?: boolean
|
||||
totalLabel?: React.ReactNode
|
||||
}
|
||||
>(
|
||||
(
|
||||
@@ -121,11 +126,16 @@ const ChartTooltipContent = React.forwardRef<
|
||||
itemSorter,
|
||||
contentFormatter: content = undefined,
|
||||
truncate = false,
|
||||
showTotal = false,
|
||||
totalLabel,
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
// const { config } = useChart()
|
||||
const config = {}
|
||||
const { t } = useLingui()
|
||||
const totalLabelNode = totalLabel ?? t`Total`
|
||||
const totalName = typeof totalLabelNode === "string" ? totalLabelNode : t`Total`
|
||||
|
||||
React.useMemo(() => {
|
||||
if (filter) {
|
||||
@@ -141,6 +151,76 @@ const ChartTooltipContent = React.forwardRef<
|
||||
}
|
||||
}, [itemSorter, payload])
|
||||
|
||||
const totalValueDisplay = React.useMemo(() => {
|
||||
if (!showTotal || !payload?.length) {
|
||||
return null
|
||||
}
|
||||
|
||||
let totalValue = 0
|
||||
let hasNumericValue = false
|
||||
const aggregatedNestedValues: Record<string, number> = {}
|
||||
|
||||
for (const item of payload) {
|
||||
const numericValue = typeof item.value === "number" ? item.value : Number(item.value)
|
||||
if (Number.isFinite(numericValue)) {
|
||||
totalValue += numericValue
|
||||
hasNumericValue = true
|
||||
}
|
||||
|
||||
if (content && item?.payload) {
|
||||
const payloadKey = `${nameKey || item.name || item.dataKey || "value"}`
|
||||
const nestedPayload = (item.payload as Record<string, unknown> | undefined)?.[payloadKey]
|
||||
|
||||
if (nestedPayload && typeof nestedPayload === "object") {
|
||||
for (const [nestedKey, nestedValue] of Object.entries(nestedPayload)) {
|
||||
if (typeof nestedValue === "number" && Number.isFinite(nestedValue)) {
|
||||
aggregatedNestedValues[nestedKey] = (aggregatedNestedValues[nestedKey] ?? 0) + nestedValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasNumericValue) {
|
||||
return null
|
||||
}
|
||||
|
||||
const totalKey = "__total__"
|
||||
const totalItem: any = {
|
||||
value: totalValue,
|
||||
name: totalName,
|
||||
dataKey: totalKey,
|
||||
color,
|
||||
}
|
||||
|
||||
if (content) {
|
||||
const basePayload =
|
||||
payload[0]?.payload && typeof payload[0].payload === "object"
|
||||
? { ...(payload[0].payload as Record<string, unknown>) }
|
||||
: {}
|
||||
totalItem.payload = {
|
||||
...basePayload,
|
||||
[totalKey]: aggregatedNestedValues,
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof formatter === "function") {
|
||||
return formatter(
|
||||
totalValue,
|
||||
totalName,
|
||||
totalItem,
|
||||
payload.length,
|
||||
totalItem.payload ?? payload[0]?.payload
|
||||
)
|
||||
}
|
||||
|
||||
if (content) {
|
||||
return content(totalItem, totalKey)
|
||||
}
|
||||
|
||||
return `${totalValue.toLocaleString()}${unit ?? ""}`
|
||||
}, [color, content, formatter, nameKey, payload, showTotal, totalName, unit])
|
||||
|
||||
const tooltipLabel = React.useMemo(() => {
|
||||
if (hideLabel || !payload?.length) {
|
||||
return null
|
||||
@@ -242,6 +322,15 @@ const ChartTooltipContent = React.forwardRef<
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
{totalValueDisplay ? (
|
||||
<>
|
||||
<Separator className="mt-0.5" />
|
||||
<div className="flex items-center justify-between gap-2 -mt-0.75 font-medium">
|
||||
<span className="text-muted-foreground ps-3">{totalLabelNode}</span>
|
||||
<span>{totalValueDisplay}</span>
|
||||
</div>
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
@@ -257,14 +346,17 @@ const ChartLegendContent = React.forwardRef<
|
||||
Pick<RechartsPrimitive.LegendProps, "payload" | "verticalAlign"> & {
|
||||
hideIcon?: boolean
|
||||
nameKey?: string
|
||||
reverse?: boolean
|
||||
}
|
||||
>(({ className, payload, verticalAlign = "bottom" }, ref) => {
|
||||
>(({ className, payload, verticalAlign = "bottom", reverse = false }, ref) => {
|
||||
// const { config } = useChart()
|
||||
|
||||
if (!payload?.length) {
|
||||
return null
|
||||
}
|
||||
|
||||
const reversedPayload = reverse ? [...payload].reverse() : payload
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
@@ -274,7 +366,7 @@ const ChartLegendContent = React.forwardRef<
|
||||
className
|
||||
)}
|
||||
>
|
||||
{payload.map((item) => {
|
||||
{reversedPayload.map((item) => {
|
||||
// const key = `${nameKey || item.dataKey || 'value'}`
|
||||
// const itemConfig = getPayloadConfigFromPayload(config, item, key)
|
||||
|
||||
@@ -363,3 +455,15 @@ export {
|
||||
xAxis,
|
||||
// ChartStyle,
|
||||
}
|
||||
|
||||
export function pinnedAxisDomain(): AxisDomain {
|
||||
return [0, (dataMax: number) => {
|
||||
if (dataMax > 10) {
|
||||
return Math.round(dataMax)
|
||||
}
|
||||
if (dataMax > 1) {
|
||||
return Math.round(dataMax / 0.1) * 0.1
|
||||
}
|
||||
return dataMax
|
||||
}]
|
||||
}
|
||||
@@ -32,6 +32,9 @@
|
||||
--chart-4: hsl(280 65% 60%);
|
||||
--chart-5: hsl(340 75% 55%);
|
||||
--table-header: hsl(225, 6%, 97%);
|
||||
--chart-saturation: 65%;
|
||||
--chart-lightness: 50%;
|
||||
--container: 1500px;
|
||||
}
|
||||
|
||||
.dark {
|
||||
@@ -51,11 +54,13 @@
|
||||
--accent: hsl(220 5% 15.5%);
|
||||
--accent-foreground: hsl(220 2% 98%);
|
||||
--destructive: hsl(0 62% 46%);
|
||||
--border: hsl(220 3% 16%);
|
||||
--border: hsl(220 3% 17%);
|
||||
--input: hsl(220 4% 22%);
|
||||
--ring: hsl(220 4% 80%);
|
||||
--table-header: hsl(220, 6%, 13%);
|
||||
--radius: 0.8rem;
|
||||
--chart-saturation: 60%;
|
||||
--chart-lightness: 55%;
|
||||
}
|
||||
|
||||
@theme inline {
|
||||
@@ -112,7 +117,6 @@
|
||||
}
|
||||
|
||||
@layer utilities {
|
||||
|
||||
/* Fonts */
|
||||
@supports (font-variation-settings: normal) {
|
||||
:root {
|
||||
@@ -137,6 +141,7 @@
|
||||
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
font-variant-ligatures: no-contextual;
|
||||
}
|
||||
|
||||
button {
|
||||
@@ -145,7 +150,8 @@
|
||||
}
|
||||
|
||||
@utility container {
|
||||
@apply max-w-370 mx-auto px-4;
|
||||
max-width: var(--container);
|
||||
@apply mx-auto px-4;
|
||||
}
|
||||
|
||||
@utility link {
|
||||
@@ -155,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 {
|
||||
@@ -168,4 +170,4 @@
|
||||
|
||||
.recharts-yAxis {
|
||||
@apply tabular-nums;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { t } from "@lingui/core/macro"
|
||||
import { CpuIcon, HardDriveIcon, HourglassIcon, MemoryStickIcon, ServerIcon, ThermometerIcon } from "lucide-react"
|
||||
import type { RecordSubscription } from "pocketbase"
|
||||
import { EthernetIcon } from "@/components/ui/icons"
|
||||
import { EthernetIcon, GpuIcon } from "@/components/ui/icons"
|
||||
import { $alerts } from "@/lib/stores"
|
||||
import type { AlertInfo, AlertRecord } from "@/types"
|
||||
import { pb } from "./api"
|
||||
@@ -41,6 +41,12 @@ export const alertInfo: Record<string, AlertInfo> = {
|
||||
desc: () => t`Triggers when combined up/down exceeds a threshold`,
|
||||
max: 125,
|
||||
},
|
||||
GPU: {
|
||||
name: () => t`GPU Usage`,
|
||||
unit: "%",
|
||||
icon: GpuIcon,
|
||||
desc: () => t`Triggers when GPU usage exceeds a threshold`,
|
||||
},
|
||||
Temperature: {
|
||||
name: () => t`Temperature`,
|
||||
unit: "°C",
|
||||
|
||||
@@ -71,3 +71,26 @@ export enum ConnectionType {
|
||||
}
|
||||
|
||||
export const connectionTypeLabels = ["", "SSH", "WebSocket"] as const
|
||||
|
||||
/** Systemd service state */
|
||||
export enum ServiceStatus {
|
||||
Active,
|
||||
Inactive,
|
||||
Failed,
|
||||
Activating,
|
||||
Deactivating,
|
||||
Reloading,
|
||||
}
|
||||
|
||||
export const ServiceStatusLabels = ["Active", "Inactive", "Failed", "Activating", "Deactivating", "Reloading"] as const
|
||||
|
||||
/** Systemd service sub state */
|
||||
export enum ServiceSubState {
|
||||
Dead,
|
||||
Running,
|
||||
Exited,
|
||||
Failed,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
export const ServiceSubStateLabels = ["Dead", "Running", "Exited", "Failed", "Unknown"] as const
|
||||
|
||||
@@ -7,13 +7,15 @@ import { messages as enMessages } from "@/locales/en/en"
|
||||
import { BatteryState } from "./enums"
|
||||
import { $direction } from "./stores"
|
||||
|
||||
const rtlLanguages = new Set(["ar", "fa", "he"])
|
||||
|
||||
// activates locale
|
||||
function activateLocale(locale: string, messages: Messages = enMessages) {
|
||||
i18n.load(locale, messages)
|
||||
i18n.activate(locale)
|
||||
document.documentElement.lang = locale
|
||||
localStorage.setItem("lang", locale)
|
||||
$direction.set(locale.startsWith("ar") || locale.startsWith("fa") ? "rtl" : "ltr")
|
||||
$direction.set(rtlLanguages.has(locale) ? "rtl" : "ltr")
|
||||
}
|
||||
|
||||
// dynamically loads translations for the given locale
|
||||
|
||||
@@ -44,6 +44,11 @@ export default [
|
||||
label: "Français",
|
||||
e: "🇫🇷",
|
||||
},
|
||||
{
|
||||
lang: "he",
|
||||
label: "עברית",
|
||||
e: "🕎",
|
||||
},
|
||||
{
|
||||
lang: "hr",
|
||||
label: "Hrvatski",
|
||||
|
||||
@@ -287,7 +287,7 @@ export function formatBytes(
|
||||
}
|
||||
}
|
||||
|
||||
export const chartMargin = { top: 12 }
|
||||
export const chartMargin = { top: 12, right: 5 }
|
||||
|
||||
/**
|
||||
* Retuns value of system host, truncating full path if socket.
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: ar\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-10-20 21:37\n"
|
||||
"PO-Revision-Date: 2025-11-14 22:51\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"
|
||||
@@ -76,13 +76,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 "نشط"
|
||||
|
||||
@@ -90,14 +93,20 @@ msgstr "نشط"
|
||||
msgid "Active Alerts"
|
||||
msgstr "التنبيهات النشطة"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Active state"
|
||||
msgstr "الحالة النشطة"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: 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 <0>System</0>"
|
||||
msgstr "إضافة <0>نظام</0>"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add New System"
|
||||
msgstr "إضافة نظام جديد"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add system"
|
||||
msgstr "إضافة نظام"
|
||||
@@ -110,11 +119,19 @@ msgstr "إضافة رابط"
|
||||
msgid "Adjust display options for charts."
|
||||
msgstr "تعديل خيارات العرض للرسوم البيانية."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Adjust the width of the main layout"
|
||||
msgstr "تعديل عرض التخطيط الرئيسي"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Admin"
|
||||
msgstr "مسؤول"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "After"
|
||||
msgstr "بعد"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "وكيل"
|
||||
@@ -139,6 +156,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"
|
||||
@@ -200,6 +218,18 @@ msgstr "عرض النطاق الترددي"
|
||||
msgid "Battery"
|
||||
msgstr "البطارية"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Became active"
|
||||
msgstr "أصبح نشطًا"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Became inactive"
|
||||
msgstr "أصبح غير نشط"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Before"
|
||||
msgstr "قبل"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Beszel supports OpenID Connect and many OAuth2 authentication providers."
|
||||
msgstr "يدعم بيزيل بروتوكول OpenID Connect والعديد من مزوّدي المصادقة عبر بروتوكول OAuth2."
|
||||
@@ -217,6 +247,10 @@ msgstr "ثنائي"
|
||||
msgid "Bits (Kbps, Mbps, Gbps)"
|
||||
msgstr "بت (كيلوبت/ثانية، ميجابت/ثانية، جيجابت/ثانية)"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Boot state"
|
||||
msgstr "حالة التمهيد"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bytes (KB/s, MB/s, GB/s)"
|
||||
@@ -226,11 +260,28 @@ msgstr "بايت (كيلوبايت/ثانية، ميجابايت/ثانية، ج
|
||||
msgid "Cache / Buffers"
|
||||
msgstr "ذاكرة التخزين المؤقت / المخازن المؤقتة"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Can reload"
|
||||
msgstr "يمكن إعادة التحميل"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Can start"
|
||||
msgstr "يمكن البدء"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
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 "إلغاء"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Capabilities"
|
||||
msgstr "القدرات"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Capacity"
|
||||
msgstr "السعة"
|
||||
@@ -276,6 +327,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 "انقر على حاوية لعرض مزيد من المعلومات."
|
||||
@@ -306,6 +363,10 @@ msgstr "هيئ التنبيهات الواردة"
|
||||
msgid "Confirm password"
|
||||
msgstr "تأكيد كلمة المرور"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Conflicts"
|
||||
msgstr "التعارضات"
|
||||
|
||||
#: src/components/active-alerts.tsx
|
||||
msgid "Connection is down"
|
||||
msgstr "الاتصال مقطوع"
|
||||
@@ -366,16 +427,38 @@ msgid "Copy YAML"
|
||||
msgstr "نسخ YAML"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "CPU"
|
||||
msgstr "المعالج"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "CPU Cores"
|
||||
msgstr "نوى المعالج"
|
||||
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "CPU Peak"
|
||||
msgstr "ذروة المعالج"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "CPU time"
|
||||
msgstr "وقت المعالج"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "CPU Time Breakdown"
|
||||
msgstr "تفصيل وقت المعالج"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "CPU Usage"
|
||||
msgstr "استخدام وحدة المعالجة المركزية"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Create"
|
||||
msgstr "إنشاء"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Create account"
|
||||
msgstr "إنشاء حساب"
|
||||
@@ -407,15 +490,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 "حذف"
|
||||
@@ -424,6 +510,10 @@ msgstr "حذف"
|
||||
msgid "Delete fingerprint"
|
||||
msgstr "حذف البصمة"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Description"
|
||||
msgstr "الوصف"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
msgid "Detail"
|
||||
msgstr "التفاصيل"
|
||||
@@ -472,6 +562,7 @@ msgid "Docker Network I/O"
|
||||
msgstr "إدخال/إخراج الشبكة للدوكر"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Documentation"
|
||||
msgstr "التوثيق"
|
||||
|
||||
@@ -495,11 +586,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
|
||||
@@ -515,6 +611,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 "أدخل عنوان البريد الإشباكي لإعادة تعيين كلمة المرور"
|
||||
@@ -531,7 +632,10 @@ msgstr "أدخل كلمة المرور لمرة واحدة الخاصة بك."
|
||||
#: 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"
|
||||
msgstr "خطأ"
|
||||
|
||||
@@ -542,10 +646,18 @@ msgstr "خطأ"
|
||||
msgid "Exceeds {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
|
||||
msgstr "يتجاوز {0}{1} في آخر {2, plural, one {# دقيقة} other {# دقائق}}"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Exec main PID"
|
||||
msgstr "معرف العملية الرئيسي للتنفيذ"
|
||||
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
|
||||
msgstr "سيتم حذف الأنظمة الحالية غير المعرفة في <0>config.yml</0>. يرجى عمل نسخ احتياطية بانتظام."
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Exited active"
|
||||
msgstr "خرج نشطًا"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr "تصدير"
|
||||
@@ -562,6 +674,10 @@ msgstr "تصدير تكوين الأنظمة الحالية الخاصة بك."
|
||||
msgid "Fahrenheit (°F)"
|
||||
msgstr "فهرنهايت (°ف)"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Failed"
|
||||
msgstr "فشل"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Failed Attributes:"
|
||||
msgstr "السمات الفاشلة:"
|
||||
@@ -572,6 +688,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 "فشل في حفظ الإعدادات"
|
||||
|
||||
@@ -583,10 +700,16 @@ msgstr "فشل في إرسال إشعار الاختبار"
|
||||
msgid "Failed to update alert"
|
||||
msgstr "فشل في تحديث التنبيه"
|
||||
|
||||
#. placeholder {0}: statusTotals[ServiceStatus.Failed]
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Failed: {0}"
|
||||
msgstr "فشل: {0}"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Filter..."
|
||||
msgstr "تصفية..."
|
||||
@@ -624,6 +747,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"
|
||||
@@ -632,6 +759,10 @@ msgstr "محركات GPU"
|
||||
msgid "GPU Power Draw"
|
||||
msgstr "استهلاك طاقة وحدة معالجة الرسوميات"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "GPU Usage"
|
||||
msgstr "استخدام وحدة معالجة الرسوميات"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Grid"
|
||||
msgstr "شبكة"
|
||||
@@ -664,6 +795,10 @@ 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 "عنوان البريد الإشباكي غير صالح."
|
||||
@@ -681,6 +816,19 @@ msgstr "اللغة"
|
||||
msgid "Layout"
|
||||
msgstr "التخطيط"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Layout width"
|
||||
msgstr "عرض التخطيط"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Lifecycle"
|
||||
msgstr "دورة الحياة"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "limit"
|
||||
msgstr "الحد"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Load Average"
|
||||
msgstr "متوسط التحميل"
|
||||
@@ -702,6 +850,14 @@ msgstr "متوسط التحميل 5 دقائق"
|
||||
msgid "Load Avg"
|
||||
msgstr "متوسط التحميل"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Load state"
|
||||
msgstr "حالة التحميل"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Loading..."
|
||||
msgstr "جاري التحميل..."
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Log Out"
|
||||
msgstr "تسجيل الخروج"
|
||||
@@ -725,6 +881,10 @@ msgstr "السجلات"
|
||||
msgid "Looking instead for where to create alerts? Click the bell <0/> icons in the systems table."
|
||||
msgstr "هل تبحث عن مكان لإنشاء التنبيهات؟ انقر على أيقونات الجرس <0/> في جدول الأنظمة."
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Main PID"
|
||||
msgstr "معرف العملية الرئيسي"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Manage display and notification preferences."
|
||||
msgstr "إدارة تفضيلات العرض والإشعارات."
|
||||
@@ -740,10 +900,21 @@ msgid "Max 1 min"
|
||||
msgstr "الحد الأقصى دقيقة"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Memory"
|
||||
msgstr "الذاكرة"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Memory limit"
|
||||
msgstr "حد الذاكرة"
|
||||
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Memory Peak"
|
||||
msgstr "ذروة الذاكرة"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Memory Usage"
|
||||
@@ -760,6 +931,8 @@ msgstr "الموديل"
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Name"
|
||||
msgstr "الاسم"
|
||||
|
||||
@@ -784,7 +957,14 @@ msgstr "حركة مرور الشبكة للواجهات العامة"
|
||||
msgid "Network unit"
|
||||
msgstr "وحدة الشبكة"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "No"
|
||||
msgstr "لا"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "No results found."
|
||||
msgstr "لم يتم العثور على نتائج."
|
||||
|
||||
@@ -793,6 +973,7 @@ msgstr "لم يتم العثور على نتائج."
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "No results."
|
||||
msgstr "لا توجد نتائج."
|
||||
|
||||
@@ -819,12 +1000,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 "فتح القائمة"
|
||||
@@ -833,10 +1021,15 @@ msgstr "فتح القائمة"
|
||||
msgid "Or continue with"
|
||||
msgstr "أو المتابعة باستخدام"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Other"
|
||||
msgstr "أخرى"
|
||||
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "Overwrite existing alerts"
|
||||
msgstr "الكتابة فوق التنبيهات الحالية"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Page"
|
||||
@@ -869,6 +1062,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 "إيقاف مؤقت"
|
||||
@@ -881,6 +1078,15 @@ msgstr "متوقف مؤقتا"
|
||||
msgid "Paused ({pausedSystemsLength})"
|
||||
msgstr "متوقف مؤقتا ({pausedSystemsLength})"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Per-core average utilization"
|
||||
msgstr "متوسط الاستخدام لكل نواة"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Percentage of time spent in each state"
|
||||
msgstr "النسبة المئوية للوقت المقضي في كل حالة"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
msgstr "يرجى <0>تكوين خادم SMTP</0> لضمان تسليم التنبيهات."
|
||||
@@ -932,11 +1138,19 @@ msgstr "الاستخدام الدقيق في الوقت المسجل"
|
||||
msgid "Preferred Language"
|
||||
msgstr "اللغة المفضلة"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Process started"
|
||||
msgstr "تم بدء العملية"
|
||||
|
||||
#. Use 'Key' if your language requires many more characters
|
||||
#: src/components/add-system.tsx
|
||||
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
|
||||
@@ -949,9 +1163,14 @@ 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 "تحديث"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Relationships"
|
||||
msgstr "العلاقات"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Request a one-time password"
|
||||
msgstr "طلب كلمة مرور لمرة واحدة"
|
||||
@@ -960,6 +1179,14 @@ msgstr "طلب كلمة مرور لمرة واحدة"
|
||||
msgid "Request OTP"
|
||||
msgstr "طلب OTP"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Required by"
|
||||
msgstr "مطلوب من قبل"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Requires"
|
||||
msgstr "يتطلب"
|
||||
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
msgid "Reset Password"
|
||||
msgstr "إعادة تعيين كلمة المرور"
|
||||
@@ -970,10 +1197,19 @@ msgstr "إعادة تعيين كلمة المرور"
|
||||
msgid "Resolved"
|
||||
msgstr "تم حلها"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Restarts"
|
||||
msgstr "إعادة التشغيل"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Resume"
|
||||
msgstr "استئناف"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgctxt "Root disk label"
|
||||
msgid "Root"
|
||||
msgstr "الجذر"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Rotate token"
|
||||
msgstr "تدوير الرمز المميز"
|
||||
@@ -982,6 +1218,10 @@ msgstr "تدوير الرمز المميز"
|
||||
msgid "Rows per page"
|
||||
msgstr "صفوف لكل صفحة"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Runtime Metrics"
|
||||
msgstr "مقاييس وقت التشغيل"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "S.M.A.R.T. Details"
|
||||
msgstr "تفاصيل S.M.A.R.T."
|
||||
@@ -1003,6 +1243,18 @@ msgstr "حفظ الإعدادات"
|
||||
msgid "Save system"
|
||||
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 "بحث"
|
||||
@@ -1015,6 +1267,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 "تم الإرسال"
|
||||
@@ -1023,6 +1279,14 @@ msgstr "تم الإرسال"
|
||||
msgid "Serial Number"
|
||||
msgstr "الرقم التسلسلي"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Service Details"
|
||||
msgstr "تفاصيل الخدمة"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Services"
|
||||
msgstr "الخدمات"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "تعيين عتبات النسبة المئوية لألوان العداد."
|
||||
@@ -1050,18 +1314,30 @@ 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 "الحالة"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Status"
|
||||
msgstr "الحالة"
|
||||
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Sub State"
|
||||
msgstr "الحالة الفرعية"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Swap space used by the system"
|
||||
msgstr "مساحة التبديل المستخدمة من قبل النظام"
|
||||
@@ -1070,9 +1346,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"
|
||||
@@ -1082,6 +1364,10 @@ msgstr "النظام"
|
||||
msgid "System load averages over time"
|
||||
msgstr "متوسط تحميل النظام مع مرور الوقت"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Systemd Services"
|
||||
msgstr "خدمات systemd"
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Systems"
|
||||
msgstr "الأنظمة"
|
||||
@@ -1094,6 +1380,10 @@ msgstr "يمكن إدارة الأنظمة في ملف <0>config.yml</0> داخ
|
||||
msgid "Table"
|
||||
msgstr "جدول"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Tasks"
|
||||
msgstr "المهام"
|
||||
|
||||
#. Temperature label in systems table
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
@@ -1177,6 +1467,11 @@ msgstr "تسمح الرموز المميزة للوكلاء بالاتصال و
|
||||
msgid "Tokens and fingerprints are used to authenticate WebSocket connections to the hub."
|
||||
msgstr "تُستخدم الرموز المميزة والبصمات للمصادقة على اتصالات WebSocket إلى المحور."
|
||||
|
||||
#: src/components/ui/chart.tsx
|
||||
#: src/components/ui/chart.tsx
|
||||
msgid "Total"
|
||||
msgstr "الإجمالي"
|
||||
|
||||
#: src/components/routes/system/network-sheet.tsx
|
||||
msgid "Total data received for each interface"
|
||||
msgstr "إجمالي البيانات المستلمة لكل واجهة"
|
||||
@@ -1185,6 +1480,19 @@ msgstr "إجمالي البيانات المستلمة لكل واجهة"
|
||||
msgid "Total data sent for each interface"
|
||||
msgstr "إجمالي البيانات المرسلة لكل واجهة"
|
||||
|
||||
#. placeholder {0}: data.length
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Total: {0}"
|
||||
msgstr "الإجمالي: {0}"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Triggered by"
|
||||
msgstr "تم التفعيل بواسطة"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Triggers"
|
||||
msgstr "المحفزات"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when 1 minute load average exceeds a threshold"
|
||||
msgstr "يتم التفعيل عندما يتجاوز متوسط التحميل لمدة دقيقة واحدة عتبة معينة"
|
||||
@@ -1209,6 +1517,10 @@ msgstr "يتم التفعيل عندما يتجاوز الجمع بين الصع
|
||||
msgid "Triggers when CPU usage exceeds a threshold"
|
||||
msgstr "يتم التفعيل عندما يتجاوز استخدام وحدة المعالجة المركزية عتبة معينة"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when GPU usage exceeds a threshold"
|
||||
msgstr "يتم التفعيل عندما يتجاوز استخدام وحدة معالجة الرسوميات عتبة معينة"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when memory usage exceeds a threshold"
|
||||
msgstr "يتم التفعيل عندما يتجاوز استخدام الذاكرة عتبة معينة"
|
||||
@@ -1221,10 +1533,16 @@ 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 "ملف الوحدة"
|
||||
|
||||
#. Temperature / network units
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Unit preferences"
|
||||
@@ -1240,6 +1558,11 @@ msgstr "رمز مميز عالمي"
|
||||
msgid "Unknown"
|
||||
msgstr "غير معروفة"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Unlimited"
|
||||
msgstr "غير محدود"
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
@@ -1250,10 +1573,20 @@ 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 "تم التحديث"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Updated every 10 minutes."
|
||||
msgstr "يتم التحديث كل 10 دقائق."
|
||||
|
||||
#: src/components/routes/system/network-sheet.tsx
|
||||
msgid "Upload"
|
||||
msgstr "رفع"
|
||||
@@ -1266,6 +1599,7 @@ msgstr "مدة التشغيل"
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Usage"
|
||||
msgstr "الاستخدام"
|
||||
|
||||
@@ -1291,6 +1625,7 @@ msgstr "القيمة"
|
||||
msgid "View"
|
||||
msgstr "عرض"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/components/routes/system/network-sheet.tsx
|
||||
msgid "View more"
|
||||
msgstr "عرض المزيد"
|
||||
@@ -1311,6 +1646,10 @@ msgstr "في انتظار وجود سجلات كافية للعرض"
|
||||
msgid "Want to help improve our translations? Check <0>Crowdin</0> for details."
|
||||
msgstr "هل تريد مساعدتنا في تحسين ترجماتنا؟ تحقق من <0>Crowdin</0> لمزيد من التفاصيل."
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Wants"
|
||||
msgstr "يريد"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Warning (%)"
|
||||
msgstr "تحذير (%)"
|
||||
@@ -1347,6 +1686,12 @@ msgstr "تكوين YAML"
|
||||
msgid "YAML Configuration"
|
||||
msgstr "تكوين YAML"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Yes"
|
||||
msgstr "نعم"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Your user settings have been updated."
|
||||
msgstr "تم تحديث إعدادات المستخدم الخاصة بك."
|
||||
|
||||
@@ -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-11-14 22:51\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Bulgarian\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -76,13 +76,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 "Активен"
|
||||
|
||||
@@ -90,14 +93,20 @@ msgstr "Активен"
|
||||
msgid "Active Alerts"
|
||||
msgstr "Активни тревоги"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Active state"
|
||||
msgstr "Активно състояние"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: 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 <0>System</0>"
|
||||
msgstr "Добави <0>Система</0>"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add New System"
|
||||
msgstr "Добави нова система"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add system"
|
||||
msgstr "Добави система"
|
||||
@@ -110,11 +119,19 @@ msgstr "Добави URL"
|
||||
msgid "Adjust display options for charts."
|
||||
msgstr "Настрой опциите за показване на диаграмите."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Adjust the width of the main layout"
|
||||
msgstr "Настройка ширината на основния макет"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Admin"
|
||||
msgstr "Администратор"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "After"
|
||||
msgstr "След"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Агент"
|
||||
@@ -139,6 +156,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"
|
||||
@@ -200,6 +218,18 @@ msgstr "Bandwidth на мрежата"
|
||||
msgid "Battery"
|
||||
msgstr "Батерия"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Became active"
|
||||
msgstr "Стана активен"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Became inactive"
|
||||
msgstr "Стана неактивен"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Before"
|
||||
msgstr "Преди"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Beszel supports OpenID Connect and many OAuth2 authentication providers."
|
||||
msgstr "Beszel поддържа OpenID Connect и много други OAuth2 доставчици за удостоверяване."
|
||||
@@ -217,6 +247,10 @@ msgstr "Двоичен код"
|
||||
msgid "Bits (Kbps, Mbps, Gbps)"
|
||||
msgstr "Бита (Kbps, Mbps, Gbps)"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Boot state"
|
||||
msgstr "Състояние при зареждане"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bytes (KB/s, MB/s, GB/s)"
|
||||
@@ -226,11 +260,28 @@ msgstr "Байта (KB/s, MB/s, GB/s)"
|
||||
msgid "Cache / Buffers"
|
||||
msgstr "Кеш / Буфери"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Can reload"
|
||||
msgstr "Може да се презареди"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Can start"
|
||||
msgstr "Може да се стартира"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
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 "Откажи"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Capabilities"
|
||||
msgstr "Възможности"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Capacity"
|
||||
msgstr "Капацитет"
|
||||
@@ -276,6 +327,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 "Кликнете върху контейнер, за да видите повече информация."
|
||||
@@ -306,6 +363,10 @@ msgstr "Настрой как получаваш нотификации за т
|
||||
msgid "Confirm password"
|
||||
msgstr "Потвърди парола"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Conflicts"
|
||||
msgstr "Конфликти"
|
||||
|
||||
#: src/components/active-alerts.tsx
|
||||
msgid "Connection is down"
|
||||
msgstr "Връзката е прекъсната"
|
||||
@@ -366,16 +427,38 @@ msgid "Copy YAML"
|
||||
msgstr "Копирай YAML"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "CPU"
|
||||
msgstr "Процесор"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "CPU Cores"
|
||||
msgstr "CPU ядра"
|
||||
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "CPU Peak"
|
||||
msgstr "Пик на CPU"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "CPU time"
|
||||
msgstr "Време на CPU"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "CPU Time Breakdown"
|
||||
msgstr "Разбивка на времето на CPU"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "CPU Usage"
|
||||
msgstr "Употреба на процесор"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Create"
|
||||
msgstr "Създай"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Create account"
|
||||
msgstr "Създай акаунт"
|
||||
@@ -407,15 +490,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 "Изтрий"
|
||||
@@ -424,6 +510,10 @@ msgstr "Изтрий"
|
||||
msgid "Delete fingerprint"
|
||||
msgstr "Изтрий пръстов отпечатък"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Description"
|
||||
msgstr "Описание"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
msgid "Detail"
|
||||
msgstr "Подробности"
|
||||
@@ -472,6 +562,7 @@ msgid "Docker Network I/O"
|
||||
msgstr "Мрежов I/O използван от docker"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Documentation"
|
||||
msgstr "Документация"
|
||||
|
||||
@@ -495,11 +586,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
|
||||
@@ -515,6 +611,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 "Въведи имейл адрес за да нулираш паролата"
|
||||
@@ -531,7 +632,10 @@ msgstr "Въведете Вашата еднократна парола."
|
||||
#: 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"
|
||||
msgstr "Грешка"
|
||||
|
||||
@@ -542,10 +646,18 @@ msgstr "Грешка"
|
||||
msgid "Exceeds {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
|
||||
msgstr "Надвишава {0}{1} в последните {2, plural, one {# минута} other {# минути}}"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Exec main PID"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
|
||||
msgstr "Съществуващи системи които не са дефинирани в <0>config.yml</0> ще бъдат изтрити. Моля прави чести архиви."
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Exited active"
|
||||
msgstr "Излезе активно"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr "Експортиране"
|
||||
@@ -562,6 +674,10 @@ msgstr "Експортирай конфигурацията на системи
|
||||
msgid "Fahrenheit (°F)"
|
||||
msgstr "Фаренхайт (°F)"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Failed"
|
||||
msgstr "Неуспешно"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Failed Attributes:"
|
||||
msgstr "Неуспешни атрибути:"
|
||||
@@ -572,6 +688,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 "Неуспешно запазване на настройки"
|
||||
|
||||
@@ -583,10 +700,16 @@ msgstr "Неуспешно изпрати тестова нотификация"
|
||||
msgid "Failed to update alert"
|
||||
msgstr "Неуспешно обнови тревога"
|
||||
|
||||
#. placeholder {0}: statusTotals[ServiceStatus.Failed]
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Failed: {0}"
|
||||
msgstr "Неуспешни: {0}"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Filter..."
|
||||
msgstr "Филтрирай..."
|
||||
@@ -624,6 +747,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 двигатели"
|
||||
@@ -632,6 +759,10 @@ msgstr "GPU двигатели"
|
||||
msgid "GPU Power Draw"
|
||||
msgstr "Консумация на ток от графична карта"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "GPU Usage"
|
||||
msgstr "Употреба на GPU"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Grid"
|
||||
msgstr "Мрежово"
|
||||
@@ -664,6 +795,10 @@ 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 "Невалиден имейл адрес."
|
||||
@@ -681,6 +816,19 @@ msgstr "Език"
|
||||
msgid "Layout"
|
||||
msgstr "Подреждане"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Layout width"
|
||||
msgstr "Ширина на макета"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Lifecycle"
|
||||
msgstr "Жизнен цикъл"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "limit"
|
||||
msgstr "лимит"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Load Average"
|
||||
msgstr "Средно натоварване"
|
||||
@@ -702,6 +850,14 @@ msgstr "Средно натоварване 5 минути"
|
||||
msgid "Load Avg"
|
||||
msgstr "Средно натоварване"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Load state"
|
||||
msgstr "Състояние на зареждане"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Loading..."
|
||||
msgstr "Зареждане..."
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Log Out"
|
||||
msgstr "Изход"
|
||||
@@ -725,6 +881,10 @@ msgstr "Логове"
|
||||
msgid "Looking instead for where to create alerts? Click the bell <0/> icons in the systems table."
|
||||
msgstr "Търсиш къде да създадеш тревоги? Натисни емотиконата за звънец <0/> в таблицата за системи."
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Main PID"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Manage display and notification preferences."
|
||||
msgstr "Управление на предпочитанията за показване и уведомяване."
|
||||
@@ -740,10 +900,21 @@ msgid "Max 1 min"
|
||||
msgstr "Максимум 1 минута"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Memory"
|
||||
msgstr "Памет"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Memory limit"
|
||||
msgstr "Лимит на памет"
|
||||
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Memory Peak"
|
||||
msgstr "Пик на памет"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Memory Usage"
|
||||
@@ -760,6 +931,8 @@ msgstr "Модел"
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Name"
|
||||
msgstr "Име"
|
||||
|
||||
@@ -784,7 +957,14 @@ msgstr "Мрежов трафик на публични интерфейси"
|
||||
msgid "Network unit"
|
||||
msgstr "Единица за измерване на скорост"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "No"
|
||||
msgstr "Не"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "No results found."
|
||||
msgstr "Няма намерени резултати."
|
||||
|
||||
@@ -793,6 +973,7 @@ msgstr "Няма намерени резултати."
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "No results."
|
||||
msgstr "Няма резултати."
|
||||
|
||||
@@ -819,12 +1000,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 "Отвори менюто"
|
||||
@@ -833,10 +1021,15 @@ msgstr "Отвори менюто"
|
||||
msgid "Or continue with"
|
||||
msgstr "Или продължи с"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Other"
|
||||
msgstr "Други"
|
||||
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "Overwrite existing alerts"
|
||||
msgstr "Презапиши съществуващи тревоги"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Page"
|
||||
@@ -869,6 +1062,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 "Пауза"
|
||||
@@ -881,6 +1078,15 @@ msgstr "На пауза"
|
||||
msgid "Paused ({pausedSystemsLength})"
|
||||
msgstr "На пауза ({pausedSystemsLength})"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Per-core average utilization"
|
||||
msgstr "Средно използване на ядро"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Percentage of time spent in each state"
|
||||
msgstr "Процент време, прекарано във всяко състояние"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
msgstr "Моля <0>конфигурурай SMTP сървър</0> за да се подсигуриш, че тревогите са доставени."
|
||||
@@ -932,11 +1138,19 @@ msgstr "Точно използване в записаното време"
|
||||
msgid "Preferred Language"
|
||||
msgstr "Предпочитан език"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Process started"
|
||||
msgstr "Процесът стартира"
|
||||
|
||||
#. Use 'Key' if your language requires many more characters
|
||||
#: src/components/add-system.tsx
|
||||
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
|
||||
@@ -949,9 +1163,14 @@ 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 "Опресни"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Relationships"
|
||||
msgstr "Връзки"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Request a one-time password"
|
||||
msgstr "Заявка за еднократна парола"
|
||||
@@ -960,6 +1179,14 @@ msgstr "Заявка за еднократна парола"
|
||||
msgid "Request OTP"
|
||||
msgstr "Заявка OTP"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Required by"
|
||||
msgstr "Изисква се от"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Requires"
|
||||
msgstr "Изисква"
|
||||
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
msgid "Reset Password"
|
||||
msgstr "Нулиране на парола"
|
||||
@@ -970,10 +1197,19 @@ msgstr "Нулиране на парола"
|
||||
msgid "Resolved"
|
||||
msgstr "Решен"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Restarts"
|
||||
msgstr "Рестартирания"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Възобнови"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgctxt "Root disk label"
|
||||
msgid "Root"
|
||||
msgstr "Корен"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Rotate token"
|
||||
msgstr "Пресъздаване на идентификатора"
|
||||
@@ -982,6 +1218,10 @@ msgstr "Пресъздаване на идентификатора"
|
||||
msgid "Rows per page"
|
||||
msgstr "Редове на страница"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Runtime Metrics"
|
||||
msgstr "Метрики на изпълнение"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "S.M.A.R.T. Details"
|
||||
msgstr "S.M.A.R.T. Детайли"
|
||||
@@ -1003,6 +1243,18 @@ msgstr "Запази настройките"
|
||||
msgid "Save system"
|
||||
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 "Търси"
|
||||
@@ -1015,6 +1267,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 "Изпратени"
|
||||
@@ -1023,6 +1279,14 @@ msgstr "Изпратени"
|
||||
msgid "Serial Number"
|
||||
msgstr "Сериен номер"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Service Details"
|
||||
msgstr "Детайли на услугата"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Services"
|
||||
msgstr "Услуги"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "Задайте процентни прагове за цветовете на измервателните уреди."
|
||||
@@ -1050,18 +1314,30 @@ 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 "Състояние"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Status"
|
||||
msgstr "Статус"
|
||||
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Sub State"
|
||||
msgstr "Подсъстояние"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Swap space used by the system"
|
||||
msgstr "Изполван swap от системата"
|
||||
@@ -1070,9 +1346,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"
|
||||
@@ -1082,6 +1364,10 @@ msgstr "Система"
|
||||
msgid "System load averages over time"
|
||||
msgstr "Средно натоварване на системата във времето"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Systemd Services"
|
||||
msgstr "Услуги на systemd"
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Systems"
|
||||
msgstr "Системи"
|
||||
@@ -1094,6 +1380,10 @@ msgstr "Системите могат да бъдат управлявани в
|
||||
msgid "Table"
|
||||
msgstr "Таблица"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Tasks"
|
||||
msgstr "Задачи"
|
||||
|
||||
#. Temperature label in systems table
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
@@ -1177,6 +1467,11 @@ msgstr "Токените позволяват на агентите да се с
|
||||
msgid "Tokens and fingerprints are used to authenticate WebSocket connections to the hub."
|
||||
msgstr "Токените и пръстовите отпечатъци се използват за удостоверяване на WebSocket връзките към концентратора."
|
||||
|
||||
#: src/components/ui/chart.tsx
|
||||
#: src/components/ui/chart.tsx
|
||||
msgid "Total"
|
||||
msgstr "Общо"
|
||||
|
||||
#: src/components/routes/system/network-sheet.tsx
|
||||
msgid "Total data received for each interface"
|
||||
msgstr "Общо получени данни за всеки интерфейс"
|
||||
@@ -1185,6 +1480,19 @@ msgstr "Общо получени данни за всеки интерфейс"
|
||||
msgid "Total data sent for each interface"
|
||||
msgstr "Общо изпратени данни за всеки интерфейс"
|
||||
|
||||
#. placeholder {0}: data.length
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Total: {0}"
|
||||
msgstr "Общо: {0}"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Triggered by"
|
||||
msgstr "Активиран от"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Triggers"
|
||||
msgstr "Активатори"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when 1 minute load average exceeds a threshold"
|
||||
msgstr "Задейства се, когато употребата на паметта за 1 минута надвиши зададен праг"
|
||||
@@ -1209,6 +1517,10 @@ msgstr "Задейства се, когато комбинираното кач
|
||||
msgid "Triggers when CPU usage exceeds a threshold"
|
||||
msgstr "Задейства се, когато употребата на процесора надвиши зададен праг"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when GPU usage exceeds a threshold"
|
||||
msgstr "Задейства се, когато използването на GPU надвиши праг"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when memory usage exceeds a threshold"
|
||||
msgstr "Задейства се, когато употребата на паметта надвиши зададен праг"
|
||||
@@ -1221,10 +1533,16 @@ 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 "Файл на единица"
|
||||
|
||||
#. Temperature / network units
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Unit preferences"
|
||||
@@ -1240,6 +1558,11 @@ msgstr "Универсален тоукън"
|
||||
msgid "Unknown"
|
||||
msgstr "Неизвестна"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Unlimited"
|
||||
msgstr "Неограничено"
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
@@ -1250,10 +1573,20 @@ 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 "Актуализирано"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Updated every 10 minutes."
|
||||
msgstr "Актуализира се на всеки 10 минути."
|
||||
|
||||
#: src/components/routes/system/network-sheet.tsx
|
||||
msgid "Upload"
|
||||
msgstr "Качване"
|
||||
@@ -1266,6 +1599,7 @@ msgstr "Време на работа"
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Usage"
|
||||
msgstr "Употреба"
|
||||
|
||||
@@ -1291,6 +1625,7 @@ msgstr "Стойност"
|
||||
msgid "View"
|
||||
msgstr "Изглед"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/components/routes/system/network-sheet.tsx
|
||||
msgid "View more"
|
||||
msgstr "Виж повече"
|
||||
@@ -1311,6 +1646,10 @@ msgstr "Изчаква се за достатъчно записи за пока
|
||||
msgid "Want to help improve our translations? Check <0>Crowdin</0> for details."
|
||||
msgstr "Искаш да помогнеш да направиш преводите още по-добри? Провери нашия <0>Crowdin</0> за повече детайли."
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Wants"
|
||||
msgstr "Иска"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Warning (%)"
|
||||
msgstr "Предупреждение (%)"
|
||||
@@ -1347,6 +1686,12 @@ msgstr "YAML конфигурация"
|
||||
msgid "YAML Configuration"
|
||||
msgstr "YAML конфигурация"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Yes"
|
||||
msgstr "Да"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Your user settings have been updated."
|
||||
msgstr "Настройките за потребителя ти са обновени."
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: cs\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-10-20 21:37\n"
|
||||
"PO-Revision-Date: 2025-11-14 22:51\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Czech\n"
|
||||
"Plural-Forms: nplurals=4; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 3;\n"
|
||||
@@ -76,13 +76,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 "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í"
|
||||
|
||||
@@ -90,14 +93,20 @@ msgstr "Aktivní"
|
||||
msgid "Active Alerts"
|
||||
msgstr "Aktivní výstrahy"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Active state"
|
||||
msgstr "Aktivní stav"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: 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 <0>System</0>"
|
||||
msgstr "Přidat <0>Systém</0>"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add New System"
|
||||
msgstr "Přidat nový systém"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add system"
|
||||
msgstr "Přidat systém"
|
||||
@@ -110,11 +119,19 @@ msgstr "Přidat URL"
|
||||
msgid "Adjust display options for charts."
|
||||
msgstr "Upravit možnosti zobrazení pro grafy."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Adjust the width of the main layout"
|
||||
msgstr "Upravit šířku hlavního rozvržení"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Admin"
|
||||
msgstr "Administrátor"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "After"
|
||||
msgstr "Po"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Agent"
|
||||
@@ -139,6 +156,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"
|
||||
@@ -200,6 +218,18 @@ msgstr "Přenos"
|
||||
msgid "Battery"
|
||||
msgstr "Baterie"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Became active"
|
||||
msgstr "Stal se aktivním"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Became inactive"
|
||||
msgstr "Stal se neaktivním"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Before"
|
||||
msgstr "Před"
|
||||
|
||||
#: 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í."
|
||||
@@ -217,6 +247,10 @@ msgstr "Binární"
|
||||
msgid "Bits (Kbps, Mbps, Gbps)"
|
||||
msgstr "Bity (Kbps, Mbps, Gbps)"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Boot state"
|
||||
msgstr "Stav zavádění"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bytes (KB/s, MB/s, GB/s)"
|
||||
@@ -226,11 +260,28 @@ msgstr "Byty (KB/s, MB/s, GB/s)"
|
||||
msgid "Cache / Buffers"
|
||||
msgstr "Cache / vyrovnávací paměť"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Can reload"
|
||||
msgstr "Může znovu načíst"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Can start"
|
||||
msgstr "Může spustit"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
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"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Capabilities"
|
||||
msgstr "Schopnosti"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Capacity"
|
||||
msgstr "Kapacita"
|
||||
@@ -276,6 +327,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í."
|
||||
@@ -306,6 +363,10 @@ msgstr "Konfigurace způsobu přijímání upozornění."
|
||||
msgid "Confirm password"
|
||||
msgstr "Potvrdit heslo"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Conflicts"
|
||||
msgstr "Konflikty"
|
||||
|
||||
#: src/components/active-alerts.tsx
|
||||
msgid "Connection is down"
|
||||
msgstr "Připojení je nedostupné"
|
||||
@@ -366,16 +427,38 @@ msgid "Copy YAML"
|
||||
msgstr "Kopírovat YAML"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "CPU"
|
||||
msgstr "Procesor"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "CPU Cores"
|
||||
msgstr "CPU jádra"
|
||||
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "CPU Peak"
|
||||
msgstr "Špička CPU"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "CPU time"
|
||||
msgstr "Čas CPU"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "CPU Time Breakdown"
|
||||
msgstr "Rozdělení času CPU"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/lib/alerts.ts
|
||||
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"
|
||||
@@ -407,15 +490,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"
|
||||
@@ -424,6 +510,10 @@ msgstr "Odstranit"
|
||||
msgid "Delete fingerprint"
|
||||
msgstr "Smazat identifikátor"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Description"
|
||||
msgstr "Popis"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
msgid "Detail"
|
||||
msgstr "Detail"
|
||||
@@ -439,11 +529,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"
|
||||
@@ -472,6 +562,7 @@ msgid "Docker Network I/O"
|
||||
msgstr "Síťové I/O Dockeru"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Documentation"
|
||||
msgstr "Dokumentace"
|
||||
|
||||
@@ -495,16 +586,21 @@ 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
|
||||
msgid "Email"
|
||||
msgstr "Email"
|
||||
msgstr "E-mail"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Email notifications"
|
||||
@@ -515,6 +611,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"
|
||||
@@ -531,7 +632,10 @@ msgstr "Zadejte Vaše jednorázové heslo."
|
||||
#: 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"
|
||||
msgstr "Chyba"
|
||||
|
||||
@@ -542,10 +646,18 @@ msgstr "Chyba"
|
||||
msgid "Exceeds {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
|
||||
msgstr "Překračuje {0}{1} za {2, plural, one {poslední # minutu} few {poslední # minuty} other {posledních # minut}}"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Exec main PID"
|
||||
msgstr "Hlavní PID spuštění"
|
||||
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
|
||||
msgstr "Stávající systémy, které nejsou definovány v <0>config.yml</0>, budou odstraněny. Provádějte pravidelné zálohování."
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Exited active"
|
||||
msgstr "Ukončeno aktivně"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr "Exportovat"
|
||||
@@ -562,6 +674,10 @@ msgstr "Exportovat aktuální konfiguraci systémů."
|
||||
msgid "Fahrenheit (°F)"
|
||||
msgstr "Fahrenheita (°F)"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Failed"
|
||||
msgstr "Selhalo"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Failed Attributes:"
|
||||
msgstr "Neúspěšné atributy:"
|
||||
@@ -572,6 +688,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í"
|
||||
|
||||
@@ -583,10 +700,16 @@ msgstr "Nepodařilo se odeslat testovací oznámení"
|
||||
msgid "Failed to update alert"
|
||||
msgstr "Nepodařilo se aktualizovat upozornění"
|
||||
|
||||
#. placeholder {0}: statusTotals[ServiceStatus.Failed]
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Failed: {0}"
|
||||
msgstr "Neúspěšné: {0}"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Filter..."
|
||||
msgstr "Filtr..."
|
||||
@@ -597,7 +720,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}}"
|
||||
@@ -624,6 +747,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"
|
||||
@@ -632,6 +759,10 @@ msgstr "GPU enginy"
|
||||
msgid "GPU Power Draw"
|
||||
msgstr "Spotřeba energie GPU"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "GPU Usage"
|
||||
msgstr "Využití GPU"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Grid"
|
||||
msgstr "Mřížka"
|
||||
@@ -664,6 +795,10 @@ 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."
|
||||
@@ -681,6 +816,19 @@ msgstr "Jazyk"
|
||||
msgid "Layout"
|
||||
msgstr "Rozvržení"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Layout width"
|
||||
msgstr "Šířka rozvržení"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Lifecycle"
|
||||
msgstr "Životní cyklus"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "limit"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Load Average"
|
||||
msgstr "Průměrné vytížení"
|
||||
@@ -702,6 +850,14 @@ msgstr "Průměrná zátěž 5m"
|
||||
msgid "Load Avg"
|
||||
msgstr "Prům. zatížení"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Load state"
|
||||
msgstr "Stav načtení"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Loading..."
|
||||
msgstr "Načítání..."
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Log Out"
|
||||
msgstr "Odhlásit"
|
||||
@@ -725,6 +881,10 @@ msgstr "Logy"
|
||||
msgid "Looking instead for where to create alerts? Click the bell <0/> icons in the systems table."
|
||||
msgstr "Hledáte místo kde vytvářet upozornění? Klikněte na ikonu zvonku <0/> v systémové tabulce."
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Main PID"
|
||||
msgstr "Hlavní PID"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Manage display and notification preferences."
|
||||
msgstr "Správa nastavení zobrazení a oznámení."
|
||||
@@ -740,10 +900,21 @@ msgid "Max 1 min"
|
||||
msgstr "Max. 1 min"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Memory"
|
||||
msgstr "Paměť"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Memory limit"
|
||||
msgstr "Limit paměti"
|
||||
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Memory Peak"
|
||||
msgstr "Špička paměti"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Memory Usage"
|
||||
@@ -755,11 +926,13 @@ 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
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Name"
|
||||
msgstr "Název"
|
||||
|
||||
@@ -784,7 +957,14 @@ msgstr "Síťový provoz veřejných rozhraní"
|
||||
msgid "Network unit"
|
||||
msgstr "Síťová jednotka"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "No"
|
||||
msgstr "Ne"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "No results found."
|
||||
msgstr "Nenalezeny žádné výskyty."
|
||||
|
||||
@@ -793,6 +973,7 @@ msgstr "Nenalezeny žádné výskyty."
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "No results."
|
||||
msgstr "Žádné výsledky."
|
||||
|
||||
@@ -819,12 +1000,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"
|
||||
@@ -833,10 +1021,15 @@ msgstr "Otevřít menu"
|
||||
msgid "Or continue with"
|
||||
msgstr "Nebo pokračujte s"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Other"
|
||||
msgstr "Jiné"
|
||||
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
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"
|
||||
@@ -869,6 +1062,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"
|
||||
@@ -881,6 +1078,15 @@ msgstr "Pozastaveno"
|
||||
msgid "Paused ({pausedSystemsLength})"
|
||||
msgstr "Pozastaveno ({pausedSystemsLength})"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Per-core average utilization"
|
||||
msgstr "Průměrné využití na jádro"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Percentage of time spent in each state"
|
||||
msgstr "Procento času strávěného v každém stavu"
|
||||
|
||||
#: 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."
|
||||
@@ -916,7 +1122,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
|
||||
@@ -932,11 +1138,19 @@ msgstr "Přesné využití v zaznamenaném čase"
|
||||
msgid "Preferred Language"
|
||||
msgstr "Upřednostňovaný jazyk"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Process started"
|
||||
msgstr "Proces spuštěn"
|
||||
|
||||
#. Use 'Key' if your language requires many more characters
|
||||
#: src/components/add-system.tsx
|
||||
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
|
||||
@@ -949,9 +1163,14 @@ 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"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Relationships"
|
||||
msgstr "Vztahy"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Request a one-time password"
|
||||
msgstr "Požádat o jednorázové heslo"
|
||||
@@ -960,6 +1179,14 @@ msgstr "Požádat o jednorázové heslo"
|
||||
msgid "Request OTP"
|
||||
msgstr "Požádat OTP"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Required by"
|
||||
msgstr "Vyžadováno službou"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Requires"
|
||||
msgstr "Vyžaduje"
|
||||
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
msgid "Reset Password"
|
||||
msgstr "Obnovit heslo"
|
||||
@@ -970,10 +1197,19 @@ msgstr "Obnovit heslo"
|
||||
msgid "Resolved"
|
||||
msgstr "Vyřešeno"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Restarts"
|
||||
msgstr "Restarty"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Pokračovat"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgctxt "Root disk label"
|
||||
msgid "Root"
|
||||
msgstr "Kořenový"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Rotate token"
|
||||
msgstr "Změnit token"
|
||||
@@ -982,6 +1218,10 @@ msgstr "Změnit token"
|
||||
msgid "Rows per page"
|
||||
msgstr "Řádků na stránku"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Runtime Metrics"
|
||||
msgstr "Metriky běhu"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "S.M.A.R.T. Details"
|
||||
msgstr "S.M.A.R.T. Detaily"
|
||||
@@ -1003,6 +1243,18 @@ msgstr "Uložit nastavení"
|
||||
msgid "Save system"
|
||||
msgstr "Uložit systém"
|
||||
|
||||
#: 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"
|
||||
@@ -1015,6 +1267,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"
|
||||
@@ -1023,6 +1279,14 @@ msgstr "Odeslat"
|
||||
msgid "Serial Number"
|
||||
msgstr "Sériové číslo"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Service Details"
|
||||
msgstr "Detaily služby"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Services"
|
||||
msgstr "Služby"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "Nastavte procentuální prahové hodnoty pro barvy měřičů."
|
||||
@@ -1050,18 +1314,30 @@ 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"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Status"
|
||||
msgstr "Stav"
|
||||
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Sub State"
|
||||
msgstr "Podstav"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Swap space used by the system"
|
||||
msgstr "Swap prostor využívaný systémem"
|
||||
@@ -1070,9 +1346,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"
|
||||
@@ -1082,6 +1364,10 @@ msgstr "Systém"
|
||||
msgid "System load averages over time"
|
||||
msgstr "Průměry zatížení systému v průběhu času"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Systemd Services"
|
||||
msgstr "Služby systemd"
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Systems"
|
||||
msgstr "Systémy"
|
||||
@@ -1094,6 +1380,10 @@ msgstr "Systémy lze spravovat v souboru <0>config.yml</0> uvnitř datového adr
|
||||
msgid "Table"
|
||||
msgstr "Tabulka"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Tasks"
|
||||
msgstr "Úlohy"
|
||||
|
||||
#. Temperature label in systems table
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
@@ -1161,7 +1451,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
|
||||
@@ -1177,6 +1467,11 @@ msgstr "Tokeny umožňují agentům připojení a registraci. Otisky jsou stabil
|
||||
msgid "Tokens and fingerprints are used to authenticate WebSocket connections to the hub."
|
||||
msgstr "Tokeny a otisky slouží k ověření připojení WebSocket k uzlu."
|
||||
|
||||
#: src/components/ui/chart.tsx
|
||||
#: src/components/ui/chart.tsx
|
||||
msgid "Total"
|
||||
msgstr "Celkem"
|
||||
|
||||
#: src/components/routes/system/network-sheet.tsx
|
||||
msgid "Total data received for each interface"
|
||||
msgstr "Celkový přijatý objem dat pro každé rozhraní"
|
||||
@@ -1185,6 +1480,19 @@ msgstr "Celkový přijatý objem dat pro každé rozhraní"
|
||||
msgid "Total data sent for each interface"
|
||||
msgstr "Celkový odeslaný objem dat pro každé rozhraní"
|
||||
|
||||
#. placeholder {0}: data.length
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Total: {0}"
|
||||
msgstr "Celkem: {0}"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Triggered by"
|
||||
msgstr "Spuštěno službou"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Triggers"
|
||||
msgstr "Spouštěče"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when 1 minute load average exceeds a threshold"
|
||||
msgstr "Spustí se, když využití paměti během 1 minuty překročí prahovou hodnotu"
|
||||
@@ -1209,6 +1517,10 @@ msgstr "Spustí se, když kombinace up/down překročí prahovou hodnotu"
|
||||
msgid "Triggers when CPU usage exceeds a threshold"
|
||||
msgstr "Spustí se, když využití procesoru překročí prahovou hodnotu"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when GPU usage exceeds a threshold"
|
||||
msgstr "Spustí se, když využití GPU překročí prahovou hodnotu"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when memory usage exceeds a threshold"
|
||||
msgstr "Spustí se, když využití paměti překročí prahovou hodnotu"
|
||||
@@ -1221,10 +1533,16 @@ 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"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Unit file"
|
||||
msgstr "Soubor jednotky"
|
||||
|
||||
#. Temperature / network units
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Unit preferences"
|
||||
@@ -1240,6 +1558,11 @@ msgstr "Univerzální token"
|
||||
msgid "Unknown"
|
||||
msgstr "Neznámá"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Unlimited"
|
||||
msgstr "Neomezeno"
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
@@ -1250,10 +1573,20 @@ 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"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Updated every 10 minutes."
|
||||
msgstr "Aktualizováno každých 10 minut."
|
||||
|
||||
#: src/components/routes/system/network-sheet.tsx
|
||||
msgid "Upload"
|
||||
msgstr "Odeslání"
|
||||
@@ -1266,6 +1599,7 @@ msgstr "Doba provozu"
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Usage"
|
||||
msgstr "Využití"
|
||||
|
||||
@@ -1291,6 +1625,7 @@ msgstr "Hodnota"
|
||||
msgid "View"
|
||||
msgstr "Zobrazení"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/components/routes/system/network-sheet.tsx
|
||||
msgid "View more"
|
||||
msgstr "Zobrazit více"
|
||||
@@ -1311,6 +1646,10 @@ msgstr "Čeká se na dostatek záznamů k zobrazení"
|
||||
msgid "Want to help improve our translations? Check <0>Crowdin</0> for details."
|
||||
msgstr "Chcete nám pomoci s našimi překlady ještě lépe? Podívejte se na <0>Crowdin</0> pro více informací."
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Wants"
|
||||
msgstr "Chce"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Warning (%)"
|
||||
msgstr "Varování (%)"
|
||||
@@ -1347,6 +1686,12 @@ msgstr "YAML konfigurace"
|
||||
msgid "YAML Configuration"
|
||||
msgstr "YAML konfigurace"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Yes"
|
||||
msgstr "Ano"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Your user settings have been updated."
|
||||
msgstr "Vaše uživatelská nastavení byla aktualizována."
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: da\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-10-25 10:58\n"
|
||||
"PO-Revision-Date: 2025-11-14 22:51\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Danish\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -76,13 +76,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"
|
||||
|
||||
@@ -90,14 +93,20 @@ msgstr "Aktiv"
|
||||
msgid "Active Alerts"
|
||||
msgstr "Aktive Alarmer"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Active state"
|
||||
msgstr "Aktiv tilstand"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: 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 <0>System</0>"
|
||||
msgstr "Tilføj <0>System</0>"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add New System"
|
||||
msgstr "Tilføj nyt system"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add system"
|
||||
msgstr "Tilføj system"
|
||||
@@ -110,11 +119,19 @@ msgstr "Tilføj URL"
|
||||
msgid "Adjust display options for charts."
|
||||
msgstr "Juster visningsindstillinger for diagrammer."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Adjust the width of the main layout"
|
||||
msgstr "Juster bredden af hovedlayoutet"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Admin"
|
||||
msgstr "Administrator"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "After"
|
||||
msgstr "Efter"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Agent"
|
||||
@@ -139,6 +156,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"
|
||||
@@ -200,6 +218,18 @@ msgstr "Båndbredde"
|
||||
msgid "Battery"
|
||||
msgstr "Batteri"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Became active"
|
||||
msgstr "Blev aktiv"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Became inactive"
|
||||
msgstr "Blev inaktiv"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Before"
|
||||
msgstr "Før"
|
||||
|
||||
#: 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."
|
||||
@@ -217,6 +247,10 @@ msgstr "Binær"
|
||||
msgid "Bits (Kbps, Mbps, Gbps)"
|
||||
msgstr "Bits (Kbps, Mbps, Gbps)"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Boot state"
|
||||
msgstr "Opstartstilstand"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bytes (KB/s, MB/s, GB/s)"
|
||||
@@ -226,11 +260,28 @@ msgstr "Bytes (KB/s, MB/s, GB/s)"
|
||||
msgid "Cache / Buffers"
|
||||
msgstr "Cache / Buffere"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Can reload"
|
||||
msgstr "Kan genindlæse"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Can start"
|
||||
msgstr "Kan starte"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
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 "Funktioner"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Capacity"
|
||||
msgstr "Kapacitet"
|
||||
@@ -276,6 +327,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."
|
||||
@@ -306,6 +363,10 @@ msgstr "Konfigurer hvordan du modtager advarselsmeddelelser."
|
||||
msgid "Confirm password"
|
||||
msgstr "Bekræft adgangskode"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Conflicts"
|
||||
msgstr "Konflikter"
|
||||
|
||||
#: src/components/active-alerts.tsx
|
||||
msgid "Connection is down"
|
||||
msgstr "Forbindelsen er nede"
|
||||
@@ -366,16 +427,38 @@ msgid "Copy YAML"
|
||||
msgstr "Kopier YAML"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "CPU"
|
||||
msgstr "CPU"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "CPU Cores"
|
||||
msgstr "CPU-kerner"
|
||||
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "CPU Peak"
|
||||
msgstr "CPU Peak"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "CPU time"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "CPU Time Breakdown"
|
||||
msgstr "CPU-tidsfordeling"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/lib/alerts.ts
|
||||
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"
|
||||
@@ -407,15 +490,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"
|
||||
@@ -424,6 +510,10 @@ msgstr "Slet"
|
||||
msgid "Delete fingerprint"
|
||||
msgstr "Slet fingeraftryk"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Description"
|
||||
msgstr "Beskrivelse"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
msgid "Detail"
|
||||
msgstr "Detalje"
|
||||
@@ -472,6 +562,7 @@ msgid "Docker Network I/O"
|
||||
msgstr "Docker Netværk I/O"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Documentation"
|
||||
msgstr "Dokumentation"
|
||||
|
||||
@@ -495,11 +586,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
|
||||
@@ -515,6 +611,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"
|
||||
@@ -531,7 +632,10 @@ msgstr "Indtast din engangsadgangskode."
|
||||
#: 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"
|
||||
msgstr "Fejl"
|
||||
|
||||
@@ -542,10 +646,18 @@ msgstr "Fejl"
|
||||
msgid "Exceeds {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
|
||||
msgstr "Overskrider {0}{1} i sidste {2, plural, one {# minut} other {# minutter}}"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Exec main PID"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
|
||||
msgstr "Eksisterende systemer ikke defineret i <0>config.yml</0> vil blive slettet. Opret venligst regelmæssige sikkerhedskopier."
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Exited active"
|
||||
msgstr "Afsluttet aktiv"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr "Eksporter"
|
||||
@@ -562,6 +674,10 @@ msgstr "Eksporter din nuværende systemkonfiguration."
|
||||
msgid "Fahrenheit (°F)"
|
||||
msgstr "Fahrenheit (°F)"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Failed"
|
||||
msgstr "Mislykkedes"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Failed Attributes:"
|
||||
msgstr "Mislykkede attributter:"
|
||||
@@ -572,6 +688,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"
|
||||
|
||||
@@ -583,10 +700,16 @@ msgstr "Afsendelse af testnotifikation mislykkedes"
|
||||
msgid "Failed to update alert"
|
||||
msgstr "Kunne ikke opdatere alarm"
|
||||
|
||||
#. placeholder {0}: statusTotals[ServiceStatus.Failed]
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Failed: {0}"
|
||||
msgstr "Mislykkedes: {0}"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Filter..."
|
||||
msgstr "Filter..."
|
||||
@@ -597,7 +720,7 @@ msgstr "Fingeraftryk"
|
||||
|
||||
#: 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}}"
|
||||
@@ -624,6 +747,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"
|
||||
@@ -632,6 +759,10 @@ msgstr "GPU-enheder"
|
||||
msgid "GPU Power Draw"
|
||||
msgstr "Gpu Strøm Træk"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "GPU Usage"
|
||||
msgstr "GPU-forbrug"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Grid"
|
||||
msgstr "Gitter"
|
||||
@@ -664,6 +795,10 @@ 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."
|
||||
@@ -681,6 +816,19 @@ msgstr "Sprog"
|
||||
msgid "Layout"
|
||||
msgstr "Opstilling"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Layout width"
|
||||
msgstr "Layoutbredde"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Lifecycle"
|
||||
msgstr "Livscyklus"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "limit"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Load Average"
|
||||
msgstr "Belastning Gennemsnitlig"
|
||||
@@ -702,6 +850,14 @@ msgstr "Belastning Gennemsnitlig 5m"
|
||||
msgid "Load Avg"
|
||||
msgstr "Belastning gns."
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Load state"
|
||||
msgstr "Indlæsningstilstand"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Loading..."
|
||||
msgstr "Indlæser..."
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Log Out"
|
||||
msgstr "Log ud"
|
||||
@@ -725,6 +881,10 @@ msgstr "Logs"
|
||||
msgid "Looking instead for where to create alerts? Click the bell <0/> icons in the systems table."
|
||||
msgstr "Leder du i stedet for efter hvor du kan oprette alarmer? Klik på klokken <0/> ikoner i system tabellen."
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Main PID"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Manage display and notification preferences."
|
||||
msgstr "Administrer display og notifikationsindstillinger."
|
||||
@@ -740,10 +900,21 @@ msgid "Max 1 min"
|
||||
msgstr "Maks. 1 min"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Memory"
|
||||
msgstr "Hukommelse"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Memory limit"
|
||||
msgstr "Hukommelsesgrænse"
|
||||
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Memory Peak"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Memory Usage"
|
||||
@@ -760,13 +931,15 @@ msgstr "Model"
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Name"
|
||||
msgstr "Navn"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Net"
|
||||
msgstr "Net"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Network traffic of docker containers"
|
||||
@@ -784,7 +957,14 @@ msgstr "Netværkstrafik af offentlige grænseflader"
|
||||
msgid "Network unit"
|
||||
msgstr "Netværksenhed"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "No"
|
||||
msgstr "Nej"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "No results found."
|
||||
msgstr "Ingen resultater fundet."
|
||||
|
||||
@@ -793,6 +973,7 @@ msgstr "Ingen resultater fundet."
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "No results."
|
||||
msgstr "Ingen resultater."
|
||||
|
||||
@@ -819,12 +1000,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"
|
||||
@@ -833,10 +1021,15 @@ msgstr "Åbn menu"
|
||||
msgid "Or continue with"
|
||||
msgstr "Eller fortsæt med"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Other"
|
||||
msgstr "Andre"
|
||||
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
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"
|
||||
@@ -869,6 +1062,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"
|
||||
@@ -881,6 +1078,15 @@ msgstr "Sat på pause"
|
||||
msgid "Paused ({pausedSystemsLength})"
|
||||
msgstr "Sat på pause ({pausedSystemsLength})"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Per-core average utilization"
|
||||
msgstr "Gennemsnitlig udnyttelse pr. kerne"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Percentage of time spent in each state"
|
||||
msgstr "Procentdel af tid brugt i hver tilstand"
|
||||
|
||||
#: 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."
|
||||
@@ -932,11 +1138,19 @@ msgstr "Præcis udnyttelse på det registrerede tidspunkt"
|
||||
msgid "Preferred Language"
|
||||
msgstr "Foretrukket sprog"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Process started"
|
||||
msgstr "Proces startet"
|
||||
|
||||
#. Use 'Key' if your language requires many more characters
|
||||
#: src/components/add-system.tsx
|
||||
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
|
||||
@@ -949,9 +1163,14 @@ 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"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Relationships"
|
||||
msgstr "Relationer"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Request a one-time password"
|
||||
msgstr "Anmod om engangsadgangskode"
|
||||
@@ -960,6 +1179,14 @@ msgstr "Anmod om engangsadgangskode"
|
||||
msgid "Request OTP"
|
||||
msgstr "Anmod OTP"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Required by"
|
||||
msgstr "Kræves af"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Requires"
|
||||
msgstr "Kræver"
|
||||
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
msgid "Reset Password"
|
||||
msgstr "Nulstil adgangskode"
|
||||
@@ -970,10 +1197,19 @@ msgstr "Nulstil adgangskode"
|
||||
msgid "Resolved"
|
||||
msgstr "Løst"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Restarts"
|
||||
msgstr "Genstarter"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Genoptag"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgctxt "Root disk label"
|
||||
msgid "Root"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Rotate token"
|
||||
msgstr "Roter nøgle"
|
||||
@@ -982,6 +1218,10 @@ msgstr "Roter nøgle"
|
||||
msgid "Rows per page"
|
||||
msgstr "Rækker per side"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Runtime Metrics"
|
||||
msgstr "Køretidsmålinger"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "S.M.A.R.T. Details"
|
||||
msgstr "S.M.A.R.T.-detaljer"
|
||||
@@ -1003,6 +1243,18 @@ msgstr "Gem indstillinger"
|
||||
msgid "Save system"
|
||||
msgstr "Gem system"
|
||||
|
||||
#: 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"
|
||||
@@ -1015,6 +1267,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"
|
||||
@@ -1023,6 +1279,14 @@ msgstr "Sendt"
|
||||
msgid "Serial Number"
|
||||
msgstr "Serienummer"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Service Details"
|
||||
msgstr "Tjenestedetaljer"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Services"
|
||||
msgstr "Tjenester"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "Indstil procentvise tærskler for målerfarver."
|
||||
@@ -1050,18 +1314,30 @@ 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"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Status"
|
||||
msgstr "Status"
|
||||
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Sub State"
|
||||
msgstr "Undertilstand"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Swap space used by the system"
|
||||
msgstr "Swap plads brugt af systemet"
|
||||
@@ -1070,9 +1346,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"
|
||||
@@ -1082,6 +1364,10 @@ msgstr "System"
|
||||
msgid "System load averages over time"
|
||||
msgstr "Gennemsnitlig system belastning over tid"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Systemd Services"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Systems"
|
||||
msgstr "Systemer"
|
||||
@@ -1094,6 +1380,10 @@ msgstr "Systemer kan være administreres i filen <0>config.yml</0> i din datamap
|
||||
msgid "Table"
|
||||
msgstr "Tabel"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Tasks"
|
||||
msgstr "Opgaver"
|
||||
|
||||
#. Temperature label in systems table
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
@@ -1177,6 +1467,11 @@ msgstr "Nøgler tillader agenter at oprette forbindelse og registrere. Fingeraft
|
||||
msgid "Tokens and fingerprints are used to authenticate WebSocket connections to the hub."
|
||||
msgstr "Nøgler og fingeraftryk bruges til at godkende WebSocket-forbindelser til hubben."
|
||||
|
||||
#: src/components/ui/chart.tsx
|
||||
#: src/components/ui/chart.tsx
|
||||
msgid "Total"
|
||||
msgstr "Samlet"
|
||||
|
||||
#: src/components/routes/system/network-sheet.tsx
|
||||
msgid "Total data received for each interface"
|
||||
msgstr "Samlet modtaget data for hver interface"
|
||||
@@ -1185,6 +1480,19 @@ msgstr "Samlet modtaget data for hver interface"
|
||||
msgid "Total data sent for each interface"
|
||||
msgstr "Samlet sendt data for hver interface"
|
||||
|
||||
#. placeholder {0}: data.length
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Total: {0}"
|
||||
msgstr "I alt: {0}"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Triggered by"
|
||||
msgstr "Udløst af"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Triggers"
|
||||
msgstr "Udløsere"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when 1 minute load average exceeds a threshold"
|
||||
msgstr "Udløser når 1 minut belastning gennemsnit overstiger en tærskel"
|
||||
@@ -1209,6 +1517,10 @@ msgstr "Udløses når de kombinerede op/ned overstiger en tærskel"
|
||||
msgid "Triggers when CPU usage exceeds a threshold"
|
||||
msgstr "Udløser når CPU-forbrug overstiger en tærskel"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when GPU usage exceeds a threshold"
|
||||
msgstr "Udløses når GPU-brug overstiger en grænse"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when memory usage exceeds a threshold"
|
||||
msgstr "Udløser når hukommelsesforbruget overstiger en tærskel"
|
||||
@@ -1221,10 +1533,16 @@ 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 ""
|
||||
|
||||
#. Temperature / network units
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Unit preferences"
|
||||
@@ -1240,6 +1558,11 @@ msgstr "Universalnøgle"
|
||||
msgid "Unknown"
|
||||
msgstr "Ukendt"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Unlimited"
|
||||
msgstr ""
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
@@ -1250,10 +1573,20 @@ 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"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Updated every 10 minutes."
|
||||
msgstr "Opdateret hver 10. minut."
|
||||
|
||||
#: src/components/routes/system/network-sheet.tsx
|
||||
msgid "Upload"
|
||||
msgstr "Overfør"
|
||||
@@ -1266,6 +1599,7 @@ msgstr "Oppetid"
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Usage"
|
||||
msgstr "Forbrug"
|
||||
|
||||
@@ -1291,6 +1625,7 @@ msgstr "Værdi"
|
||||
msgid "View"
|
||||
msgstr "Vis"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/components/routes/system/network-sheet.tsx
|
||||
msgid "View more"
|
||||
msgstr "Se mere"
|
||||
@@ -1311,6 +1646,10 @@ msgstr "Venter på nok posteringer til at vise"
|
||||
msgid "Want to help improve our translations? Check <0>Crowdin</0> for details."
|
||||
msgstr "Vil du hjælpe os med at gøre vores oversættelser endnu bedre? Tjek <0>Crowdin</0> for flere detaljer."
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Wants"
|
||||
msgstr "Ønsker"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Warning (%)"
|
||||
msgstr "Advarsel (%)"
|
||||
@@ -1347,6 +1686,12 @@ msgstr "YAML Konfiguration"
|
||||
msgid "YAML Configuration"
|
||||
msgstr "YAML Konfiguration"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Yes"
|
||||
msgstr "Ja"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Your user settings have been updated."
|
||||
msgstr "Dine brugerindstillinger er opdateret."
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: de\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-10-25 21:09\n"
|
||||
"PO-Revision-Date: 2025-11-14 22:51\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: German\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -76,13 +76,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"
|
||||
|
||||
@@ -90,14 +93,20 @@ msgstr "Aktiv"
|
||||
msgid "Active Alerts"
|
||||
msgstr "Aktive Warnungen"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Active state"
|
||||
msgstr "Aktiver Zustand"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: 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 <0>System</0>"
|
||||
msgstr "<0>System</0> hinzufügen"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add New System"
|
||||
msgstr "Neues System hinzufügen"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add system"
|
||||
msgstr "System hinzufügen"
|
||||
@@ -110,11 +119,19 @@ msgstr "URL hinzufügen"
|
||||
msgid "Adjust display options for charts."
|
||||
msgstr "Anzeigeoptionen für Diagramme anpassen."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Adjust the width of the main layout"
|
||||
msgstr "Breite des Hauptlayouts anpassen"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Admin"
|
||||
msgstr "Admin"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "After"
|
||||
msgstr "Nach"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Agent"
|
||||
@@ -139,6 +156,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"
|
||||
@@ -200,6 +218,18 @@ msgstr "Bandbreite"
|
||||
msgid "Battery"
|
||||
msgstr "Batterie"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Became active"
|
||||
msgstr "Wurde aktiv"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Became inactive"
|
||||
msgstr "Wurde inaktiv"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Before"
|
||||
msgstr "Vor"
|
||||
|
||||
#: 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."
|
||||
@@ -217,6 +247,10 @@ msgstr "Binär"
|
||||
msgid "Bits (Kbps, Mbps, Gbps)"
|
||||
msgstr "Bits (Kbps, Mbps, Gbps)"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Boot state"
|
||||
msgstr "Boot-Zustand"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bytes (KB/s, MB/s, GB/s)"
|
||||
@@ -226,11 +260,28 @@ msgstr "Bytes (KB/s, MB/s, GB/s)"
|
||||
msgid "Cache / Buffers"
|
||||
msgstr "Cache / Puffer"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Can reload"
|
||||
msgstr "Kann neu laden"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Can start"
|
||||
msgstr "Kann starten"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
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"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Capabilities"
|
||||
msgstr "Fähigkeiten"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Capacity"
|
||||
msgstr "Kapazität"
|
||||
@@ -276,6 +327,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."
|
||||
@@ -306,6 +363,10 @@ msgstr "Konfiguriere, wie du Warnbenachrichtigungen erhältst."
|
||||
msgid "Confirm password"
|
||||
msgstr "Passwort bestätigen"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Conflicts"
|
||||
msgstr "Konflikte"
|
||||
|
||||
#: src/components/active-alerts.tsx
|
||||
msgid "Connection is down"
|
||||
msgstr "Verbindung unterbrochen"
|
||||
@@ -366,16 +427,38 @@ msgid "Copy YAML"
|
||||
msgstr "YAML kopieren"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "CPU"
|
||||
msgstr "CPU"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "CPU Cores"
|
||||
msgstr "CPU-Kerne"
|
||||
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "CPU Peak"
|
||||
msgstr "CPU-Spitze"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "CPU time"
|
||||
msgstr "CPU-Zeit"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "CPU Time Breakdown"
|
||||
msgstr "CPU-Zeit-Aufschlüsselung"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/lib/alerts.ts
|
||||
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"
|
||||
@@ -407,15 +490,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"
|
||||
@@ -424,6 +510,10 @@ msgstr "Löschen"
|
||||
msgid "Delete fingerprint"
|
||||
msgstr "Fingerabdruck löschen"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Description"
|
||||
msgstr "Beschreibung"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
msgid "Detail"
|
||||
msgstr "Details"
|
||||
@@ -472,6 +562,7 @@ msgid "Docker Network I/O"
|
||||
msgstr "Docker-Netzwerk-I/O"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Documentation"
|
||||
msgstr "Dokumentation"
|
||||
|
||||
@@ -495,11 +586,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
|
||||
@@ -515,6 +611,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"
|
||||
@@ -531,7 +632,10 @@ msgstr "Geben Sie Ihr Einmalpasswort ein."
|
||||
#: 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"
|
||||
msgstr "Fehler"
|
||||
|
||||
@@ -542,10 +646,18 @@ msgstr "Fehler"
|
||||
msgid "Exceeds {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
|
||||
msgstr "Überschreitet {0}{1} in den letzten {2, plural, one {# Minute} other {# Minuten}}"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Exec main PID"
|
||||
msgstr "Ausführungs-Haupt-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."
|
||||
msgstr "Bestehende Systeme, die nicht in der <0>config.yml</0> definiert sind, werden gelöscht. Bitte mache regelmäßige Backups."
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Exited active"
|
||||
msgstr "Beendet aktiv"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr "Exportieren"
|
||||
@@ -562,6 +674,10 @@ msgstr "Exportiere die aktuelle Systemkonfiguration."
|
||||
msgid "Fahrenheit (°F)"
|
||||
msgstr "Fahrenheit (°F)"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Failed"
|
||||
msgstr "Fehlgeschlagen"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Failed Attributes:"
|
||||
msgstr "Fehlgeschlagene Attribute:"
|
||||
@@ -572,6 +688,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"
|
||||
|
||||
@@ -583,10 +700,16 @@ msgstr "Testbenachrichtigung konnte nicht gesendet werden"
|
||||
msgid "Failed to update alert"
|
||||
msgstr "Warnung konnte nicht aktualisiert werden"
|
||||
|
||||
#. placeholder {0}: statusTotals[ServiceStatus.Failed]
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Failed: {0}"
|
||||
msgstr "Fehlgeschlagen: {0}"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Filter..."
|
||||
msgstr "Filter..."
|
||||
@@ -597,7 +720,7 @@ msgstr "Fingerabdruck"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Firmware"
|
||||
msgstr "Firmware"
|
||||
msgstr "Firmware"
|
||||
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "For <0>{min}</0> {min, plural, one {minute} other {minutes}}"
|
||||
@@ -624,6 +747,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"
|
||||
@@ -632,6 +759,10 @@ msgstr "GPU-Engines"
|
||||
msgid "GPU Power Draw"
|
||||
msgstr "GPU-Leistungsaufnahme"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "GPU Usage"
|
||||
msgstr "GPU-Auslastung"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Grid"
|
||||
msgstr "Raster"
|
||||
@@ -664,6 +795,10 @@ 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."
|
||||
@@ -681,6 +816,19 @@ msgstr "Sprache"
|
||||
msgid "Layout"
|
||||
msgstr "Anordnung"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Layout width"
|
||||
msgstr "Layoutbreite"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Lifecycle"
|
||||
msgstr "Lebenszyklus"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "limit"
|
||||
msgstr "Limit"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Load Average"
|
||||
msgstr "Durchschnittliche Systemlast"
|
||||
@@ -702,6 +850,14 @@ msgstr "Durchschnittliche Systemlast 5 Min"
|
||||
msgid "Load Avg"
|
||||
msgstr "Systemlast"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Load state"
|
||||
msgstr "Ladezustand"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Loading..."
|
||||
msgstr "Lädt..."
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Log Out"
|
||||
msgstr "Abmelden"
|
||||
@@ -725,6 +881,10 @@ msgstr "Protokolle"
|
||||
msgid "Looking instead for where to create alerts? Click the bell <0/> icons in the systems table."
|
||||
msgstr "Du möchtest neue Warnungen erstellen? Klicke dafür auf die Glocken-<0/>-Symbole in der Systemtabelle."
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Main PID"
|
||||
msgstr "Haupt-PID"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Manage display and notification preferences."
|
||||
msgstr "Anzeige- und Benachrichtigungseinstellungen verwalten."
|
||||
@@ -740,10 +900,21 @@ msgid "Max 1 min"
|
||||
msgstr "Max 1 Min"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Memory"
|
||||
msgstr "Arbeitsspeicher"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Memory limit"
|
||||
msgstr "Arbeitsspeicherlimit"
|
||||
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Memory Peak"
|
||||
msgstr "Arbeitsspeicher-Spitze"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Memory Usage"
|
||||
@@ -760,6 +931,8 @@ msgstr "Modell"
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Name"
|
||||
msgstr "Name"
|
||||
|
||||
@@ -784,7 +957,14 @@ msgstr "Netzwerkverkehr der öffentlichen Schnittstellen"
|
||||
msgid "Network unit"
|
||||
msgstr "Netzwerkeinheit"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "No"
|
||||
msgstr "Nein"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "No results found."
|
||||
msgstr "Keine Ergebnisse gefunden."
|
||||
|
||||
@@ -793,6 +973,7 @@ msgstr "Keine Ergebnisse gefunden."
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "No results."
|
||||
msgstr "Keine Ergebnisse."
|
||||
|
||||
@@ -819,12 +1000,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"
|
||||
@@ -833,10 +1021,15 @@ msgstr "Menü öffnen"
|
||||
msgid "Or continue with"
|
||||
msgstr "Oder fortfahren mit"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Other"
|
||||
msgstr "Andere"
|
||||
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
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"
|
||||
@@ -869,6 +1062,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"
|
||||
@@ -881,6 +1078,15 @@ msgstr "Pausiert"
|
||||
msgid "Paused ({pausedSystemsLength})"
|
||||
msgstr "Pausiert ({pausedSystemsLength})"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Per-core average utilization"
|
||||
msgstr "Durchschnittliche Auslastung pro Kern"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Percentage of time spent in each state"
|
||||
msgstr "Prozentsatz der Zeit in jedem Zustand"
|
||||
|
||||
#: 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."
|
||||
@@ -932,11 +1138,19 @@ msgstr "Genaue Nutzung zum aufgezeichneten Zeitpunkt"
|
||||
msgid "Preferred Language"
|
||||
msgstr "Bevorzugte Sprache"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Process started"
|
||||
msgstr "Prozess gestartet"
|
||||
|
||||
#. Use 'Key' if your language requires many more characters
|
||||
#: src/components/add-system.tsx
|
||||
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
|
||||
@@ -949,9 +1163,14 @@ 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"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Relationships"
|
||||
msgstr "Beziehungen"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Request a one-time password"
|
||||
msgstr "Einmalpasswort anfordern"
|
||||
@@ -960,6 +1179,14 @@ msgstr "Einmalpasswort anfordern"
|
||||
msgid "Request OTP"
|
||||
msgstr "OTP anfordern"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Required by"
|
||||
msgstr "Benötigt von"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Requires"
|
||||
msgstr "Benötigt"
|
||||
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
msgid "Reset Password"
|
||||
msgstr "Passwort zurücksetzen"
|
||||
@@ -970,10 +1197,19 @@ msgstr "Passwort zurücksetzen"
|
||||
msgid "Resolved"
|
||||
msgstr "Gelöst"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Restarts"
|
||||
msgstr "Neustarts"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Fortsetzen"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgctxt "Root disk label"
|
||||
msgid "Root"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Rotate token"
|
||||
msgstr "Token rotieren"
|
||||
@@ -982,6 +1218,10 @@ msgstr "Token rotieren"
|
||||
msgid "Rows per page"
|
||||
msgstr "Zeilen pro Seite"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Runtime Metrics"
|
||||
msgstr "Laufzeitmetriken"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "S.M.A.R.T. Details"
|
||||
msgstr "S.M.A.R.T.-Details"
|
||||
@@ -1003,6 +1243,18 @@ msgstr "Einstellungen speichern"
|
||||
msgid "Save system"
|
||||
msgstr "System speichern"
|
||||
|
||||
#: 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"
|
||||
@@ -1015,6 +1267,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"
|
||||
@@ -1023,6 +1279,14 @@ msgstr "Gesendet"
|
||||
msgid "Serial Number"
|
||||
msgstr "Seriennummer"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Service Details"
|
||||
msgstr "Servicedetails"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Services"
|
||||
msgstr "Dienste"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "Prozentuale Schwellenwerte für Zählerfarben festlegen."
|
||||
@@ -1050,18 +1314,30 @@ 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"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Status"
|
||||
msgstr "Status"
|
||||
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Sub State"
|
||||
msgstr "Unterzustand"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Swap space used by the system"
|
||||
msgstr "Vom System genutzter Swap-Speicher"
|
||||
@@ -1070,9 +1346,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"
|
||||
@@ -1082,6 +1364,10 @@ msgstr "System"
|
||||
msgid "System load averages over time"
|
||||
msgstr "Systemlastdurchschnitt im Zeitverlauf"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Systemd Services"
|
||||
msgstr "Systemd-Dienste"
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Systems"
|
||||
msgstr "Systeme"
|
||||
@@ -1094,6 +1380,10 @@ msgstr "Systeme können in einer <0>config.yml</0>-Datei im Datenverzeichnis ver
|
||||
msgid "Table"
|
||||
msgstr "Tabelle"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Tasks"
|
||||
msgstr "Aufgaben"
|
||||
|
||||
#. Temperature label in systems table
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
@@ -1177,6 +1467,11 @@ msgstr "Tokens ermöglichen es Agents, sich zu verbinden und zu registrieren. Fi
|
||||
msgid "Tokens and fingerprints are used to authenticate WebSocket connections to the hub."
|
||||
msgstr "Tokens und Fingerabdrücke werden verwendet, um WebSocket-Verbindungen zum Hub zu authentifizieren."
|
||||
|
||||
#: src/components/ui/chart.tsx
|
||||
#: src/components/ui/chart.tsx
|
||||
msgid "Total"
|
||||
msgstr "Gesamt"
|
||||
|
||||
#: src/components/routes/system/network-sheet.tsx
|
||||
msgid "Total data received for each interface"
|
||||
msgstr "Empfangene Gesamtdatenmenge je Schnittstelle "
|
||||
@@ -1185,6 +1480,19 @@ msgstr "Empfangene Gesamtdatenmenge je Schnittstelle "
|
||||
msgid "Total data sent for each interface"
|
||||
msgstr "Gesendete Gesamtdatenmenge je Schnittstelle"
|
||||
|
||||
#. placeholder {0}: data.length
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Total: {0}"
|
||||
msgstr "Gesamt: {0}"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Triggered by"
|
||||
msgstr "Ausgelöst von"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Triggers"
|
||||
msgstr "Trigger"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when 1 minute load average exceeds a threshold"
|
||||
msgstr "Löst aus, wenn der Lastdurchschnitt der letzten Minute einen Schwellenwert überschreitet"
|
||||
@@ -1209,6 +1517,10 @@ msgstr "Löst aus, wenn die kombinierte Up- und Downloadrate einen Schwellenwert
|
||||
msgid "Triggers when CPU usage exceeds a threshold"
|
||||
msgstr "Löst aus, wenn die CPU-Auslastung einen Schwellenwert überschreitet"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when GPU usage exceeds a threshold"
|
||||
msgstr "Löst aus, wenn die GPU-Auslastung einen Schwellenwert überschreitet"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when memory usage exceeds a threshold"
|
||||
msgstr "Löst aus, wenn die Arbeitsspeichernutzung einen Schwellenwert überschreitet"
|
||||
@@ -1221,10 +1533,16 @@ 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"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Unit file"
|
||||
msgstr "Unit-Datei"
|
||||
|
||||
#. Temperature / network units
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Unit preferences"
|
||||
@@ -1240,6 +1558,11 @@ msgstr "Universeller Token"
|
||||
msgid "Unknown"
|
||||
msgstr "Unbekannt"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Unlimited"
|
||||
msgstr "Unbegrenzt"
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
@@ -1250,10 +1573,20 @@ 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"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Updated every 10 minutes."
|
||||
msgstr "Alle 10 Minuten aktualisiert."
|
||||
|
||||
#: src/components/routes/system/network-sheet.tsx
|
||||
msgid "Upload"
|
||||
msgstr "Hochladen"
|
||||
@@ -1266,6 +1599,7 @@ msgstr "Betriebszeit"
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Usage"
|
||||
msgstr "Nutzung"
|
||||
|
||||
@@ -1291,6 +1625,7 @@ msgstr "Wert"
|
||||
msgid "View"
|
||||
msgstr "Ansicht"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/components/routes/system/network-sheet.tsx
|
||||
msgid "View more"
|
||||
msgstr "Mehr anzeigen"
|
||||
@@ -1311,6 +1646,10 @@ msgstr "Warten auf genügend Datensätze zur Anzeige"
|
||||
msgid "Want to help improve our translations? Check <0>Crowdin</0> for details."
|
||||
msgstr "Möchtest du uns helfen, unsere Übersetzungen noch besser zu machen? Schau dir <0>Crowdin</0> für weitere Details an."
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Wants"
|
||||
msgstr "Möchte"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Warning (%)"
|
||||
msgstr "Warnung (%)"
|
||||
@@ -1347,6 +1686,12 @@ msgstr "YAML-Konfiguration"
|
||||
msgid "YAML Configuration"
|
||||
msgstr "YAML-Konfiguration"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Yes"
|
||||
msgstr "Ja"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Your user settings have been updated."
|
||||
msgstr "Deine Benutzereinstellungen wurden aktualisiert."
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -71,13 +71,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"
|
||||
|
||||
@@ -85,14 +88,20 @@ msgstr "Active"
|
||||
msgid "Active Alerts"
|
||||
msgstr "Active Alerts"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Active state"
|
||||
msgstr "Active state"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: 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 <0>System</0>"
|
||||
msgstr "Add <0>System</0>"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add New System"
|
||||
msgstr "Add New System"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add system"
|
||||
msgstr "Add system"
|
||||
@@ -105,11 +114,19 @@ msgstr "Add URL"
|
||||
msgid "Adjust display options for charts."
|
||||
msgstr "Adjust display options for charts."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Adjust the width of the main layout"
|
||||
msgstr "Adjust the width of the main layout"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Admin"
|
||||
msgstr "Admin"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "After"
|
||||
msgstr "After"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Agent"
|
||||
@@ -134,6 +151,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"
|
||||
@@ -195,6 +213,18 @@ msgstr "Bandwidth"
|
||||
msgid "Battery"
|
||||
msgstr "Battery"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Became active"
|
||||
msgstr "Became active"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Became inactive"
|
||||
msgstr "Became inactive"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Before"
|
||||
msgstr "Before"
|
||||
|
||||
#: 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."
|
||||
@@ -212,6 +242,10 @@ msgstr "Binary"
|
||||
msgid "Bits (Kbps, Mbps, Gbps)"
|
||||
msgstr "Bits (Kbps, Mbps, Gbps)"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Boot state"
|
||||
msgstr "Boot state"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bytes (KB/s, MB/s, GB/s)"
|
||||
@@ -221,11 +255,28 @@ msgstr "Bytes (KB/s, MB/s, GB/s)"
|
||||
msgid "Cache / Buffers"
|
||||
msgstr "Cache / Buffers"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Can reload"
|
||||
msgstr "Can reload"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Can start"
|
||||
msgstr "Can start"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
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"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Capabilities"
|
||||
msgstr "Capabilities"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Capacity"
|
||||
msgstr "Capacity"
|
||||
@@ -271,6 +322,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."
|
||||
@@ -301,6 +358,10 @@ msgstr "Configure how you receive alert notifications."
|
||||
msgid "Confirm password"
|
||||
msgstr "Confirm password"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Conflicts"
|
||||
msgstr "Conflicts"
|
||||
|
||||
#: src/components/active-alerts.tsx
|
||||
msgid "Connection is down"
|
||||
msgstr "Connection is down"
|
||||
@@ -361,16 +422,38 @@ msgid "Copy YAML"
|
||||
msgstr "Copy YAML"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "CPU"
|
||||
msgstr "CPU"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "CPU Cores"
|
||||
msgstr "CPU Cores"
|
||||
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "CPU Peak"
|
||||
msgstr "CPU Peak"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "CPU time"
|
||||
msgstr "CPU time"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "CPU Time Breakdown"
|
||||
msgstr "CPU Time Breakdown"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/lib/alerts.ts
|
||||
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"
|
||||
@@ -402,15 +485,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"
|
||||
@@ -419,6 +505,10 @@ msgstr "Delete"
|
||||
msgid "Delete fingerprint"
|
||||
msgstr "Delete fingerprint"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Description"
|
||||
msgstr "Description"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
msgid "Detail"
|
||||
msgstr "Detail"
|
||||
@@ -467,6 +557,7 @@ msgid "Docker Network I/O"
|
||||
msgstr "Docker Network I/O"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Documentation"
|
||||
msgstr "Documentation"
|
||||
|
||||
@@ -490,11 +581,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
|
||||
@@ -510,6 +606,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"
|
||||
@@ -526,7 +627,10 @@ msgstr "Enter your one-time password."
|
||||
#: 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"
|
||||
msgstr "Error"
|
||||
|
||||
@@ -537,10 +641,18 @@ msgstr "Error"
|
||||
msgid "Exceeds {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
|
||||
msgstr "Exceeds {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Exec main PID"
|
||||
msgstr "Exec main 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."
|
||||
msgstr "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Exited active"
|
||||
msgstr "Exited active"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr "Export"
|
||||
@@ -557,6 +669,10 @@ msgstr "Export your current systems configuration."
|
||||
msgid "Fahrenheit (°F)"
|
||||
msgstr "Fahrenheit (°F)"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Failed"
|
||||
msgstr "Failed"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Failed Attributes:"
|
||||
msgstr "Failed Attributes:"
|
||||
@@ -567,6 +683,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"
|
||||
|
||||
@@ -578,10 +695,16 @@ msgstr "Failed to send test notification"
|
||||
msgid "Failed to update alert"
|
||||
msgstr "Failed to update alert"
|
||||
|
||||
#. placeholder {0}: statusTotals[ServiceStatus.Failed]
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Failed: {0}"
|
||||
msgstr "Failed: {0}"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Filter..."
|
||||
msgstr "Filter..."
|
||||
@@ -619,6 +742,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"
|
||||
@@ -627,6 +754,10 @@ msgstr "GPU Engines"
|
||||
msgid "GPU Power Draw"
|
||||
msgstr "GPU Power Draw"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "GPU Usage"
|
||||
msgstr "GPU Usage"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Grid"
|
||||
msgstr "Grid"
|
||||
@@ -659,6 +790,10 @@ 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."
|
||||
@@ -676,6 +811,19 @@ msgstr "Language"
|
||||
msgid "Layout"
|
||||
msgstr "Layout"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Layout width"
|
||||
msgstr "Layout width"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Lifecycle"
|
||||
msgstr "Lifecycle"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "limit"
|
||||
msgstr "limit"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Load Average"
|
||||
msgstr "Load Average"
|
||||
@@ -697,6 +845,14 @@ msgstr "Load Average 5m"
|
||||
msgid "Load Avg"
|
||||
msgstr "Load Avg"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Load state"
|
||||
msgstr "Load state"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Loading..."
|
||||
msgstr "Loading..."
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Log Out"
|
||||
msgstr "Log Out"
|
||||
@@ -720,6 +876,10 @@ msgstr "Logs"
|
||||
msgid "Looking instead for where to create alerts? Click the bell <0/> icons in the systems table."
|
||||
msgstr "Looking instead for where to create alerts? Click the bell <0/> icons in the systems table."
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Main PID"
|
||||
msgstr "Main PID"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Manage display and notification preferences."
|
||||
msgstr "Manage display and notification preferences."
|
||||
@@ -735,10 +895,21 @@ msgid "Max 1 min"
|
||||
msgstr "Max 1 min"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Memory"
|
||||
msgstr "Memory"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Memory limit"
|
||||
msgstr "Memory limit"
|
||||
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Memory Peak"
|
||||
msgstr "Memory Peak"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Memory Usage"
|
||||
@@ -755,6 +926,8 @@ msgstr "Model"
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Name"
|
||||
msgstr "Name"
|
||||
|
||||
@@ -779,7 +952,14 @@ msgstr "Network traffic of public interfaces"
|
||||
msgid "Network unit"
|
||||
msgstr "Network unit"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "No"
|
||||
msgstr "No"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "No results found."
|
||||
msgstr "No results found."
|
||||
|
||||
@@ -788,6 +968,7 @@ msgstr "No results found."
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "No results."
|
||||
msgstr "No results."
|
||||
|
||||
@@ -814,12 +995,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"
|
||||
@@ -828,10 +1016,15 @@ msgstr "Open menu"
|
||||
msgid "Or continue with"
|
||||
msgstr "Or continue with"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Other"
|
||||
msgstr "Other"
|
||||
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
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"
|
||||
@@ -864,6 +1057,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"
|
||||
@@ -876,6 +1073,15 @@ msgstr "Paused"
|
||||
msgid "Paused ({pausedSystemsLength})"
|
||||
msgstr "Paused ({pausedSystemsLength})"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Per-core average utilization"
|
||||
msgstr "Per-core average utilization"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Percentage of time spent in each state"
|
||||
msgstr "Percentage of time spent in each state"
|
||||
|
||||
#: 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."
|
||||
@@ -927,11 +1133,19 @@ msgstr "Precise utilization at the recorded time"
|
||||
msgid "Preferred Language"
|
||||
msgstr "Preferred Language"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Process started"
|
||||
msgstr "Process started"
|
||||
|
||||
#. Use 'Key' if your language requires many more characters
|
||||
#: src/components/add-system.tsx
|
||||
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
|
||||
@@ -944,9 +1158,14 @@ 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"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Relationships"
|
||||
msgstr "Relationships"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Request a one-time password"
|
||||
msgstr "Request a one-time password"
|
||||
@@ -955,6 +1174,14 @@ msgstr "Request a one-time password"
|
||||
msgid "Request OTP"
|
||||
msgstr "Request OTP"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Required by"
|
||||
msgstr "Required by"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Requires"
|
||||
msgstr "Requires"
|
||||
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
msgid "Reset Password"
|
||||
msgstr "Reset Password"
|
||||
@@ -965,10 +1192,19 @@ msgstr "Reset Password"
|
||||
msgid "Resolved"
|
||||
msgstr "Resolved"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Restarts"
|
||||
msgstr "Restarts"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Resume"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgctxt "Root disk label"
|
||||
msgid "Root"
|
||||
msgstr "Root"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Rotate token"
|
||||
msgstr "Rotate token"
|
||||
@@ -977,6 +1213,10 @@ msgstr "Rotate token"
|
||||
msgid "Rows per page"
|
||||
msgstr "Rows per page"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Runtime Metrics"
|
||||
msgstr "Runtime Metrics"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "S.M.A.R.T. Details"
|
||||
msgstr "S.M.A.R.T. Details"
|
||||
@@ -998,6 +1238,18 @@ msgstr "Save Settings"
|
||||
msgid "Save system"
|
||||
msgstr "Save system"
|
||||
|
||||
#: 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"
|
||||
@@ -1010,6 +1262,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"
|
||||
@@ -1018,6 +1274,14 @@ msgstr "Sent"
|
||||
msgid "Serial Number"
|
||||
msgstr "Serial Number"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Service Details"
|
||||
msgstr "Service Details"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Services"
|
||||
msgstr "Services"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "Set percentage thresholds for meter colors."
|
||||
@@ -1045,18 +1309,30 @@ 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"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Status"
|
||||
msgstr "Status"
|
||||
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Sub State"
|
||||
msgstr "Sub State"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Swap space used by the system"
|
||||
msgstr "Swap space used by the system"
|
||||
@@ -1065,9 +1341,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"
|
||||
@@ -1077,6 +1359,10 @@ msgstr "System"
|
||||
msgid "System load averages over time"
|
||||
msgstr "System load averages over time"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Systemd Services"
|
||||
msgstr "Systemd Services"
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Systems"
|
||||
msgstr "Systems"
|
||||
@@ -1089,6 +1375,10 @@ msgstr "Systems may be managed in a <0>config.yml</0> file inside your data dire
|
||||
msgid "Table"
|
||||
msgstr "Table"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Tasks"
|
||||
msgstr "Tasks"
|
||||
|
||||
#. Temperature label in systems table
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
@@ -1172,6 +1462,11 @@ msgstr "Tokens allow agents to connect and register. Fingerprints are stable ide
|
||||
msgid "Tokens and fingerprints are used to authenticate WebSocket connections to the hub."
|
||||
msgstr "Tokens and fingerprints are used to authenticate WebSocket connections to the hub."
|
||||
|
||||
#: src/components/ui/chart.tsx
|
||||
#: src/components/ui/chart.tsx
|
||||
msgid "Total"
|
||||
msgstr "Total"
|
||||
|
||||
#: src/components/routes/system/network-sheet.tsx
|
||||
msgid "Total data received for each interface"
|
||||
msgstr "Total data received for each interface"
|
||||
@@ -1180,6 +1475,19 @@ msgstr "Total data received for each interface"
|
||||
msgid "Total data sent for each interface"
|
||||
msgstr "Total data sent for each interface"
|
||||
|
||||
#. placeholder {0}: data.length
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Total: {0}"
|
||||
msgstr "Total: {0}"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Triggered by"
|
||||
msgstr "Triggered by"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Triggers"
|
||||
msgstr "Triggers"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when 1 minute load average exceeds a threshold"
|
||||
msgstr "Triggers when 1 minute load average exceeds a threshold"
|
||||
@@ -1204,6 +1512,10 @@ msgstr "Triggers when combined up/down exceeds a threshold"
|
||||
msgid "Triggers when CPU usage exceeds a threshold"
|
||||
msgstr "Triggers when CPU usage exceeds a threshold"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when GPU usage exceeds a threshold"
|
||||
msgstr "Triggers when GPU usage exceeds a threshold"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when memory usage exceeds a threshold"
|
||||
msgstr "Triggers when memory usage exceeds a threshold"
|
||||
@@ -1216,10 +1528,16 @@ 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"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Unit file"
|
||||
msgstr "Unit file"
|
||||
|
||||
#. Temperature / network units
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Unit preferences"
|
||||
@@ -1235,6 +1553,11 @@ msgstr "Universal token"
|
||||
msgid "Unknown"
|
||||
msgstr "Unknown"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Unlimited"
|
||||
msgstr "Unlimited"
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
@@ -1245,10 +1568,20 @@ 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"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Updated every 10 minutes."
|
||||
msgstr "Updated every 10 minutes."
|
||||
|
||||
#: src/components/routes/system/network-sheet.tsx
|
||||
msgid "Upload"
|
||||
msgstr "Upload"
|
||||
@@ -1261,6 +1594,7 @@ msgstr "Uptime"
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Usage"
|
||||
msgstr "Usage"
|
||||
|
||||
@@ -1286,6 +1620,7 @@ msgstr "Value"
|
||||
msgid "View"
|
||||
msgstr "View"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/components/routes/system/network-sheet.tsx
|
||||
msgid "View more"
|
||||
msgstr "View more"
|
||||
@@ -1306,6 +1641,10 @@ msgstr "Waiting for enough records to display"
|
||||
msgid "Want to help improve our translations? Check <0>Crowdin</0> for details."
|
||||
msgstr "Want to help improve our translations? Check <0>Crowdin</0> for details."
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Wants"
|
||||
msgstr "Wants"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Warning (%)"
|
||||
msgstr "Warning (%)"
|
||||
@@ -1342,6 +1681,12 @@ msgstr "YAML Config"
|
||||
msgid "YAML Configuration"
|
||||
msgstr "YAML Configuration"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Yes"
|
||||
msgstr "Yes"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Your user settings have been updated."
|
||||
msgstr "Your user settings have been updated."
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: es\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-10-25 21:09\n"
|
||||
"PO-Revision-Date: 2025-12-01 23:32\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Spanish\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -76,13 +76,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"
|
||||
|
||||
@@ -90,14 +93,20 @@ msgstr "Activo"
|
||||
msgid "Active Alerts"
|
||||
msgstr "Alertas activas"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Active state"
|
||||
msgstr "Estado activo"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: 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 <0>System</0>"
|
||||
msgstr "Agregar <0>sistema</0>"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add New System"
|
||||
msgstr "Agregar nuevo sistema"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add system"
|
||||
msgstr "Agregar sistema"
|
||||
@@ -110,11 +119,19 @@ msgstr "Agregar URL"
|
||||
msgid "Adjust display options for charts."
|
||||
msgstr "Ajustar las opciones de visualización para los gráficos."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Adjust the width of the main layout"
|
||||
msgstr "Ajustar el ancho del diseño principal"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Admin"
|
||||
msgstr "Administrador"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "After"
|
||||
msgstr "Después"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Agente"
|
||||
@@ -123,7 +140,7 @@ msgstr "Agente"
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Alert History"
|
||||
msgstr "Historial de Alertas"
|
||||
msgstr "Historial de alertas"
|
||||
|
||||
#: src/components/alerts/alert-button.tsx
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
@@ -139,10 +156,11 @@ 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"
|
||||
msgstr "Todos los Sistemas"
|
||||
msgstr "Todos los sistemas"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Are you sure you want to delete {name}?"
|
||||
@@ -200,6 +218,18 @@ msgstr "Ancho de banda"
|
||||
msgid "Battery"
|
||||
msgstr "Batería"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Became active"
|
||||
msgstr "Se activó"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Became inactive"
|
||||
msgstr "Se desactivó"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Before"
|
||||
msgstr "Antes"
|
||||
|
||||
#: 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."
|
||||
@@ -217,6 +247,10 @@ msgstr "Binario"
|
||||
msgid "Bits (Kbps, Mbps, Gbps)"
|
||||
msgstr "Bits (kbps, Mbps, Gbps)"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Boot state"
|
||||
msgstr "Estado de arranque"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bytes (KB/s, MB/s, GB/s)"
|
||||
@@ -226,11 +260,28 @@ msgstr "Bytes (kB/s, MB/s, GB/s)"
|
||||
msgid "Cache / Buffers"
|
||||
msgstr "Caché / Buffers"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Can reload"
|
||||
msgstr "Puede recargarse"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Can start"
|
||||
msgstr "Puede iniciarse"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Can stop"
|
||||
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"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Capabilities"
|
||||
msgstr "Capacidades"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Capacity"
|
||||
msgstr "Capacidad"
|
||||
@@ -276,9 +327,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 ""
|
||||
|
||||
#: 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."
|
||||
@@ -306,6 +363,10 @@ msgstr "Configura cómo recibe las notificaciones de alertas."
|
||||
msgid "Confirm password"
|
||||
msgstr "Confirmar contraseña"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Conflicts"
|
||||
msgstr "Conflictos"
|
||||
|
||||
#: src/components/active-alerts.tsx
|
||||
msgid "Connection is down"
|
||||
msgstr "La conexión está caída"
|
||||
@@ -366,16 +427,38 @@ msgid "Copy YAML"
|
||||
msgstr "Copiar YAML"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "CPU"
|
||||
msgstr "CPU"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "CPU Cores"
|
||||
msgstr "Núcleos de CPU"
|
||||
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "CPU Peak"
|
||||
msgstr "Pico de CPU"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "CPU time"
|
||||
msgstr "Tiempo de CPU"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "CPU Time Breakdown"
|
||||
msgstr "Desglose de tiempo de CPU"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/lib/alerts.ts
|
||||
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"
|
||||
@@ -407,15 +490,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"
|
||||
@@ -424,6 +510,10 @@ msgstr "Eliminar"
|
||||
msgid "Delete fingerprint"
|
||||
msgstr "Eliminar huella digital"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Description"
|
||||
msgstr "Descripción"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
msgid "Detail"
|
||||
msgstr "Detalle"
|
||||
@@ -472,6 +562,7 @@ msgid "Docker Network I/O"
|
||||
msgstr "E/S de red de Docker"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Documentation"
|
||||
msgstr "Documentación"
|
||||
|
||||
@@ -495,11 +586,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
|
||||
@@ -515,6 +611,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"
|
||||
@@ -531,7 +632,10 @@ msgstr "Ingrese su contraseña de un solo uso."
|
||||
#: 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"
|
||||
msgstr "Error"
|
||||
|
||||
@@ -542,10 +646,18 @@ msgstr "Error"
|
||||
msgid "Exceeds {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
|
||||
msgstr "Excede {0}{1} en el último {2, plural, one {# minuto} other {# minutos}}"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Exec main PID"
|
||||
msgstr "PID principal de ejecución"
|
||||
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
|
||||
msgstr "Los sistemas existentes no definidos en <0>config.yml</0> serán eliminados. Por favor, haz copias de seguridad regularmente."
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Exited active"
|
||||
msgstr "Salió activo"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr "Exportar"
|
||||
@@ -562,6 +674,10 @@ msgstr "Exporta la configuración actual de sus sistemas."
|
||||
msgid "Fahrenheit (°F)"
|
||||
msgstr "Fahrenheit (°F)"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Failed"
|
||||
msgstr "Fallido"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Failed Attributes:"
|
||||
msgstr "Atributos fallidos:"
|
||||
@@ -572,6 +688,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"
|
||||
|
||||
@@ -583,10 +700,16 @@ msgstr "Error al enviar la notificación de prueba"
|
||||
msgid "Failed to update alert"
|
||||
msgstr "Error al actualizar la alerta"
|
||||
|
||||
#. placeholder {0}: statusTotals[ServiceStatus.Failed]
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Failed: {0}"
|
||||
msgstr "Fallidos: {0}"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Filter..."
|
||||
msgstr "Filtrar..."
|
||||
@@ -624,6 +747,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"
|
||||
@@ -632,6 +759,10 @@ msgstr "Motores GPU"
|
||||
msgid "GPU Power Draw"
|
||||
msgstr "Consumo de energía de la GPU"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "GPU Usage"
|
||||
msgstr "Uso de GPU"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Grid"
|
||||
msgstr "Cuadrícula"
|
||||
@@ -664,6 +795,10 @@ 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."
|
||||
@@ -681,6 +816,19 @@ msgstr "Idioma"
|
||||
msgid "Layout"
|
||||
msgstr "Diseño"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Layout width"
|
||||
msgstr "Ancho del diseño"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Lifecycle"
|
||||
msgstr "Ciclo de vida"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "limit"
|
||||
msgstr "límite"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Load Average"
|
||||
msgstr "Carga media"
|
||||
@@ -702,6 +850,14 @@ msgstr "Carga media 5m"
|
||||
msgid "Load Avg"
|
||||
msgstr "Carga media"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Load state"
|
||||
msgstr "Estado de carga"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Loading..."
|
||||
msgstr "Cargando..."
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Log Out"
|
||||
msgstr "Cerrar sesión"
|
||||
@@ -725,6 +881,10 @@ msgstr "Registros"
|
||||
msgid "Looking instead for where to create alerts? Click the bell <0/> icons in the systems table."
|
||||
msgstr "¿Buscas dónde crear alertas? Haz clic en los iconos de campana <0/> en la tabla de sistemas."
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Main PID"
|
||||
msgstr "PID principal"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Manage display and notification preferences."
|
||||
msgstr "Administrar preferencias de visualización y notificaciones."
|
||||
@@ -737,21 +897,32 @@ msgstr "Instrucciones manuales de configuración"
|
||||
#. Chart select field. Please try to keep this short.
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Max 1 min"
|
||||
msgstr "Máx 1 min"
|
||||
msgstr "Máx. 1 min"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Memory"
|
||||
msgstr "Memoria"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Memory limit"
|
||||
msgstr "Límite de memoria"
|
||||
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Memory Peak"
|
||||
msgstr "Pico de memoria"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Memory Usage"
|
||||
msgstr "Uso de Memoria"
|
||||
msgstr "Uso de memoria"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Memory usage of docker containers"
|
||||
msgstr "Uso de memoria de los contenedores de Docker"
|
||||
msgstr "Uso de memoria de los contenedores Docker"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Model"
|
||||
@@ -760,6 +931,8 @@ msgstr "Modelo"
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Name"
|
||||
msgstr "Nombre"
|
||||
|
||||
@@ -770,7 +943,7 @@ msgstr "Red"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Network traffic of docker containers"
|
||||
msgstr "Tráfico de red de los contenedores de Docker"
|
||||
msgstr "Tráfico de red de los contenedores Docker"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/network-sheet.tsx
|
||||
@@ -784,7 +957,14 @@ msgstr "Tráfico de red de interfaces públicas"
|
||||
msgid "Network unit"
|
||||
msgstr "Unidad de red"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "No"
|
||||
msgstr "No"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "No results found."
|
||||
msgstr "No se encontraron resultados."
|
||||
|
||||
@@ -793,6 +973,7 @@ msgstr "No se encontraron resultados."
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "No results."
|
||||
msgstr "Sin resultados."
|
||||
|
||||
@@ -819,12 +1000,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ú"
|
||||
@@ -833,10 +1021,15 @@ msgstr "Abrir menú"
|
||||
msgid "Or continue with"
|
||||
msgstr "O continuar con"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Other"
|
||||
msgstr "Otro"
|
||||
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
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"
|
||||
@@ -869,6 +1062,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"
|
||||
@@ -881,38 +1078,47 @@ msgstr "Pausado"
|
||||
msgid "Paused ({pausedSystemsLength})"
|
||||
msgstr "Pausado ({pausedSystemsLength})"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Per-core average utilization"
|
||||
msgstr "Uso promedio por núcleo"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Percentage of time spent in each state"
|
||||
msgstr "Porcentaje de tiempo dedicado a cada estado"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
msgstr "Por favor, <0>configure un servidor SMTP</0> para asegurar que las alertas sean entregadas."
|
||||
msgstr "Por favor, <0>configura un servidor SMTP</0> para asegurar que las alertas sean entregadas."
|
||||
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "Please check logs for more details."
|
||||
msgstr "Por favor, revise los registros para más detalles."
|
||||
msgstr "Por favor, revisa los registros para más detalles."
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
msgid "Please check your credentials and try again"
|
||||
msgstr "Por favor, verifique sus credenciales e intente de nuevo"
|
||||
msgstr "Por favor, verifica tus credenciales e inténtalo de nuevo"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Please create an admin account"
|
||||
msgstr "Por favor, cree una cuenta de administrador"
|
||||
msgstr "Por favor, crea una cuenta de administrador"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Please enable pop-ups for this site"
|
||||
msgstr "Por favor, habilite las ventanas emergentes para este sitio"
|
||||
msgstr "Por favor, habilita las ventanas emergentes para este sitio"
|
||||
|
||||
#: src/lib/api.ts
|
||||
msgid "Please log in again"
|
||||
msgstr "Por favor, inicie sesión de nuevo"
|
||||
msgstr "Por favor, inicia sesión de nuevo"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Please see <0>the documentation</0> for instructions."
|
||||
msgstr "Por favor, consulte <0>la documentación</0> para obtener instrucciones."
|
||||
msgstr "Por favor, consulta <0>la documentación</0> para obtener instrucciones."
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Please sign in to your account"
|
||||
msgstr "Por favor, inicie sesión en su cuenta"
|
||||
msgstr "Por favor, inicia sesión en tu cuenta"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Port"
|
||||
@@ -930,12 +1136,20 @@ msgstr "Utilización precisa en el momento registrado"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Preferred Language"
|
||||
msgstr "Idioma Preferido"
|
||||
msgstr "Idioma preferido"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Process started"
|
||||
msgstr "Proceso iniciado"
|
||||
|
||||
#. Use 'Key' if your language requires many more characters
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Public Key"
|
||||
msgstr "Clave Pública"
|
||||
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
|
||||
@@ -949,9 +1163,14 @@ 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"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Relationships"
|
||||
msgstr "Relaciones"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Request a one-time password"
|
||||
msgstr "Solicitar contraseña de un solo uso"
|
||||
@@ -960,9 +1179,17 @@ msgstr "Solicitar contraseña de un solo uso"
|
||||
msgid "Request OTP"
|
||||
msgstr "Solicitar OTP"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Required by"
|
||||
msgstr "Requerido por"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Requires"
|
||||
msgstr "Requiere"
|
||||
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
msgid "Reset Password"
|
||||
msgstr "Restablecer Contraseña"
|
||||
msgstr "Restablecer contraseña"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
@@ -970,10 +1197,19 @@ msgstr "Restablecer Contraseña"
|
||||
msgid "Resolved"
|
||||
msgstr "Resuelto"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Restarts"
|
||||
msgstr "Reinicios"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Reanudar"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgctxt "Root disk label"
|
||||
msgid "Root"
|
||||
msgstr "Raíz"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Rotate token"
|
||||
msgstr "Rotar token"
|
||||
@@ -982,6 +1218,10 @@ msgstr "Rotar token"
|
||||
msgid "Rows per page"
|
||||
msgstr "Filas por página"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Runtime Metrics"
|
||||
msgstr "Métricas de tiempo de ejecución"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "S.M.A.R.T. Details"
|
||||
msgstr "Detalles S.M.A.R.T."
|
||||
@@ -992,16 +1232,28 @@ msgstr "Autoprueba S.M.A.R.T."
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Save address using enter key or comma. Leave blank to disable email notifications."
|
||||
msgstr "Guarde la dirección usando la tecla enter o coma. Deje en blanco para desactivar las notificaciones por correo."
|
||||
msgstr "Guarda la dirección usando la tecla enter o coma. Deja en blanco para desactivar las notificaciones por correo."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Save Settings"
|
||||
msgstr "Guardar Configuración"
|
||||
msgstr "Guardar configuración"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Save system"
|
||||
msgstr "Guardar Sistema"
|
||||
msgstr "Guardar sistema"
|
||||
|
||||
#: 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"
|
||||
@@ -1013,7 +1265,11 @@ msgstr "Buscar sistemas o configuraciones..."
|
||||
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "Consulte <0>configuración de notificaciones</0> para configurar cómo recibe alertas."
|
||||
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"
|
||||
@@ -1023,6 +1279,14 @@ msgstr "Enviado"
|
||||
msgid "Serial Number"
|
||||
msgstr "Número de serie"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Service Details"
|
||||
msgstr "Detalles del servicio"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Services"
|
||||
msgstr "Servicios"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "Establecer umbrales de porcentaje para los colores de los medidores."
|
||||
@@ -1050,29 +1314,47 @@ 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"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Status"
|
||||
msgstr "Estado"
|
||||
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Sub State"
|
||||
msgstr "Subestado"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Swap space used by the system"
|
||||
msgstr "Espacio de swap utilizado por el sistema"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Swap Usage"
|
||||
msgstr "Uso de Swap"
|
||||
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"
|
||||
@@ -1082,18 +1364,26 @@ msgstr "Sistema"
|
||||
msgid "System load averages over time"
|
||||
msgstr "Promedios de carga del sistema a lo largo del tiempo"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Systemd Services"
|
||||
msgstr "Servicios de systemd"
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Systems"
|
||||
msgstr "Sistemas"
|
||||
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
msgid "Systems may be managed in a <0>config.yml</0> file inside your data directory."
|
||||
msgstr "Los sistemas pueden ser gestionados en un archivo <0>config.yml</0> dentro de su directorio de datos."
|
||||
msgstr "Los sistemas pueden ser gestionados en un archivo <0>config.yml</0> dentro de tu directorio de datos."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Table"
|
||||
msgstr "Tabla"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Tasks"
|
||||
msgstr "Tareas"
|
||||
|
||||
#. Temperature label in systems table
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
@@ -1123,7 +1413,7 @@ msgstr "Notificación de prueba enviada"
|
||||
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
msgid "Then log into the backend and reset your user account password in the users table."
|
||||
msgstr "Luego inicie sesión en el backend y restablezca la contraseña de su cuenta de usuario en la tabla de usuarios."
|
||||
msgstr "Luego inicia sesión en el backend y restablece la contraseña de tu cuenta de usuario en la tabla de usuarios."
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "This action cannot be undone. This will permanently delete all current records for {name} from the database."
|
||||
@@ -1167,7 +1457,7 @@ msgstr "Token"
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Tokens & Fingerprints"
|
||||
msgstr "Tokens y Huellas Digitales"
|
||||
msgstr "Tokens y huellas digitales"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Tokens allow agents to connect and register. Fingerprints are stable identifiers unique to each system, set on first connection."
|
||||
@@ -1177,6 +1467,11 @@ msgstr "Los tokens permiten que los agentes se conecten y registren. Las huellas
|
||||
msgid "Tokens and fingerprints are used to authenticate WebSocket connections to the hub."
|
||||
msgstr "Los tokens y las huellas digitales se utilizan para autenticar las conexiones WebSocket al hub."
|
||||
|
||||
#: src/components/ui/chart.tsx
|
||||
#: src/components/ui/chart.tsx
|
||||
msgid "Total"
|
||||
msgstr "Total"
|
||||
|
||||
#: src/components/routes/system/network-sheet.tsx
|
||||
msgid "Total data received for each interface"
|
||||
msgstr "Datos totales recibidos por cada interfaz"
|
||||
@@ -1185,6 +1480,19 @@ msgstr "Datos totales recibidos por cada interfaz"
|
||||
msgid "Total data sent for each interface"
|
||||
msgstr "Datos totales enviados por cada interfaz"
|
||||
|
||||
#. placeholder {0}: data.length
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Total: {0}"
|
||||
msgstr "Total: {0}"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Triggered by"
|
||||
msgstr "Activado por"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Triggers"
|
||||
msgstr "Activadores"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when 1 minute load average exceeds a threshold"
|
||||
msgstr "Se activa cuando la carga media de 1 minuto supera un umbral"
|
||||
@@ -1209,6 +1517,10 @@ msgstr "Se activa cuando la suma de subida/bajada supera un umbral"
|
||||
msgid "Triggers when CPU usage exceeds a threshold"
|
||||
msgstr "Se activa cuando el uso de CPU supera un umbral"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when GPU usage exceeds a threshold"
|
||||
msgstr "Se activa cuando el uso de GPU supera un umbral"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when memory usage exceeds a threshold"
|
||||
msgstr "Se activa cuando el uso de memoria supera un umbral"
|
||||
@@ -1221,10 +1533,16 @@ 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"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Unit file"
|
||||
msgstr "Archivo de unidad"
|
||||
|
||||
#. Temperature / network units
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Unit preferences"
|
||||
@@ -1240,6 +1558,11 @@ msgstr "Token universal"
|
||||
msgid "Unknown"
|
||||
msgstr "Desconocida"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Unlimited"
|
||||
msgstr "Ilimitado"
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
@@ -1250,10 +1573,20 @@ 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"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Updated every 10 minutes."
|
||||
msgstr "Actualizado cada 10 minutos."
|
||||
|
||||
#: src/components/routes/system/network-sheet.tsx
|
||||
msgid "Upload"
|
||||
msgstr "Cargar"
|
||||
@@ -1266,6 +1599,7 @@ msgstr "Tiempo de actividad"
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Usage"
|
||||
msgstr "Uso"
|
||||
|
||||
@@ -1291,13 +1625,14 @@ msgstr "Valor"
|
||||
msgid "View"
|
||||
msgstr "Vista"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/components/routes/system/network-sheet.tsx
|
||||
msgid "View more"
|
||||
msgstr "Ver más"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "View your 200 most recent alerts."
|
||||
msgstr "Ver sus 200 alertas más recientes."
|
||||
msgstr "Ver tus 200 alertas más recientes."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Visible Fields"
|
||||
@@ -1309,7 +1644,11 @@ msgstr "Esperando suficientes registros para mostrar"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Want to help improve our translations? Check <0>Crowdin</0> for details."
|
||||
msgstr "¿Quieres ayudarnos a mejorar nuestras traducciones? Consulta <0>Crowdin</0> para más detalles."
|
||||
msgstr "¿Quieres ayudar a mejorar nuestras traducciones? Consulta <0>Crowdin</0> para más detalles."
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Wants"
|
||||
msgstr "Desea"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Warning (%)"
|
||||
@@ -1347,6 +1686,12 @@ msgstr "Configuración YAML"
|
||||
msgid "YAML Configuration"
|
||||
msgstr "Configuración YAML"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Yes"
|
||||
msgstr "Sí"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Your user settings have been updated."
|
||||
msgstr "Su configuración de usuario ha sido actualizada."
|
||||
msgstr "Tu configuración de usuario ha sido actualizada."
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: fa\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-10-20 21:37\n"
|
||||
"PO-Revision-Date: 2025-11-14 22:51\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Persian\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -76,13 +76,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 "فعال"
|
||||
|
||||
@@ -90,14 +93,20 @@ msgstr "فعال"
|
||||
msgid "Active Alerts"
|
||||
msgstr " هشدارهای فعال"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Active state"
|
||||
msgstr "وضعیت فعال"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: 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 <0>System</0>"
|
||||
msgstr "افزودن <0>سیستم</0>"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add New System"
|
||||
msgstr "افزودن سیستم جدید"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add system"
|
||||
msgstr "افزودن سیستم"
|
||||
@@ -110,11 +119,19 @@ msgstr "افزودن آدرس اینترنتی"
|
||||
msgid "Adjust display options for charts."
|
||||
msgstr "تنظیم گزینههای نمایش برای نمودارها."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Adjust the width of the main layout"
|
||||
msgstr "تنظیم عرض چیدمان اصلی"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Admin"
|
||||
msgstr "مدیر"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "After"
|
||||
msgstr "بعد از"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "عامل"
|
||||
@@ -139,6 +156,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"
|
||||
@@ -200,6 +218,18 @@ msgstr "پهنای باند"
|
||||
msgid "Battery"
|
||||
msgstr "باتری"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Became active"
|
||||
msgstr "فعال شد"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Became inactive"
|
||||
msgstr "غیرفعال شد"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Before"
|
||||
msgstr "قبل از"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Beszel supports OpenID Connect and many OAuth2 authentication providers."
|
||||
msgstr "بِزل از OpenID Connect و بسیاری از ارائهدهندگان احراز هویت OAuth2 پشتیبانی میکند."
|
||||
@@ -217,6 +247,10 @@ msgstr "دودویی"
|
||||
msgid "Bits (Kbps, Mbps, Gbps)"
|
||||
msgstr "بیت (کیلوبیت بر ثانیه، مگابیت بر ثانیه، گیگابیت بر ثانیه)"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Boot state"
|
||||
msgstr "وضعیت بوت"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bytes (KB/s, MB/s, GB/s)"
|
||||
@@ -226,11 +260,28 @@ msgstr "بایت (کیلوبایت بر ثانیه، مگابایت بر ثان
|
||||
msgid "Cache / Buffers"
|
||||
msgstr "حافظه پنهان / بافرها"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Can reload"
|
||||
msgstr "میتواند بارگذاری مجدد شود"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Can start"
|
||||
msgstr "میتواند شروع شود"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
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 "لغو"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Capabilities"
|
||||
msgstr "قابلیتها"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Capacity"
|
||||
msgstr "ظرفیت"
|
||||
@@ -276,6 +327,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 "برای مشاهده اطلاعات بیشتر روی کانتینر کلیک کنید."
|
||||
@@ -306,6 +363,10 @@ msgstr "نحوه دریافت هشدارهای اطلاعرسانی را پی
|
||||
msgid "Confirm password"
|
||||
msgstr "تأیید رمز عبور"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Conflicts"
|
||||
msgstr "تعارضها"
|
||||
|
||||
#: src/components/active-alerts.tsx
|
||||
msgid "Connection is down"
|
||||
msgstr "اتصال قطع است"
|
||||
@@ -366,16 +427,38 @@ msgid "Copy YAML"
|
||||
msgstr "کپی YAML"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "CPU"
|
||||
msgstr "پردازنده"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "CPU Cores"
|
||||
msgstr "هستههای CPU"
|
||||
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "CPU Peak"
|
||||
msgstr "حداکثر CPU"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "CPU time"
|
||||
msgstr "زمان CPU"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "CPU Time Breakdown"
|
||||
msgstr "تجزیه زمان CPU"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "CPU Usage"
|
||||
msgstr "میزان استفاده از پردازنده"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Create"
|
||||
msgstr "ایجاد"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Create account"
|
||||
msgstr "ایجاد حساب کاربری"
|
||||
@@ -407,15 +490,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 "حذف"
|
||||
@@ -424,6 +510,10 @@ msgstr "حذف"
|
||||
msgid "Delete fingerprint"
|
||||
msgstr "حذف اثر انگشت"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Description"
|
||||
msgstr "توضیحات"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
msgid "Detail"
|
||||
msgstr "جزئیات"
|
||||
@@ -472,6 +562,7 @@ msgid "Docker Network I/O"
|
||||
msgstr "ورودی/خروجی شبکه داکر"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Documentation"
|
||||
msgstr "مستندات"
|
||||
|
||||
@@ -495,11 +586,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
|
||||
@@ -515,6 +611,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 "آدرس ایمیل را برای بازنشانی رمز عبور وارد کنید"
|
||||
@@ -531,7 +632,10 @@ msgstr "رمز عبور یکبار مصرف خود را وارد کنید."
|
||||
#: 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"
|
||||
msgstr "خطا"
|
||||
|
||||
@@ -542,10 +646,18 @@ msgstr "خطا"
|
||||
msgid "Exceeds {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
|
||||
msgstr "در {2, plural, one {# دقیقه} other {# دقیقه}} گذشته از {0}{1} بیشتر است"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Exec main PID"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
|
||||
msgstr "سیستمهای موجود که در <0>config.yml</0> تعریف نشدهاند حذف خواهند شد. لطفاً به طور منظم پشتیبانگیری کنید."
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Exited active"
|
||||
msgstr "خروج فعال"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr "خروجی گرفتن"
|
||||
@@ -562,6 +674,10 @@ msgstr "پیکربندی سیستمهای فعلی خود را خارج کن
|
||||
msgid "Fahrenheit (°F)"
|
||||
msgstr "فارنهایت (°F)"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Failed"
|
||||
msgstr "ناموفق"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Failed Attributes:"
|
||||
msgstr "ویژگیهای ناموفق:"
|
||||
@@ -572,6 +688,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 "ذخیره تنظیمات ناموفق بود"
|
||||
|
||||
@@ -583,10 +700,16 @@ msgstr "ارسال اعلان آزمایشی ناموفق بود"
|
||||
msgid "Failed to update alert"
|
||||
msgstr "بهروزرسانی هشدار ناموفق بود"
|
||||
|
||||
#. placeholder {0}: statusTotals[ServiceStatus.Failed]
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Failed: {0}"
|
||||
msgstr "ناموفق: {0}"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Filter..."
|
||||
msgstr "فیلتر..."
|
||||
@@ -624,6 +747,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"
|
||||
@@ -632,6 +759,10 @@ msgstr "موتورهای GPU"
|
||||
msgid "GPU Power Draw"
|
||||
msgstr "مصرف برق پردازنده گرافیکی"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "GPU Usage"
|
||||
msgstr "میزان استفاده از GPU"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Grid"
|
||||
msgstr "جدول"
|
||||
@@ -664,6 +795,10 @@ 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 "آدرس ایمیل نامعتبر است."
|
||||
@@ -681,6 +816,19 @@ msgstr "زبان"
|
||||
msgid "Layout"
|
||||
msgstr "طرحبندی"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Layout width"
|
||||
msgstr "عرض چیدمان"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Lifecycle"
|
||||
msgstr "چرخه حیات"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "limit"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Load Average"
|
||||
msgstr "میانگین بار"
|
||||
@@ -702,6 +850,14 @@ msgstr "میانگین بار ۵ دقیقه"
|
||||
msgid "Load Avg"
|
||||
msgstr "میانگین بار"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Load state"
|
||||
msgstr "وضعیت بارگذاری"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Loading..."
|
||||
msgstr "در حال بارگذاری..."
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Log Out"
|
||||
msgstr "خروج"
|
||||
@@ -725,6 +881,10 @@ msgstr "لاگها"
|
||||
msgid "Looking instead for where to create alerts? Click the bell <0/> icons in the systems table."
|
||||
msgstr "به دنبال جایی برای ایجاد هشدار هستید؟ روی آیکونهای زنگ <0/> در جدول سیستمها کلیک کنید."
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Main PID"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Manage display and notification preferences."
|
||||
msgstr "مدیریت تنظیمات نمایش و اعلانها."
|
||||
@@ -740,10 +900,21 @@ msgid "Max 1 min"
|
||||
msgstr "حداکثر ۱ دقیقه"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Memory"
|
||||
msgstr "حافظه"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Memory limit"
|
||||
msgstr "محدودیت حافظه"
|
||||
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Memory Peak"
|
||||
msgstr "حداکثر حافظه"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Memory Usage"
|
||||
@@ -760,6 +931,8 @@ msgstr "مدل"
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Name"
|
||||
msgstr "نام"
|
||||
|
||||
@@ -784,7 +957,14 @@ msgstr "ترافیک شبکه رابطهای عمومی"
|
||||
msgid "Network unit"
|
||||
msgstr "واحد شبکه"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "No"
|
||||
msgstr "خیر"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "No results found."
|
||||
msgstr "هیچ نتیجهای یافت نشد."
|
||||
|
||||
@@ -793,6 +973,7 @@ msgstr "هیچ نتیجهای یافت نشد."
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "No results."
|
||||
msgstr "نتیجهای یافت نشد."
|
||||
|
||||
@@ -819,12 +1000,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 "باز کردن منو"
|
||||
@@ -833,10 +1021,15 @@ msgstr "باز کردن منو"
|
||||
msgid "Or continue with"
|
||||
msgstr "یا ادامه با"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Other"
|
||||
msgstr "سایر"
|
||||
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "Overwrite existing alerts"
|
||||
msgstr "بازنویسی هشدارهای موجود"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Page"
|
||||
@@ -869,6 +1062,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 "توقف"
|
||||
@@ -881,6 +1078,15 @@ msgstr "مکث شده"
|
||||
msgid "Paused ({pausedSystemsLength})"
|
||||
msgstr "مکث شده ({pausedSystemsLength})"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Per-core average utilization"
|
||||
msgstr "میانگین استفاده در هر هسته"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Percentage of time spent in each state"
|
||||
msgstr "درصد زمان صرف شده در هر حالت"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
msgstr "لطفاً برای اطمینان از تحویل هشدارها، یک <0>سرور SMTP پیکربندی کنید</0>."
|
||||
@@ -932,11 +1138,19 @@ msgstr "میزان دقیق استفاده در زمان ثبت شده"
|
||||
msgid "Preferred Language"
|
||||
msgstr "زبان ترجیحی"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Process started"
|
||||
msgstr "فرآیند شروع شد"
|
||||
|
||||
#. Use 'Key' if your language requires many more characters
|
||||
#: src/components/add-system.tsx
|
||||
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
|
||||
@@ -949,9 +1163,14 @@ 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 "تازهسازی"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Relationships"
|
||||
msgstr "روابط"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Request a one-time password"
|
||||
msgstr "درخواست رمز عبور یکبار مصرف"
|
||||
@@ -960,6 +1179,14 @@ msgstr "درخواست رمز عبور یکبار مصرف"
|
||||
msgid "Request OTP"
|
||||
msgstr "درخواست OTP"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Required by"
|
||||
msgstr "مورد نیاز توسط"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Requires"
|
||||
msgstr "نیازمند"
|
||||
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
msgid "Reset Password"
|
||||
msgstr "بازنشانی رمز عبور"
|
||||
@@ -970,10 +1197,19 @@ msgstr "بازنشانی رمز عبور"
|
||||
msgid "Resolved"
|
||||
msgstr "حل شده"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Restarts"
|
||||
msgstr "راهاندازی مجدد"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Resume"
|
||||
msgstr "ادامه"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgctxt "Root disk label"
|
||||
msgid "Root"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Rotate token"
|
||||
msgstr "چرخش توکن"
|
||||
@@ -982,6 +1218,10 @@ msgstr "چرخش توکن"
|
||||
msgid "Rows per page"
|
||||
msgstr "ردیف در هر صفحه"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Runtime Metrics"
|
||||
msgstr "معیارهای زمان اجرا"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "S.M.A.R.T. Details"
|
||||
msgstr "جزئیات S.M.A.R.T"
|
||||
@@ -1003,6 +1243,18 @@ msgstr "ذخیره تنظیمات"
|
||||
msgid "Save system"
|
||||
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 "جستجو"
|
||||
@@ -1015,6 +1267,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 "ارسال شد"
|
||||
@@ -1023,6 +1279,14 @@ msgstr "ارسال شد"
|
||||
msgid "Serial Number"
|
||||
msgstr "شماره سریال"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Service Details"
|
||||
msgstr "جزئیات سرویس"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Services"
|
||||
msgstr "سرویسها"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "آستانه های درصدی را برای رنگ های متر تنظیم کنید."
|
||||
@@ -1050,18 +1314,30 @@ 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 "وضعیت"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Status"
|
||||
msgstr "وضعیت"
|
||||
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Sub State"
|
||||
msgstr "وضعیت فرعی"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Swap space used by the system"
|
||||
msgstr "فضای Swap استفاده شده توسط سیستم"
|
||||
@@ -1070,9 +1346,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"
|
||||
@@ -1082,6 +1364,10 @@ msgstr "سیستم"
|
||||
msgid "System load averages over time"
|
||||
msgstr "میانگین بار سیستم در طول زمان"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Systemd Services"
|
||||
msgstr "خدمات Systemd"
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Systems"
|
||||
msgstr "سیستمها"
|
||||
@@ -1094,6 +1380,10 @@ msgstr "سیستمها ممکن است در یک فایل <0>config.yml</0>
|
||||
msgid "Table"
|
||||
msgstr "جدول"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Tasks"
|
||||
msgstr "وظایف"
|
||||
|
||||
#. Temperature label in systems table
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
@@ -1177,6 +1467,11 @@ msgstr "توکنها به عاملها اجازه اتصال و ثبت
|
||||
msgid "Tokens and fingerprints are used to authenticate WebSocket connections to the hub."
|
||||
msgstr "توکنها و اثرات انگشت برای احراز هویت اتصالات WebSocket به هاب استفاده میشوند."
|
||||
|
||||
#: src/components/ui/chart.tsx
|
||||
#: src/components/ui/chart.tsx
|
||||
msgid "Total"
|
||||
msgstr "کل"
|
||||
|
||||
#: src/components/routes/system/network-sheet.tsx
|
||||
msgid "Total data received for each interface"
|
||||
msgstr "دادههای کل دریافت شده برای هر رابط"
|
||||
@@ -1185,6 +1480,19 @@ msgstr "دادههای کل دریافت شده برای هر رابط"
|
||||
msgid "Total data sent for each interface"
|
||||
msgstr "دادههای کل ارسال شده برای هر رابط"
|
||||
|
||||
#. placeholder {0}: data.length
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Total: {0}"
|
||||
msgstr "کل: {0}"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Triggered by"
|
||||
msgstr "فعال شده توسط"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Triggers"
|
||||
msgstr "محرکها"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when 1 minute load average exceeds a threshold"
|
||||
msgstr "هنگامی که میانگین بار ۱ دقیقهای از یک آستانه فراتر رود، فعال میشود"
|
||||
@@ -1209,6 +1517,10 @@ msgstr "هنگامی که مجموع بالا/پایین از یک آستانه
|
||||
msgid "Triggers when CPU usage exceeds a threshold"
|
||||
msgstr "هنگامی که میزان استفاده از CPU از یک آستانه فراتر رود، فعال میشود"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when GPU usage exceeds a threshold"
|
||||
msgstr "هنگامی که میزان استفاده از GPU از یک آستانه فراتر رود، فعال میشود"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when memory usage exceeds a threshold"
|
||||
msgstr "هنگامی که میزان استفاده از حافظه از یک آستانه فراتر رود، فعال میشود"
|
||||
@@ -1221,10 +1533,16 @@ 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 ""
|
||||
|
||||
#. Temperature / network units
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Unit preferences"
|
||||
@@ -1240,6 +1558,11 @@ msgstr "توکن جهانی"
|
||||
msgid "Unknown"
|
||||
msgstr "ناشناخته"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Unlimited"
|
||||
msgstr "نامحدود"
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
@@ -1250,10 +1573,20 @@ 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 "بهروزرسانی شد"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Updated every 10 minutes."
|
||||
msgstr "هر ۱۰ دقیقه بهروزرسانی میشود."
|
||||
|
||||
#: src/components/routes/system/network-sheet.tsx
|
||||
msgid "Upload"
|
||||
msgstr "آپلود"
|
||||
@@ -1266,6 +1599,7 @@ msgstr "آپتایم"
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Usage"
|
||||
msgstr "میزان استفاده"
|
||||
|
||||
@@ -1291,6 +1625,7 @@ msgstr "مقدار"
|
||||
msgid "View"
|
||||
msgstr "مشاهده"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/components/routes/system/network-sheet.tsx
|
||||
msgid "View more"
|
||||
msgstr "مشاهده بیشتر"
|
||||
@@ -1311,6 +1646,10 @@ msgstr "در انتظار رکوردهای کافی برای نمایش"
|
||||
msgid "Want to help improve our translations? Check <0>Crowdin</0> for details."
|
||||
msgstr "میخواهید به ما کمک کنید تا ترجمههای خود را بهتر کنیم؟ برای جزئیات بیشتر به <0>Crowdin</0> مراجعه کنید."
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Wants"
|
||||
msgstr "میخواهد"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Warning (%)"
|
||||
msgstr "هشدار (%)"
|
||||
@@ -1347,6 +1686,12 @@ msgstr "پیکربندی YAML"
|
||||
msgid "YAML Configuration"
|
||||
msgstr "پیکربندی YAML"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Yes"
|
||||
msgstr "بله"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Your user settings have been updated."
|
||||
msgstr "تنظیمات کاربری شما بهروزرسانی شد."
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: fr\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-10-25 20:53\n"
|
||||
"PO-Revision-Date: 2025-11-14 22:51\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: French\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
@@ -34,7 +34,7 @@ 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/lib/utils.ts
|
||||
msgid "1 hour"
|
||||
@@ -76,13 +76,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,14 +93,20 @@ msgstr "Active"
|
||||
msgid "Active Alerts"
|
||||
msgstr "Alertes actives"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Active state"
|
||||
msgstr "État actif"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: 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 <0>System</0>"
|
||||
msgstr "Ajouter <0>un Système</0>"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add New System"
|
||||
msgstr "Ajouter un nouveau système"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add system"
|
||||
msgstr "Ajouter un système"
|
||||
@@ -110,11 +119,19 @@ msgstr "Ajouter l’URL"
|
||||
msgid "Adjust display options for charts."
|
||||
msgstr "Ajuster les options d'affichage pour les graphiques."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Adjust the width of the main layout"
|
||||
msgstr "Ajuster la largeur de la mise en page principale"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Admin"
|
||||
msgstr "Admin"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "After"
|
||||
msgstr "Après"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Agent"
|
||||
@@ -139,6 +156,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"
|
||||
@@ -200,6 +218,18 @@ msgstr "Bande passante"
|
||||
msgid "Battery"
|
||||
msgstr "Batterie"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Became active"
|
||||
msgstr "Devenu actif"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Became inactive"
|
||||
msgstr "Devenu inactif"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Before"
|
||||
msgstr "Avant"
|
||||
|
||||
#: 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."
|
||||
@@ -217,6 +247,10 @@ msgstr "Binaire"
|
||||
msgid "Bits (Kbps, Mbps, Gbps)"
|
||||
msgstr "Bits (Kbps, Mbps, Gbps)"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Boot state"
|
||||
msgstr "État de démarrage"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bytes (KB/s, MB/s, GB/s)"
|
||||
@@ -226,11 +260,28 @@ msgstr "Bytes (KB/s, MB/s, GB/s)"
|
||||
msgid "Cache / Buffers"
|
||||
msgstr "Cache / Tampons"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Can reload"
|
||||
msgstr "Peut recharger"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Can start"
|
||||
msgstr "Peut démarrer"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
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"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Capabilities"
|
||||
msgstr "Capacités"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Capacity"
|
||||
msgstr "Capacité"
|
||||
@@ -276,6 +327,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."
|
||||
@@ -306,6 +363,10 @@ msgstr "Configurez comment vous recevez les notifications d'alerte."
|
||||
msgid "Confirm password"
|
||||
msgstr "Confirmer le mot de passe"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Conflicts"
|
||||
msgstr "Conflits"
|
||||
|
||||
#: src/components/active-alerts.tsx
|
||||
msgid "Connection is down"
|
||||
msgstr "Connexion interrompue"
|
||||
@@ -366,16 +427,38 @@ msgid "Copy YAML"
|
||||
msgstr "Copier YAML"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "CPU"
|
||||
msgstr "CPU"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "CPU Cores"
|
||||
msgstr "Cœurs CPU"
|
||||
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "CPU Peak"
|
||||
msgstr "Pic CPU"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "CPU time"
|
||||
msgstr "Temps CPU"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "CPU Time Breakdown"
|
||||
msgstr "Répartition du temps CPU"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/lib/alerts.ts
|
||||
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"
|
||||
@@ -407,15 +490,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"
|
||||
@@ -424,6 +510,10 @@ msgstr "Supprimer"
|
||||
msgid "Delete fingerprint"
|
||||
msgstr "Supprimer l'empreinte"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Description"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
msgid "Detail"
|
||||
msgstr "Détail"
|
||||
@@ -472,6 +562,7 @@ msgid "Docker Network I/O"
|
||||
msgstr "Entrée/Sortie réseau Docker"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Documentation"
|
||||
msgstr "Documentation"
|
||||
|
||||
@@ -495,11 +586,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
|
||||
@@ -515,6 +611,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"
|
||||
@@ -531,7 +632,10 @@ msgstr "Entrez votre mot de passe à usage unique."
|
||||
#: 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"
|
||||
msgstr "Erreur"
|
||||
|
||||
@@ -542,10 +646,18 @@ msgstr "Erreur"
|
||||
msgid "Exceeds {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
|
||||
msgstr "Dépasse {0}{1} dans {2, plural, one {la dernière # minute} other {les dernières # minutes}}"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Exec main PID"
|
||||
msgstr "PID principal d'exécution"
|
||||
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
|
||||
msgstr "Les systèmes existants non définis dans <0>config.yml</0> seront supprimés. Veuillez faire des sauvegardes régulières."
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Exited active"
|
||||
msgstr "Sorti actif"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr "Exporter"
|
||||
@@ -562,6 +674,10 @@ msgstr "Exportez la configuration actuelle de vos systèmes."
|
||||
msgid "Fahrenheit (°F)"
|
||||
msgstr "Fahrenheit (°F)"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Failed"
|
||||
msgstr "Échoué"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Failed Attributes:"
|
||||
msgstr "Attributs défaillants :"
|
||||
@@ -572,6 +688,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"
|
||||
|
||||
@@ -583,10 +700,16 @@ msgstr "Échec de l'envoi de la notification de test"
|
||||
msgid "Failed to update alert"
|
||||
msgstr "Échec de la mise à jour de l'alerte"
|
||||
|
||||
#. placeholder {0}: statusTotals[ServiceStatus.Failed]
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Failed: {0}"
|
||||
msgstr "Échec : {0}"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Filter..."
|
||||
msgstr "Filtrer..."
|
||||
@@ -624,6 +747,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"
|
||||
@@ -632,6 +759,10 @@ msgstr "Moteurs GPU"
|
||||
msgid "GPU Power Draw"
|
||||
msgstr "Consommation du GPU"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "GPU Usage"
|
||||
msgstr "Utilisation GPU"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Grid"
|
||||
msgstr "Grille"
|
||||
@@ -664,6 +795,10 @@ 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."
|
||||
@@ -681,6 +816,19 @@ msgstr "Langue"
|
||||
msgid "Layout"
|
||||
msgstr "Disposition"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Layout width"
|
||||
msgstr "Largeur de la mise en page"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Lifecycle"
|
||||
msgstr "Cycle de vie"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "limit"
|
||||
msgstr "limite"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Load Average"
|
||||
msgstr "Charge moyenne"
|
||||
@@ -702,6 +850,14 @@ msgstr "Charge moyenne 5m"
|
||||
msgid "Load Avg"
|
||||
msgstr "Charge moy."
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Load state"
|
||||
msgstr "État de charge"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Loading..."
|
||||
msgstr "Chargement..."
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Log Out"
|
||||
msgstr "Déconnexion"
|
||||
@@ -725,6 +881,10 @@ msgstr "Journaux"
|
||||
msgid "Looking instead for where to create alerts? Click the bell <0/> icons in the systems table."
|
||||
msgstr "Vous cherchez plutôt où créer des alertes ? Cliquez sur les icônes de cloche <0/> dans le tableau des systèmes."
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Main PID"
|
||||
msgstr "PID principal"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Manage display and notification preferences."
|
||||
msgstr "Gérer les préférences d'affichage et de notification."
|
||||
@@ -740,10 +900,21 @@ msgid "Max 1 min"
|
||||
msgstr "Max 1 min"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Memory"
|
||||
msgstr "Mémoire"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Memory limit"
|
||||
msgstr "Limite mémoire"
|
||||
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Memory Peak"
|
||||
msgstr "Pic mémoire"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Memory Usage"
|
||||
@@ -760,6 +931,8 @@ msgstr "Modèle"
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Name"
|
||||
msgstr "Nom"
|
||||
|
||||
@@ -784,7 +957,14 @@ msgstr "Trafic réseau des interfaces publiques"
|
||||
msgid "Network unit"
|
||||
msgstr "Unité réseau"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "No"
|
||||
msgstr "Non"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "No results found."
|
||||
msgstr "Aucun résultat trouvé."
|
||||
|
||||
@@ -793,6 +973,7 @@ msgstr "Aucun résultat trouvé."
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "No results."
|
||||
msgstr "Aucun résultat."
|
||||
|
||||
@@ -819,12 +1000,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"
|
||||
@@ -833,10 +1021,15 @@ msgstr "Ouvrir le menu"
|
||||
msgid "Or continue with"
|
||||
msgstr "Ou continuer avec"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Other"
|
||||
msgstr "Autre"
|
||||
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
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"
|
||||
@@ -869,6 +1062,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"
|
||||
@@ -881,6 +1078,15 @@ msgstr "En pause"
|
||||
msgid "Paused ({pausedSystemsLength})"
|
||||
msgstr "Mis en pause ({pausedSystemsLength})"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Per-core average utilization"
|
||||
msgstr "Utilisation moyenne par cœur"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Percentage of time spent in each state"
|
||||
msgstr "Pourcentage de temps passé dans chaque état"
|
||||
|
||||
#: 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."
|
||||
@@ -932,11 +1138,19 @@ msgstr "Utilisation précise au moment enregistré"
|
||||
msgid "Preferred Language"
|
||||
msgstr "Langue préférée"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Process started"
|
||||
msgstr "Processus démarré"
|
||||
|
||||
#. Use 'Key' if your language requires many more characters
|
||||
#: src/components/add-system.tsx
|
||||
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
|
||||
@@ -949,9 +1163,14 @@ 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"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Relationships"
|
||||
msgstr "Relations"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Request a one-time password"
|
||||
msgstr "Demander un mot de passe à usage unique"
|
||||
@@ -960,6 +1179,14 @@ msgstr "Demander un mot de passe à usage unique"
|
||||
msgid "Request OTP"
|
||||
msgstr "Demander OTP"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Required by"
|
||||
msgstr "Requis par"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Requires"
|
||||
msgstr "Requiert"
|
||||
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
msgid "Reset Password"
|
||||
msgstr "Réinitialiser le mot de passe"
|
||||
@@ -968,12 +1195,21 @@ msgstr "Réinitialiser le mot de passe"
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Resolved"
|
||||
msgstr "Résolution"
|
||||
msgstr "Résolu"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Restarts"
|
||||
msgstr "Redémarrages"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Reprendre"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgctxt "Root disk label"
|
||||
msgid "Root"
|
||||
msgstr "Racine"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Rotate token"
|
||||
msgstr "Faire tourner le token"
|
||||
@@ -982,6 +1218,10 @@ msgstr "Faire tourner le token"
|
||||
msgid "Rows per page"
|
||||
msgstr "Lignes par page"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Runtime Metrics"
|
||||
msgstr "Métriques d'exécution"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "S.M.A.R.T. Details"
|
||||
msgstr "Détails S.M.A.R.T."
|
||||
@@ -1003,6 +1243,18 @@ msgstr "Enregistrer les paramètres"
|
||||
msgid "Save system"
|
||||
msgstr "Sauvegarder le système"
|
||||
|
||||
#: 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"
|
||||
@@ -1015,6 +1267,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é"
|
||||
@@ -1023,6 +1279,14 @@ msgstr "Envoyé"
|
||||
msgid "Serial Number"
|
||||
msgstr "Numéro de série"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Service Details"
|
||||
msgstr "Détails du service"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Services"
|
||||
msgstr "Services"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "Définir des seuils de pourcentage pour les couleurs des compteurs."
|
||||
@@ -1050,18 +1314,30 @@ 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"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Status"
|
||||
msgstr "Statut"
|
||||
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Sub State"
|
||||
msgstr "Sous-état"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Swap space used by the system"
|
||||
msgstr "Espace Swap utilisé par le système"
|
||||
@@ -1070,9 +1346,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"
|
||||
@@ -1082,6 +1364,10 @@ msgstr "Système"
|
||||
msgid "System load averages over time"
|
||||
msgstr "Charges moyennes du système dans le temps"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Systemd Services"
|
||||
msgstr "Services systemd"
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Systems"
|
||||
msgstr "Systèmes"
|
||||
@@ -1094,6 +1380,10 @@ msgstr "Les systèmes peuvent être gérés dans un fichier <0>config.yml</0> à
|
||||
msgid "Table"
|
||||
msgstr "Tableau"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Tasks"
|
||||
msgstr "Tâches"
|
||||
|
||||
#. Temperature label in systems table
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
@@ -1177,6 +1467,11 @@ msgstr "Les tokens permettent aux agents de se connecter et de s'enregistrer. Le
|
||||
msgid "Tokens and fingerprints are used to authenticate WebSocket connections to the hub."
|
||||
msgstr "Les tokens et les empreintes sont utilisés pour authentifier les connexions WebSocket vers le hub."
|
||||
|
||||
#: src/components/ui/chart.tsx
|
||||
#: src/components/ui/chart.tsx
|
||||
msgid "Total"
|
||||
msgstr "Total"
|
||||
|
||||
#: src/components/routes/system/network-sheet.tsx
|
||||
msgid "Total data received for each interface"
|
||||
msgstr "Données totales reçues pour chaque interface"
|
||||
@@ -1185,6 +1480,19 @@ msgstr "Données totales reçues pour chaque interface"
|
||||
msgid "Total data sent for each interface"
|
||||
msgstr "Données totales envoyées pour chaque interface"
|
||||
|
||||
#. placeholder {0}: data.length
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Total: {0}"
|
||||
msgstr "Total : {0}"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Triggered by"
|
||||
msgstr "Déclenché par"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Triggers"
|
||||
msgstr "Déclencheurs"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when 1 minute load average exceeds a threshold"
|
||||
msgstr "Se déclenche lorsque la charge moyenne sur 1 minute dépasse un seuil"
|
||||
@@ -1209,6 +1517,10 @@ msgstr "Déclenchement lorsque le montant/descendant combinée dépasse un seuil
|
||||
msgid "Triggers when CPU usage exceeds a threshold"
|
||||
msgstr "Déclenchement lorsque l'utilisation du CPU dépasse un seuil"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when GPU usage exceeds a threshold"
|
||||
msgstr "Déclenchement lorsque l'utilisation du GPU dépasse un seuil"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when memory usage exceeds a threshold"
|
||||
msgstr "Déclenchement lorsque l'utilisation de la mémoire dépasse un seuil"
|
||||
@@ -1221,10 +1533,16 @@ 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"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Unit file"
|
||||
msgstr "Fichier unité"
|
||||
|
||||
#. Temperature / network units
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Unit preferences"
|
||||
@@ -1240,6 +1558,11 @@ msgstr "Token universel"
|
||||
msgid "Unknown"
|
||||
msgstr "Inconnue"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Unlimited"
|
||||
msgstr "Illimité"
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
@@ -1250,10 +1573,20 @@ 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"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Updated every 10 minutes."
|
||||
msgstr "Mis à jour toutes les 10 minutes."
|
||||
|
||||
#: src/components/routes/system/network-sheet.tsx
|
||||
msgid "Upload"
|
||||
msgstr "Téléverser"
|
||||
@@ -1266,6 +1599,7 @@ msgstr "Temps de fonctionnement"
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Usage"
|
||||
msgstr "Utilisation"
|
||||
|
||||
@@ -1291,6 +1625,7 @@ msgstr "Valeur"
|
||||
msgid "View"
|
||||
msgstr "Vue"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/components/routes/system/network-sheet.tsx
|
||||
msgid "View more"
|
||||
msgstr "Voir plus"
|
||||
@@ -1311,6 +1646,10 @@ msgstr "En attente de suffisamment d'enregistrements à afficher"
|
||||
msgid "Want to help improve our translations? Check <0>Crowdin</0> for details."
|
||||
msgstr "Vous voulez nous aider à améliorer nos traductions ? Consultez <0>Crowdin</0> pour plus de détails."
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Wants"
|
||||
msgstr "Souhaite"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Warning (%)"
|
||||
msgstr "Avertissement (%)"
|
||||
@@ -1347,6 +1686,12 @@ msgstr "Configuration YAML"
|
||||
msgid "YAML Configuration"
|
||||
msgstr "Configuration YAML"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Yes"
|
||||
msgstr "Oui"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Your user settings have been updated."
|
||||
msgstr "Vos paramètres utilisateur ont été mis à jour."
|
||||
|
||||
@@ -1,17 +1,22 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"POT-Creation-Date: 2025-10-30 16:35-0400\n"
|
||||
"POT-Creation-Date: 2024-11-01 11:30-0400\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: @lingui/cli\n"
|
||||
"Language: he\n"
|
||||
"Project-Id-Version: \n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-10-30 20:47\n"
|
||||
"PO-Revision-Date: 2025-11-14 22:51\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"Plural-Forms: \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"
|
||||
"X-Crowdin-Project: beszel\n"
|
||||
"X-Crowdin-Project-ID: 733311\n"
|
||||
"X-Crowdin-Language: he\n"
|
||||
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
|
||||
"X-Crowdin-File-ID: 32\n"
|
||||
|
||||
#. placeholder {0}: table.getFilteredSelectedRowModel().rows.length
|
||||
#. placeholder {1}: table.getFilteredRowModel().rows.length
|
||||
@@ -71,13 +76,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 "פעיל"
|
||||
|
||||
@@ -85,14 +93,20 @@ msgstr "פעיל"
|
||||
msgid "Active Alerts"
|
||||
msgstr "התראות פעילות"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Active state"
|
||||
msgstr "מצב פעיל"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: 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 <0>System</0>"
|
||||
msgstr "הוסף <0>מערכת</0>"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add New System"
|
||||
msgstr "הוסף מערכת חדשה"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add system"
|
||||
msgstr "הוסף מערכת"
|
||||
@@ -105,11 +119,19 @@ msgstr "הוסף URL"
|
||||
msgid "Adjust display options for charts."
|
||||
msgstr "התאם אפשרויות תצוגה עבור גרפים."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Adjust the width of the main layout"
|
||||
msgstr "התאם את רוחב הפריסה הראשית"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Admin"
|
||||
msgstr "מנהל"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "After"
|
||||
msgstr "אחרי"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "סוכן"
|
||||
@@ -134,6 +156,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"
|
||||
@@ -170,7 +193,7 @@ msgstr "צריכת חשמל ממוצעת של GPUs"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Average system-wide CPU utilization"
|
||||
msgstr "ניצול ממוצע של CPU ברמת המערכת"
|
||||
msgstr "ניצול ממוצע כלל-מערכתי של CPU"
|
||||
|
||||
#. placeholder {0}: gpu.n
|
||||
#: src/components/routes/system.tsx
|
||||
@@ -195,6 +218,18 @@ msgstr "רוחב פס"
|
||||
msgid "Battery"
|
||||
msgstr "סוללה"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Became active"
|
||||
msgstr "הפך לפעיל"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Became inactive"
|
||||
msgstr "הפך ללא פעיל"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Before"
|
||||
msgstr "לפני"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Beszel supports OpenID Connect and many OAuth2 authentication providers."
|
||||
msgstr "Beszel תומך ב-OpenID Connect ובספקי אימות רבים של OAuth2."
|
||||
@@ -212,20 +247,41 @@ msgstr "בינרי"
|
||||
msgid "Bits (Kbps, Mbps, Gbps)"
|
||||
msgstr "ביטים (Kbps, Mbps, Gbps)"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Boot state"
|
||||
msgstr "מצב אתחול"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bytes (KB/s, MB/s, GB/s)"
|
||||
msgstr "בייטים (KB/s, MB/s, GB/s)"
|
||||
msgstr "בתים (KB/s, MB/s, GB/s)"
|
||||
|
||||
#: src/components/charts/mem-chart.tsx
|
||||
msgid "Cache / Buffers"
|
||||
msgstr "מטמון / חוצצים"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Can reload"
|
||||
msgstr "יכול לטעון מחדש"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Can start"
|
||||
msgstr "יכול להתחיל"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
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 "ביטול"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Capabilities"
|
||||
msgstr "יכולות"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Capacity"
|
||||
msgstr "קיבולת"
|
||||
@@ -265,12 +321,18 @@ msgstr "בדוק את {email} לקישור איפוס."
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Check logs for more details."
|
||||
msgstr "בדוק יומנים לפרטים נוספים."
|
||||
msgstr "בדוק לוגים לפרטים נוספים"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
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 "לחץ על קונטיינר כדי לצפות במידע נוסף."
|
||||
@@ -294,13 +356,17 @@ msgstr "הוראות שורת פקודה"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Configure how you receive alert notifications."
|
||||
msgstr "הגדר כיצד אתה מקבל התראות."
|
||||
msgstr "הגדר כיצד לקבל התראות."
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Confirm password"
|
||||
msgstr "אשר סיסמה"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Conflicts"
|
||||
msgstr "התנגשויות"
|
||||
|
||||
#: src/components/active-alerts.tsx
|
||||
msgid "Connection is down"
|
||||
msgstr "החיבור נפל"
|
||||
@@ -361,16 +427,38 @@ msgid "Copy YAML"
|
||||
msgstr "העתק YAML"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "CPU"
|
||||
msgstr "CPU"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "CPU Cores"
|
||||
msgstr "ליבות CPU"
|
||||
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "CPU Peak"
|
||||
msgstr "שיא CPU"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "CPU time"
|
||||
msgstr "זמן CPU"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "CPU Time Breakdown"
|
||||
msgstr "פירוט זמן CPU"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/lib/alerts.ts
|
||||
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 "צור חשבון"
|
||||
@@ -402,15 +490,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 "מחק"
|
||||
@@ -419,6 +510,10 @@ msgstr "מחק"
|
||||
msgid "Delete fingerprint"
|
||||
msgstr "מחק טביעת אצבע"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Description"
|
||||
msgstr "תיאור"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
msgid "Detail"
|
||||
msgstr "פרט"
|
||||
@@ -464,9 +559,10 @@ msgstr "שימוש זיכרון של Docker"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Docker Network I/O"
|
||||
msgstr "רשת Docker I/O"
|
||||
msgstr "I/O של רשת Docker"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Documentation"
|
||||
msgstr "תיעוד"
|
||||
|
||||
@@ -484,17 +580,22 @@ msgstr "כבוי ({downSystemsLength})"
|
||||
|
||||
#: src/components/routes/system/network-sheet.tsx
|
||||
msgid "Download"
|
||||
msgstr "הורד"
|
||||
msgstr "הורדה"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
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
|
||||
@@ -510,6 +611,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 "הכנס כתובת אימייל לאיפוס סיסמה"
|
||||
@@ -526,7 +632,10 @@ msgstr "הכנס את הסיסמה החד-פעמית שלך."
|
||||
#: 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"
|
||||
msgstr "שגיאה"
|
||||
|
||||
@@ -535,11 +644,19 @@ msgstr "שגיאה"
|
||||
#. placeholder {2}: alert.min
|
||||
#: src/components/active-alerts.tsx
|
||||
msgid "Exceeds {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
|
||||
msgstr "עולה על {0}{1} ב-{2, plural, one {# דקה} other {# דקות}} האחרונות"
|
||||
msgstr "עלה על {0}{1} ב{2, plural, one {דקה האחרונה} other {-# הדקות האחרונות}}"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Exec main PID"
|
||||
msgstr "PID ראשי של Exec"
|
||||
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
|
||||
msgstr "מערכות קיימות שלא מוגדרות ב-<0>config.yml</0> יימחקו. אנא בצע גיבויים קבועים."
|
||||
msgstr "מערכות קיימות שלא מוגדרות ב-<0>config.yml</0> יימחקו. אנא בצע גיבויים באופן קבוע."
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Exited active"
|
||||
msgstr "יצא פעיל"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
@@ -557,6 +674,10 @@ msgstr "ייצא את תצורת המערכות הנוכחית שלך."
|
||||
msgid "Fahrenheit (°F)"
|
||||
msgstr "פרנהייט (°F)"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Failed"
|
||||
msgstr "נכשל"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Failed Attributes:"
|
||||
msgstr "מאפיינים שנכשלו:"
|
||||
@@ -567,6 +688,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 "שמירת הגדרות נכשלה"
|
||||
|
||||
@@ -578,10 +700,16 @@ msgstr "שליחת התראת בדיקה נכשלה"
|
||||
msgid "Failed to update alert"
|
||||
msgstr "עדכון התראה נכשל"
|
||||
|
||||
#. placeholder {0}: statusTotals[ServiceStatus.Failed]
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Failed: {0}"
|
||||
msgstr "נכשל: {0}"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Filter..."
|
||||
msgstr "סנן..."
|
||||
@@ -619,6 +747,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"
|
||||
@@ -627,6 +759,10 @@ msgstr "מנועי GPU"
|
||||
msgid "GPU Power Draw"
|
||||
msgstr "צריכת חשמל GPU"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "GPU Usage"
|
||||
msgstr "שימוש GPU"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Grid"
|
||||
msgstr "רשת"
|
||||
@@ -659,6 +795,10 @@ 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 "כתובת אימייל לא תקינה."
|
||||
@@ -676,6 +816,19 @@ msgstr "שפה"
|
||||
msgid "Layout"
|
||||
msgstr "פריסה"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Layout width"
|
||||
msgstr "רוחב פריסה"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Lifecycle"
|
||||
msgstr "מחזור חיים"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "limit"
|
||||
msgstr "גבול"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Load Average"
|
||||
msgstr "ממוצע עומס"
|
||||
@@ -697,6 +850,14 @@ msgstr "ממוצע עומס 5ד"
|
||||
msgid "Load Avg"
|
||||
msgstr "ממוצע עומס"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Load state"
|
||||
msgstr "מצב עומס"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Loading..."
|
||||
msgstr "טוען..."
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Log Out"
|
||||
msgstr "התנתק"
|
||||
@@ -720,6 +881,10 @@ msgstr "יומנים"
|
||||
msgid "Looking instead for where to create alerts? Click the bell <0/> icons in the systems table."
|
||||
msgstr "מחפש איפה ליצור התראות? לחץ על סמלי הפעמון <0/> בטבלת המערכות."
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Main PID"
|
||||
msgstr "PID ראשי"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Manage display and notification preferences."
|
||||
msgstr "נהל העדפות תצוגה והתראות."
|
||||
@@ -735,10 +900,21 @@ msgid "Max 1 min"
|
||||
msgstr "מקס 1 דק'"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Memory"
|
||||
msgstr "זיכרון"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Memory limit"
|
||||
msgstr "גבול זיכרון"
|
||||
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Memory Peak"
|
||||
msgstr "שיא זיכרון"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Memory Usage"
|
||||
@@ -755,6 +931,8 @@ msgstr "דגם"
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Name"
|
||||
msgstr "שם"
|
||||
|
||||
@@ -779,7 +957,14 @@ msgstr "תעבורת רשת של ממשקים ציבוריים"
|
||||
msgid "Network unit"
|
||||
msgstr "יחידת רשת"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "No"
|
||||
msgstr "לא"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "No results found."
|
||||
msgstr "לא נמצאו תוצאות."
|
||||
|
||||
@@ -788,6 +973,7 @@ msgstr "לא נמצאו תוצאות."
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "No results."
|
||||
msgstr "אין תוצאות."
|
||||
|
||||
@@ -814,12 +1000,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 "פתח תפריט"
|
||||
@@ -828,10 +1021,15 @@ msgstr "פתח תפריט"
|
||||
msgid "Or continue with"
|
||||
msgstr "או המשך עם"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Other"
|
||||
msgstr "אחר"
|
||||
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "Overwrite existing alerts"
|
||||
msgstr "דרוס התראות קיימות"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Page"
|
||||
@@ -864,6 +1062,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 "השהה"
|
||||
@@ -876,6 +1078,15 @@ msgstr "מושהה"
|
||||
msgid "Paused ({pausedSystemsLength})"
|
||||
msgstr "מושהה ({pausedSystemsLength})"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Per-core average utilization"
|
||||
msgstr "ניצול ממוצע לליבה"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Percentage of time spent in each state"
|
||||
msgstr "אחוז הזמן המוקדש לכל מצב"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
msgstr "אנא <0>הגדר שרת SMTP</0> כדי להבטיח שהתראות יישלחו."
|
||||
@@ -927,11 +1138,19 @@ msgstr "ניצול מדויק בזמן הרשום"
|
||||
msgid "Preferred Language"
|
||||
msgstr "שפה מועדפת"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Process started"
|
||||
msgstr "תהליך התחיל"
|
||||
|
||||
#. Use 'Key' if your language requires many more characters
|
||||
#: src/components/add-system.tsx
|
||||
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
|
||||
@@ -944,9 +1163,14 @@ 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 "רענן"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Relationships"
|
||||
msgstr "קשרים"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Request a one-time password"
|
||||
msgstr "בקש סיסמה חד-פעמית"
|
||||
@@ -955,6 +1179,14 @@ msgstr "בקש סיסמה חד-פעמית"
|
||||
msgid "Request OTP"
|
||||
msgstr "בקש OTP"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Required by"
|
||||
msgstr "נדרש על ידי"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Requires"
|
||||
msgstr "דורש"
|
||||
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
msgid "Reset Password"
|
||||
msgstr "איפוס סיסמה"
|
||||
@@ -965,10 +1197,19 @@ msgstr "איפוס סיסמה"
|
||||
msgid "Resolved"
|
||||
msgstr "נפתר"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Restarts"
|
||||
msgstr "הפעלות מחדש"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Resume"
|
||||
msgstr "המשך"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgctxt "Root disk label"
|
||||
msgid "Root"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Rotate token"
|
||||
msgstr "סובב token"
|
||||
@@ -977,6 +1218,10 @@ msgstr "סובב token"
|
||||
msgid "Rows per page"
|
||||
msgstr "שורות לעמוד"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Runtime Metrics"
|
||||
msgstr "מדדי זמן ריצה"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "S.M.A.R.T. Details"
|
||||
msgstr "פרטי S.M.A.R.T."
|
||||
@@ -998,6 +1243,18 @@ msgstr "שמור הגדרות"
|
||||
msgid "Save system"
|
||||
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 "חיפוש"
|
||||
@@ -1010,6 +1267,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 "נשלח"
|
||||
@@ -1018,6 +1279,14 @@ msgstr "נשלח"
|
||||
msgid "Serial Number"
|
||||
msgstr "מספר סידורי"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Service Details"
|
||||
msgstr "פרטי שירות"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Services"
|
||||
msgstr "שירותים"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "הגדר סף אחוזים עבור צבעי מד."
|
||||
@@ -1045,18 +1314,30 @@ 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 "מצב"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Status"
|
||||
msgstr "סטטוס"
|
||||
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Sub State"
|
||||
msgstr "מצב משני"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Swap space used by the system"
|
||||
msgstr "שטח swap בשימוש על ידי המערכת"
|
||||
@@ -1065,9 +1346,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"
|
||||
@@ -1077,6 +1364,10 @@ msgstr "מערכת"
|
||||
msgid "System load averages over time"
|
||||
msgstr "ממוצעי עומס מערכת לאורך זמן"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Systemd Services"
|
||||
msgstr "שירותי Systemd"
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Systems"
|
||||
msgstr "מערכות"
|
||||
@@ -1089,6 +1380,10 @@ msgstr "מערכות יכולות להיות מנוהלות בקובץ <0>config
|
||||
msgid "Table"
|
||||
msgstr "טבלה"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Tasks"
|
||||
msgstr "משימות"
|
||||
|
||||
#. Temperature label in systems table
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
@@ -1172,6 +1467,11 @@ msgstr "Tokens מאפשרים לסוכנים להתחבר ולהירשם. טבי
|
||||
msgid "Tokens and fingerprints are used to authenticate WebSocket connections to the hub."
|
||||
msgstr "Tokens וטביעות אצבע משמשים לאימות חיבורי WebSocket ל-hub."
|
||||
|
||||
#: src/components/ui/chart.tsx
|
||||
#: src/components/ui/chart.tsx
|
||||
msgid "Total"
|
||||
msgstr "כולל"
|
||||
|
||||
#: src/components/routes/system/network-sheet.tsx
|
||||
msgid "Total data received for each interface"
|
||||
msgstr "סך נתונים שהתקבלו עבור כל ממשק"
|
||||
@@ -1180,6 +1480,19 @@ msgstr "סך נתונים שהתקבלו עבור כל ממשק"
|
||||
msgid "Total data sent for each interface"
|
||||
msgstr "סך נתונים שנשלחו עבור כל ממשק"
|
||||
|
||||
#. placeholder {0}: data.length
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Total: {0}"
|
||||
msgstr "סה\"כ: {0}"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Triggered by"
|
||||
msgstr "הופעל על ידי"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Triggers"
|
||||
msgstr "מפעילים"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when 1 minute load average exceeds a threshold"
|
||||
msgstr "מופעל כאשר ממוצע העומס לדקה אחת עולה על סף"
|
||||
@@ -1204,6 +1517,10 @@ msgstr "מופעל כאשר השילוב של למעלה/למטה עולה על
|
||||
msgid "Triggers when CPU usage exceeds a threshold"
|
||||
msgstr "מופעל כאשר שימוש CPU עולה על סף"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when GPU usage exceeds a threshold"
|
||||
msgstr "מופעל כאשר שימוש GPU עולה על סף"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when memory usage exceeds a threshold"
|
||||
msgstr "מופעל כאשר שימוש בזיכרון עולה על סף"
|
||||
@@ -1216,10 +1533,16 @@ 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 "קובץ יחידה"
|
||||
|
||||
#. Temperature / network units
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Unit preferences"
|
||||
@@ -1235,6 +1558,11 @@ msgstr "token אוניברסלי"
|
||||
msgid "Unknown"
|
||||
msgstr "לא ידוע"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Unlimited"
|
||||
msgstr "ללא הגבלה"
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
@@ -1245,13 +1573,23 @@ 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 "עודכן"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Updated every 10 minutes."
|
||||
msgstr "מתעדכן כל 10 דקות."
|
||||
|
||||
#: src/components/routes/system/network-sheet.tsx
|
||||
msgid "Upload"
|
||||
msgstr "העלה"
|
||||
msgstr "העלאה"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Uptime"
|
||||
@@ -1261,6 +1599,7 @@ msgstr "זמן פעילות"
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Usage"
|
||||
msgstr "שימוש"
|
||||
|
||||
@@ -1286,9 +1625,10 @@ msgstr "ערך"
|
||||
msgid "View"
|
||||
msgstr "צפה"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/components/routes/system/network-sheet.tsx
|
||||
msgid "View more"
|
||||
msgstr "צפה עוד"
|
||||
msgstr "צפה בעוד"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "View your 200 most recent alerts."
|
||||
@@ -1306,6 +1646,10 @@ msgstr "ממתין לרשומות מספיקות לתצוגה"
|
||||
msgid "Want to help improve our translations? Check <0>Crowdin</0> for details."
|
||||
msgstr "רוצה לעזור לשפר את התרגומים שלנו? בדוק <0>Crowdin</0> לפרטים."
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Wants"
|
||||
msgstr "רוצה"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Warning (%)"
|
||||
msgstr "אזהרה (%)"
|
||||
@@ -1342,7 +1686,12 @@ msgstr "תצורת YAML"
|
||||
msgid "YAML Configuration"
|
||||
msgstr "תצורת YAML"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Yes"
|
||||
msgstr "כן"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Your user settings have been updated."
|
||||
msgstr "הגדרות המשתמש שלך עודכנו."
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: hr\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-10-20 21:37\n"
|
||||
"PO-Revision-Date: 2025-11-14 22:51\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"
|
||||
@@ -76,13 +76,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"
|
||||
|
||||
@@ -90,14 +93,20 @@ msgstr "Aktivan"
|
||||
msgid "Active Alerts"
|
||||
msgstr "Aktivna upozorenja"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Active state"
|
||||
msgstr "Aktivno stanje"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Add {foo}"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add <0>System</0>"
|
||||
msgstr "Dodaj <0>Sistem</0>"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add New System"
|
||||
msgstr "Dodaj Novi Sistem"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add system"
|
||||
msgstr "Dodaj sistem"
|
||||
@@ -110,11 +119,19 @@ msgstr "Dodaj URL"
|
||||
msgid "Adjust display options for charts."
|
||||
msgstr "Podesite opcije prikaza za grafikone."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Adjust the width of the main layout"
|
||||
msgstr "Prilagodite širinu glavnog rasporeda"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Admin"
|
||||
msgstr "Admin"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "After"
|
||||
msgstr "Nakon"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Agent"
|
||||
@@ -139,6 +156,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"
|
||||
@@ -200,6 +218,18 @@ msgstr "Propusnost"
|
||||
msgid "Battery"
|
||||
msgstr "Baterija"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Became active"
|
||||
msgstr "Postalo aktivno"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Became inactive"
|
||||
msgstr "Postalo neaktivno"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Before"
|
||||
msgstr "Prije"
|
||||
|
||||
#: 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."
|
||||
@@ -217,6 +247,10 @@ msgstr "Binarni"
|
||||
msgid "Bits (Kbps, Mbps, Gbps)"
|
||||
msgstr "Bitovi (Kbps, Mbps, Gbps)"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Boot state"
|
||||
msgstr "Stanje pokretanja"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bytes (KB/s, MB/s, GB/s)"
|
||||
@@ -226,11 +260,28 @@ msgstr "Bajtovi (KB/s, MB/s, GB/s)"
|
||||
msgid "Cache / Buffers"
|
||||
msgstr "Predmemorija / Međuspremnici"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Can reload"
|
||||
msgstr "Može se ponovno učitati"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Can start"
|
||||
msgstr "Može se pokrenuti"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
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 ""
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Capacity"
|
||||
msgstr "Kapacitet"
|
||||
@@ -276,6 +327,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."
|
||||
@@ -306,6 +363,10 @@ msgstr "Konfigurirajte način primanja obavijesti upozorenja."
|
||||
msgid "Confirm password"
|
||||
msgstr "Potvrdite lozinku"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Conflicts"
|
||||
msgstr "Sukobi"
|
||||
|
||||
#: src/components/active-alerts.tsx
|
||||
msgid "Connection is down"
|
||||
msgstr "Veza je pala"
|
||||
@@ -366,16 +427,38 @@ msgid "Copy YAML"
|
||||
msgstr "Kopiraj YAML"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "CPU"
|
||||
msgstr "Procesor"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "CPU Cores"
|
||||
msgstr "CPU jezgre"
|
||||
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "CPU Peak"
|
||||
msgstr "CPU vrhunac"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "CPU time"
|
||||
msgstr "CPU vrijeme"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "CPU Time Breakdown"
|
||||
msgstr "Raspodjela CPU vremena"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/lib/alerts.ts
|
||||
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"
|
||||
@@ -407,15 +490,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"
|
||||
@@ -424,6 +510,10 @@ msgstr "Izbriši"
|
||||
msgid "Delete fingerprint"
|
||||
msgstr "Izbriši otisak"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Description"
|
||||
msgstr "Opis"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
msgid "Detail"
|
||||
msgstr "Detalj"
|
||||
@@ -472,6 +562,7 @@ msgid "Docker Network I/O"
|
||||
msgstr "Docker Mrežni I/O"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Documentation"
|
||||
msgstr "Dokumentacija"
|
||||
|
||||
@@ -495,11 +586,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 ""
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
#: src/components/login/otp-forms.tsx
|
||||
@@ -515,6 +611,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"
|
||||
@@ -531,7 +632,10 @@ msgstr "Unesite Vašu jednokratnu lozinku."
|
||||
#: 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"
|
||||
msgstr "Greška"
|
||||
|
||||
@@ -542,10 +646,18 @@ msgstr "Greška"
|
||||
msgid "Exceeds {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
|
||||
msgstr "Premašuje {0}{1} u posljednjih {2, plural, one {# minuta} other {# minute}}"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Exec main PID"
|
||||
msgstr "Glavni PID izvršavanja"
|
||||
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
|
||||
msgstr "Postojeći sistemi koji nisu definirani u <0>config.yml</0> će biti izbrisani. Molimo Vas napravite redovite sigurnosne kopije."
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Exited active"
|
||||
msgstr "Izašlo aktivno"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr "Izvezi"
|
||||
@@ -562,6 +674,10 @@ msgstr "Izvoz trenutne sistemske konfiguracije."
|
||||
msgid "Fahrenheit (°F)"
|
||||
msgstr "Farenhajt (°F)"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Failed"
|
||||
msgstr "Neuspješno"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Failed Attributes:"
|
||||
msgstr "Neuspjeli atributi:"
|
||||
@@ -572,6 +688,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"
|
||||
|
||||
@@ -583,10 +700,16 @@ msgstr "Neuspješno slanje testne notifikacije"
|
||||
msgid "Failed to update alert"
|
||||
msgstr "Ažuriranje upozorenja nije uspjelo"
|
||||
|
||||
#. placeholder {0}: statusTotals[ServiceStatus.Failed]
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Failed: {0}"
|
||||
msgstr "Neuspjelo: {0}"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Filter..."
|
||||
msgstr "Filtriraj..."
|
||||
@@ -597,7 +720,7 @@ msgstr "Otisak prsta"
|
||||
|
||||
#: 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}}"
|
||||
@@ -624,6 +747,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"
|
||||
@@ -632,6 +759,10 @@ msgstr "GPU motori"
|
||||
msgid "GPU Power Draw"
|
||||
msgstr "Energetska potrošnja grafičkog procesora"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "GPU Usage"
|
||||
msgstr "Iskorištenost GPU-a"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Grid"
|
||||
msgstr "Mreža"
|
||||
@@ -664,6 +795,10 @@ 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."
|
||||
@@ -681,6 +816,19 @@ msgstr "Jezik"
|
||||
msgid "Layout"
|
||||
msgstr "Izgled"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Layout width"
|
||||
msgstr "Širina rasporeda"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Lifecycle"
|
||||
msgstr "Životni ciklus"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "limit"
|
||||
msgstr "ograničenje"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Load Average"
|
||||
msgstr "Prosječno Opterećenje"
|
||||
@@ -702,6 +850,14 @@ msgstr "Prosječno Opterećenje 5m"
|
||||
msgid "Load Avg"
|
||||
msgstr "Prosječno opterećenje"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Load state"
|
||||
msgstr "Stanje učitavanja"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Loading..."
|
||||
msgstr "Učitavanje..."
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Log Out"
|
||||
msgstr "Odjava"
|
||||
@@ -725,6 +881,10 @@ msgstr "Logovi"
|
||||
msgid "Looking instead for where to create alerts? Click the bell <0/> icons in the systems table."
|
||||
msgstr "Tražite gdje stvoriti upozorenja? Kliknite ikonu zvona <0/> u tablici sustava."
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Main PID"
|
||||
msgstr "Glavni PID"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Manage display and notification preferences."
|
||||
msgstr "Upravljajte postavkama prikaza i obavijesti."
|
||||
@@ -740,10 +900,21 @@ msgid "Max 1 min"
|
||||
msgstr "Maksimalno 1 minuta"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Memory"
|
||||
msgstr "Memorija"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Memory limit"
|
||||
msgstr "Ograničenje memorije"
|
||||
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Memory Peak"
|
||||
msgstr "Vrhunac memorije"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Memory Usage"
|
||||
@@ -755,11 +926,13 @@ msgstr "Upotreba memorije Docker spremnika"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Model"
|
||||
msgstr "Model"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Name"
|
||||
msgstr "Ime"
|
||||
|
||||
@@ -784,7 +957,14 @@ msgstr "Mrežni promet javnih sučelja"
|
||||
msgid "Network unit"
|
||||
msgstr "Mjerna jedinica za mrežu"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "No"
|
||||
msgstr "Ne"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "No results found."
|
||||
msgstr "Nema rezultata."
|
||||
|
||||
@@ -793,6 +973,7 @@ msgstr "Nema rezultata."
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "No results."
|
||||
msgstr "Nema rezultata."
|
||||
|
||||
@@ -819,12 +1000,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"
|
||||
@@ -833,10 +1021,15 @@ msgstr "Otvori menu"
|
||||
msgid "Or continue with"
|
||||
msgstr "Ili nastavi sa"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Other"
|
||||
msgstr "Ostalo"
|
||||
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
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"
|
||||
@@ -869,6 +1062,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"
|
||||
@@ -881,6 +1078,15 @@ msgstr "Pauzirano"
|
||||
msgid "Paused ({pausedSystemsLength})"
|
||||
msgstr "Pauzirano ({pausedSystemsLength})"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Per-core average utilization"
|
||||
msgstr "Prosječna iskorištenost po jezgri"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Percentage of time spent in each state"
|
||||
msgstr "Postotak vremena provedenog u svakom stanju"
|
||||
|
||||
#: 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."
|
||||
@@ -932,11 +1138,19 @@ msgstr "Precizno iskorištenje u zabilježenom vremenu"
|
||||
msgid "Preferred Language"
|
||||
msgstr "Preferirani jezik"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Process started"
|
||||
msgstr "Proces pokrenut"
|
||||
|
||||
#. Use 'Key' if your language requires many more characters
|
||||
#: src/components/add-system.tsx
|
||||
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
|
||||
@@ -949,9 +1163,14 @@ 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"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Relationships"
|
||||
msgstr "Odnosi"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Request a one-time password"
|
||||
msgstr "Zatraži jednokratnu lozinku"
|
||||
@@ -960,6 +1179,14 @@ msgstr "Zatraži jednokratnu lozinku"
|
||||
msgid "Request OTP"
|
||||
msgstr "Zatraži OTP"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Required by"
|
||||
msgstr "Zahtijeva"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Requires"
|
||||
msgstr "Zahtijeva"
|
||||
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
msgid "Reset Password"
|
||||
msgstr "Resetiraj Lozinku"
|
||||
@@ -970,10 +1197,19 @@ msgstr "Resetiraj Lozinku"
|
||||
msgid "Resolved"
|
||||
msgstr "Razrješeno"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Restarts"
|
||||
msgstr "Ponovna pokretanja"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Nastavi"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgctxt "Root disk label"
|
||||
msgid "Root"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Rotate token"
|
||||
msgstr "Promijeni token"
|
||||
@@ -982,6 +1218,10 @@ msgstr "Promijeni token"
|
||||
msgid "Rows per page"
|
||||
msgstr "Redovi po stranici"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Runtime Metrics"
|
||||
msgstr "Metrike izvršavanja"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "S.M.A.R.T. Details"
|
||||
msgstr "S.M.A.R.T. Detalji"
|
||||
@@ -1003,6 +1243,18 @@ msgstr "Spremi Postavke"
|
||||
msgid "Save system"
|
||||
msgstr "Spremi sustav"
|
||||
|
||||
#: 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"
|
||||
@@ -1015,6 +1267,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 ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "Poslano"
|
||||
@@ -1023,6 +1279,14 @@ msgstr "Poslano"
|
||||
msgid "Serial Number"
|
||||
msgstr "Serijski broj"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Service Details"
|
||||
msgstr "Detalji usluge"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Services"
|
||||
msgstr "Usluge"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "Postavite pragove postotka za boje mjerača."
|
||||
@@ -1050,18 +1314,30 @@ 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"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Status"
|
||||
msgstr "Status"
|
||||
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Sub State"
|
||||
msgstr "Podstanje"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Swap space used by the system"
|
||||
msgstr "Swap prostor uzet od strane sistema"
|
||||
@@ -1070,9 +1346,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"
|
||||
@@ -1082,6 +1364,10 @@ msgstr "Sistem"
|
||||
msgid "System load averages over time"
|
||||
msgstr "Prosječno opterećenje sustava kroz vrijeme"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Systemd Services"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Systems"
|
||||
msgstr "Sistemi"
|
||||
@@ -1094,6 +1380,10 @@ msgstr "Sistemima se može upravljati u <0>config.yml</0> datoteci unutar data d
|
||||
msgid "Table"
|
||||
msgstr "Tablica"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Tasks"
|
||||
msgstr "Zadaci"
|
||||
|
||||
#. Temperature label in systems table
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
@@ -1177,6 +1467,11 @@ msgstr "Tokeni dopuštaju agentima prijavu i registraciju. Otisci su stabilni id
|
||||
msgid "Tokens and fingerprints are used to authenticate WebSocket connections to the hub."
|
||||
msgstr "Tokeni se uz otiske koriste za autentifikaciju WebSocket veza prema središnjoj kontroli."
|
||||
|
||||
#: src/components/ui/chart.tsx
|
||||
#: src/components/ui/chart.tsx
|
||||
msgid "Total"
|
||||
msgstr "Ukupno"
|
||||
|
||||
#: src/components/routes/system/network-sheet.tsx
|
||||
msgid "Total data received for each interface"
|
||||
msgstr "Ukupni podaci primljeni za svako sučelje"
|
||||
@@ -1185,6 +1480,19 @@ msgstr "Ukupni podaci primljeni za svako sučelje"
|
||||
msgid "Total data sent for each interface"
|
||||
msgstr "Ukupni podaci poslani za svako sučelje"
|
||||
|
||||
#. placeholder {0}: data.length
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Total: {0}"
|
||||
msgstr "Ukupno: {0}"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Triggered by"
|
||||
msgstr "Pokrenuto od"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Triggers"
|
||||
msgstr "Okidači"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when 1 minute load average exceeds a threshold"
|
||||
msgstr "Pokreće se kada prosječna opterećenost sustava unutar 1 minute prijeđe prag"
|
||||
@@ -1209,6 +1517,10 @@ msgstr "Pokreće se kada kombinacija gore/dolje premaši prag"
|
||||
msgid "Triggers when CPU usage exceeds a threshold"
|
||||
msgstr "Pokreće se kada iskorištenost procesora premaši prag"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when GPU usage exceeds a threshold"
|
||||
msgstr "Pokreće se kada iskorištenost GPU-a premaši prag"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when memory usage exceeds a threshold"
|
||||
msgstr "Pokreće se kada iskorištenost memorije premaši prag"
|
||||
@@ -1221,10 +1533,16 @@ 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"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Unit file"
|
||||
msgstr "Datoteka jedinice"
|
||||
|
||||
#. Temperature / network units
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Unit preferences"
|
||||
@@ -1240,6 +1558,11 @@ msgstr "Sveopći token"
|
||||
msgid "Unknown"
|
||||
msgstr "Nepoznata"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Unlimited"
|
||||
msgstr "Neograničeno"
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
@@ -1250,10 +1573,20 @@ 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"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Updated every 10 minutes."
|
||||
msgstr "Ažurirano svakih 10 minuta."
|
||||
|
||||
#: src/components/routes/system/network-sheet.tsx
|
||||
msgid "Upload"
|
||||
msgstr "Otpremi"
|
||||
@@ -1266,6 +1599,7 @@ msgstr "Vrijeme rada"
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Usage"
|
||||
msgstr "Iskorištenost"
|
||||
|
||||
@@ -1291,6 +1625,7 @@ msgstr "Vrijednost"
|
||||
msgid "View"
|
||||
msgstr "Prikaz"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/components/routes/system/network-sheet.tsx
|
||||
msgid "View more"
|
||||
msgstr "Prikaži više"
|
||||
@@ -1311,6 +1646,10 @@ msgstr "Čeka se na više podataka prije prikaza"
|
||||
msgid "Want to help improve our translations? Check <0>Crowdin</0> for details."
|
||||
msgstr "Želite li nam pomoći da naše prijevode učinimo još boljim? Posjetite <0>Crowdin</0> za više detalja."
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Wants"
|
||||
msgstr "Želi"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Warning (%)"
|
||||
msgstr "Upozorenje (%)"
|
||||
@@ -1347,6 +1686,12 @@ msgstr "YAML konfiguracija"
|
||||
msgid "YAML Configuration"
|
||||
msgstr "YAML Konfiguracija"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Yes"
|
||||
msgstr "Da"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Your user settings have been updated."
|
||||
msgstr "Vaše korisničke postavke su ažurirane."
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: hu\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-10-20 21:37\n"
|
||||
"PO-Revision-Date: 2025-11-14 22:51\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Hungarian\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -76,13 +76,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"
|
||||
|
||||
@@ -90,14 +93,20 @@ msgstr "Aktív"
|
||||
msgid "Active Alerts"
|
||||
msgstr "Aktív riasztások"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Active state"
|
||||
msgstr "Aktív állapot"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Add {foo}"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add <0>System</0>"
|
||||
msgstr "Hozzáadás <0>System</0>"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add New System"
|
||||
msgstr "Új rendszer hozzáadása"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add system"
|
||||
msgstr "Rendszer hozzáadása"
|
||||
@@ -110,11 +119,19 @@ msgstr "URL hozzáadása"
|
||||
msgid "Adjust display options for charts."
|
||||
msgstr "Állítsa be a diagram megjelenítését."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Adjust the width of the main layout"
|
||||
msgstr "A fő elrendezés szélességének beállítása"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Admin"
|
||||
msgstr "Adminisztráció"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "After"
|
||||
msgstr "Utána"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Ügynök"
|
||||
@@ -139,6 +156,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"
|
||||
@@ -200,6 +218,18 @@ msgstr "Sávszélesség"
|
||||
msgid "Battery"
|
||||
msgstr "Akkumulátor"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Became active"
|
||||
msgstr "Aktívvá vált"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Became inactive"
|
||||
msgstr "Inaktívvá vált"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Before"
|
||||
msgstr "Előtte"
|
||||
|
||||
#: 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."
|
||||
@@ -217,6 +247,10 @@ msgstr "Bináris"
|
||||
msgid "Bits (Kbps, Mbps, Gbps)"
|
||||
msgstr "Bitek (Kbps, Mbps, Gbps)"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Boot state"
|
||||
msgstr "Indítási állapot"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bytes (KB/s, MB/s, GB/s)"
|
||||
@@ -226,11 +260,28 @@ msgstr "Byte-ok (KB/s, MB/s, GB/s)"
|
||||
msgid "Cache / Buffers"
|
||||
msgstr "Gyorsítótár / Pufferelések"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Can reload"
|
||||
msgstr "Újratölthető"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Can start"
|
||||
msgstr "Indítható"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
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"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Capabilities"
|
||||
msgstr "Képességek"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Capacity"
|
||||
msgstr "Kapacitás"
|
||||
@@ -276,6 +327,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."
|
||||
@@ -306,6 +363,10 @@ msgstr "Konfiguráld, hogyan kapod az értesítéseket."
|
||||
msgid "Confirm password"
|
||||
msgstr "Jelszó megerősítése"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Conflicts"
|
||||
msgstr "Konfliktusok"
|
||||
|
||||
#: src/components/active-alerts.tsx
|
||||
msgid "Connection is down"
|
||||
msgstr "Kapcsolat megszakadt"
|
||||
@@ -355,27 +416,49 @@ msgstr "Szöveg másolása"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Copy the installation command for the agent below, or register agents automatically with a <0>universal token</0>."
|
||||
msgstr ""
|
||||
msgstr "Másold az alábbi ügynök telepítési parancsát, vagy regisztráld az ügynököket automatikusan egy <0>univerzális tokennel</0>."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Copy the<0>docker-compose.yml</0> content for the agent below, or register agents automatically with a <1>universal token</1>."
|
||||
msgstr ""
|
||||
msgstr "Másold az alábbi ügynök <0>docker-compose.yml</0> tartalmát, vagy regisztráld az ügynököket automatikusan egy <1>univerzális tokennel</1>."
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Copy YAML"
|
||||
msgstr "YAML másolása"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "CPU"
|
||||
msgstr "CPU"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "CPU Cores"
|
||||
msgstr "CPU magok"
|
||||
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "CPU Peak"
|
||||
msgstr "CPU csúcs"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "CPU time"
|
||||
msgstr "CPU idő"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "CPU Time Breakdown"
|
||||
msgstr "CPU idő felbontása"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/lib/alerts.ts
|
||||
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"
|
||||
@@ -407,15 +490,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"
|
||||
@@ -424,6 +510,10 @@ msgstr "Törlés"
|
||||
msgid "Delete fingerprint"
|
||||
msgstr "Ujjlenyomat törlése"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Description"
|
||||
msgstr "Leírás"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
msgid "Detail"
|
||||
msgstr "Részlet"
|
||||
@@ -472,6 +562,7 @@ msgid "Docker Network I/O"
|
||||
msgstr "Docker hálózat I/O"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Documentation"
|
||||
msgstr "Dokumentáció"
|
||||
|
||||
@@ -495,11 +586,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 ""
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
#: src/components/login/otp-forms.tsx
|
||||
@@ -515,6 +611,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"
|
||||
@@ -531,7 +632,10 @@ msgstr "Adja meg az egyszeri jelszavát."
|
||||
#: 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"
|
||||
msgstr "Hiba"
|
||||
|
||||
@@ -542,10 +646,18 @@ msgstr "Hiba"
|
||||
msgid "Exceeds {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
|
||||
msgstr "Túllépi a {0}{1} értéket az elmúlt {2, plural, one {# percben} other {# percben}}"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Exec main PID"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
|
||||
msgstr "A <0>config.yml</0> fájlban nem definiált meglévő rendszerek törlésre kerülnek. Kérjük, készítsen rendszeres biztonsági mentéseket."
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Exited active"
|
||||
msgstr "Aktívként kilépett"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr "Exportálás"
|
||||
@@ -562,6 +674,10 @@ msgstr "Exportálja a jelenlegi rendszerkonfigurációt."
|
||||
msgid "Fahrenheit (°F)"
|
||||
msgstr "Fahrenheit (°F)"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Failed"
|
||||
msgstr "Sikertelen"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Failed Attributes:"
|
||||
msgstr "Sikertelen attribútumok:"
|
||||
@@ -572,6 +688,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"
|
||||
|
||||
@@ -583,10 +700,16 @@ msgstr "Teszt értesítés elküldése sikertelen"
|
||||
msgid "Failed to update alert"
|
||||
msgstr "Nem sikerült frissíteni a riasztást"
|
||||
|
||||
#. placeholder {0}: statusTotals[ServiceStatus.Failed]
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Failed: {0}"
|
||||
msgstr "Sikertelen: {0}"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Filter..."
|
||||
msgstr "Szűrő..."
|
||||
@@ -597,7 +720,7 @@ msgstr "Ujjlenyomat"
|
||||
|
||||
#: 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}}"
|
||||
@@ -624,6 +747,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"
|
||||
@@ -632,6 +759,10 @@ msgstr "GPU-k"
|
||||
msgid "GPU Power Draw"
|
||||
msgstr "GPU áramfelvétele"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "GPU Usage"
|
||||
msgstr "GPU használat"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Grid"
|
||||
msgstr "Rács"
|
||||
@@ -664,6 +795,10 @@ 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."
|
||||
@@ -681,6 +816,19 @@ msgstr "Nyelv"
|
||||
msgid "Layout"
|
||||
msgstr "Elrendezés"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Layout width"
|
||||
msgstr "Elrendezés szélessége"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Lifecycle"
|
||||
msgstr "Életciklus"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "limit"
|
||||
msgstr "korlát"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Load Average"
|
||||
msgstr "Terhelési átlag"
|
||||
@@ -702,6 +850,14 @@ msgstr "Terhelési átlag 5p"
|
||||
msgid "Load Avg"
|
||||
msgstr "Terhelési átlag"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Load state"
|
||||
msgstr "Betöltési állapot"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Loading..."
|
||||
msgstr "Betöltés..."
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Log Out"
|
||||
msgstr "Kijelentkezés"
|
||||
@@ -725,6 +881,10 @@ msgstr "Naplók"
|
||||
msgid "Looking instead for where to create alerts? Click the bell <0/> icons in the systems table."
|
||||
msgstr "Inkább azt keresi, hogy hol hozhat létre riasztásokat? Kattintson a csengő <0/> ikonokra a rendszerek táblázatában."
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Main PID"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Manage display and notification preferences."
|
||||
msgstr "A megjelenítési és értesítési beállítások kezelése."
|
||||
@@ -740,10 +900,21 @@ msgid "Max 1 min"
|
||||
msgstr "Maximum 1 perc"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Memory"
|
||||
msgstr "RAM"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Memory limit"
|
||||
msgstr "Memória korlát"
|
||||
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Memory Peak"
|
||||
msgstr "Memória csúcs"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Memory Usage"
|
||||
@@ -760,6 +931,8 @@ msgstr "Modell"
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Name"
|
||||
msgstr "Név"
|
||||
|
||||
@@ -784,7 +957,14 @@ msgstr "Nyilvános interfészek hálózati forgalma"
|
||||
msgid "Network unit"
|
||||
msgstr "Sávszélesség mértékegysége"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "No"
|
||||
msgstr "Nem"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "No results found."
|
||||
msgstr "Nincs találat."
|
||||
|
||||
@@ -793,6 +973,7 @@ msgstr "Nincs találat."
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "No results."
|
||||
msgstr "Nincs találat."
|
||||
|
||||
@@ -819,12 +1000,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"
|
||||
@@ -833,10 +1021,15 @@ msgstr "Menü megnyitása"
|
||||
msgid "Or continue with"
|
||||
msgstr "Vagy folytasd ezzel"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Other"
|
||||
msgstr "Egyéb"
|
||||
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
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"
|
||||
@@ -869,6 +1062,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"
|
||||
@@ -881,6 +1078,15 @@ msgstr "Szüneteltetve"
|
||||
msgid "Paused ({pausedSystemsLength})"
|
||||
msgstr "Szüneteltetve ({pausedSystemsLength})"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Per-core average utilization"
|
||||
msgstr "Átlagos kihasználtság magonként"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Percentage of time spent in each state"
|
||||
msgstr "Az idő százalékos aránya minden állapotban"
|
||||
|
||||
#: 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."
|
||||
@@ -932,11 +1138,19 @@ msgstr "Pontos kihasználás a rögzített időpontban"
|
||||
msgid "Preferred Language"
|
||||
msgstr "Preferált nyelv"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Process started"
|
||||
msgstr "Folyamat elindítva"
|
||||
|
||||
#. Use 'Key' if your language requires many more characters
|
||||
#: src/components/add-system.tsx
|
||||
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
|
||||
@@ -949,9 +1163,14 @@ 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"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Relationships"
|
||||
msgstr "Kapcsolatok"
|
||||
|
||||
#: src/components/login/login.tsx
|
||||
msgid "Request a one-time password"
|
||||
msgstr "Egyszeri jelszó kérése"
|
||||
@@ -960,6 +1179,14 @@ msgstr "Egyszeri jelszó kérése"
|
||||
msgid "Request OTP"
|
||||
msgstr "OTP kérése"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Required by"
|
||||
msgstr "Szükséges"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Requires"
|
||||
msgstr "Igényel"
|
||||
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
msgid "Reset Password"
|
||||
msgstr "Jelszó visszaállítása"
|
||||
@@ -970,10 +1197,19 @@ msgstr "Jelszó visszaállítása"
|
||||
msgid "Resolved"
|
||||
msgstr "Megoldva"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Restarts"
|
||||
msgstr "Újraindítások"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Folytatás"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgctxt "Root disk label"
|
||||
msgid "Root"
|
||||
msgstr "Gyökér"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Rotate token"
|
||||
msgstr "Tokenváltás"
|
||||
@@ -982,6 +1218,10 @@ msgstr "Tokenváltás"
|
||||
msgid "Rows per page"
|
||||
msgstr "Sorok száma oldalanként"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Runtime Metrics"
|
||||
msgstr "Futásidejű metrikák"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "S.M.A.R.T. Details"
|
||||
msgstr "S.M.A.R.T. Részletek"
|
||||
@@ -1003,6 +1243,18 @@ msgstr "Beállítások mentése"
|
||||
msgid "Save system"
|
||||
msgstr "Rendszer mentése"
|
||||
|
||||
#: 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"
|
||||
@@ -1015,6 +1267,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"
|
||||
@@ -1023,6 +1279,14 @@ msgstr "Elküldve"
|
||||
msgid "Serial Number"
|
||||
msgstr "Sorozatszám"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Service Details"
|
||||
msgstr "Szolgáltatás részletei"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Services"
|
||||
msgstr "Szolgáltatások"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "Százalékos küszöbértékek beállítása a mérőszínekhez."
|
||||
@@ -1050,18 +1314,30 @@ 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"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Status"
|
||||
msgstr "Állapot"
|
||||
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Sub State"
|
||||
msgstr "Részállapot"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Swap space used by the system"
|
||||
msgstr "Rendszer által használt swap terület"
|
||||
@@ -1070,9 +1346,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"
|
||||
@@ -1082,6 +1364,10 @@ msgstr "Rendszer"
|
||||
msgid "System load averages over time"
|
||||
msgstr "Rendszer terhelési átlaga"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Systemd Services"
|
||||
msgstr "Systemd szolgáltatások"
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Systems"
|
||||
msgstr "Rendszer"
|
||||
@@ -1094,6 +1380,10 @@ msgstr "A rendszereket egy <0>config.yml</0> fájlban lehet kezelni az adatköny
|
||||
msgid "Table"
|
||||
msgstr "Tábla"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Tasks"
|
||||
msgstr "Feladatok"
|
||||
|
||||
#. Temperature label in systems table
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
@@ -1171,11 +1461,16 @@ msgstr "Tokenek & Ujjlenyomatok"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Tokens allow agents to connect and register. Fingerprints are stable identifiers unique to each system, set on first connection."
|
||||
msgstr ""
|
||||
msgstr "A tokenek lehetővé teszik az ügynökök csatlakozását és regisztrációját. A fingeravtrykkok stabil azonosítók, amelyek minden rendszerre egyediek, és az első kapcsolódáskor kerülnek beállításra."
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Tokens and fingerprints are used to authenticate WebSocket connections to the hub."
|
||||
msgstr ""
|
||||
msgstr "A tokeneket és fingeravtrykkokat a hubhoz való WebSocket kapcsolatok hitelesítésére használják."
|
||||
|
||||
#: src/components/ui/chart.tsx
|
||||
#: src/components/ui/chart.tsx
|
||||
msgid "Total"
|
||||
msgstr "Összesen"
|
||||
|
||||
#: src/components/routes/system/network-sheet.tsx
|
||||
msgid "Total data received for each interface"
|
||||
@@ -1185,6 +1480,19 @@ msgstr "Összes fogadott adat minden interfészenként"
|
||||
msgid "Total data sent for each interface"
|
||||
msgstr "Összes elküldött adat minden interfészenként"
|
||||
|
||||
#. placeholder {0}: data.length
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Total: {0}"
|
||||
msgstr "Összesen: {0}"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Triggered by"
|
||||
msgstr "Kiváltva"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Triggers"
|
||||
msgstr "Kiváltók"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when 1 minute load average exceeds a threshold"
|
||||
msgstr "Riaszt, ha az 1 perces terhelési átlag túllép egy küszöbértéket"
|
||||
@@ -1209,6 +1517,10 @@ msgstr "Bekapcsol, ha bármelyik érzékelő túllép egy küszöbértéket"
|
||||
msgid "Triggers when CPU usage exceeds a threshold"
|
||||
msgstr "Bekapcsol, ha a CPU érzékelő túllép egy küszöbértéket"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when GPU usage exceeds a threshold"
|
||||
msgstr "Riaszt, ha a GPU használat túllép egy küszöbértéket"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when memory usage exceeds a threshold"
|
||||
msgstr "Bekapcsol, ha a Ram érzékelő túllép egy küszöbértéket"
|
||||
@@ -1221,10 +1533,16 @@ 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"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Unit file"
|
||||
msgstr "Egység fájl"
|
||||
|
||||
#. Temperature / network units
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Unit preferences"
|
||||
@@ -1240,6 +1558,11 @@ msgstr "Univerzális token"
|
||||
msgid "Unknown"
|
||||
msgstr "Ismeretlen"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Unlimited"
|
||||
msgstr "Korlátlan"
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
@@ -1250,10 +1573,20 @@ 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"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Updated every 10 minutes."
|
||||
msgstr "10 percenként frissítve."
|
||||
|
||||
#: src/components/routes/system/network-sheet.tsx
|
||||
msgid "Upload"
|
||||
msgstr "Feltöltés"
|
||||
@@ -1266,6 +1599,7 @@ msgstr "Üzemidő"
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Usage"
|
||||
msgstr "Használat"
|
||||
|
||||
@@ -1291,6 +1625,7 @@ msgstr "Érték"
|
||||
msgid "View"
|
||||
msgstr "Nézet"
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
#: src/components/routes/system/network-sheet.tsx
|
||||
msgid "View more"
|
||||
msgstr "Továbbiak megjelenítése"
|
||||
@@ -1311,6 +1646,10 @@ msgstr "Elegendő rekordra várva a megjelenítéshez"
|
||||
msgid "Want to help improve our translations? Check <0>Crowdin</0> for details."
|
||||
msgstr "Szeretne segíteni nekünk abban, hogy fordításaink még jobbak legyenek? További részletekért nézze meg a <0>Crowdin</0> honlapot."
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Wants"
|
||||
msgstr "Igényel"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Warning (%)"
|
||||
msgstr "Figyelmeztetés (%)"
|
||||
@@ -1325,7 +1664,7 @@ 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 ""
|
||||
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."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
@@ -1347,6 +1686,12 @@ msgstr "YAML konfiguráció"
|
||||
msgid "YAML Configuration"
|
||||
msgstr "YAML konfiguráció"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Yes"
|
||||
msgstr "Igen"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Your user settings have been updated."
|
||||
msgstr "A felhasználói beállítások frissítésre kerültek."
|
||||
|
||||
@@ -5,40 +5,37 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: @lingui/cli\n"
|
||||
"Language: ro\n"
|
||||
"Language: id\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-10-20 21:37\n"
|
||||
"PO-Revision-Date: 2025-10-29 21:56\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Romanian\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : (n==0 || (n%100>0 && n%100<20)) ? 1 : 2);\n"
|
||||
"Language-Team: Indonesian\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
"X-Crowdin-Project: beszel\n"
|
||||
"X-Crowdin-Project-ID: 733311\n"
|
||||
"X-Crowdin-Language: ro\n"
|
||||
"X-Crowdin-Language: id\n"
|
||||
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
|
||||
"X-Crowdin-File-ID: 32\n"
|
||||
|
||||
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "{0, plural, one {# day} other {# days}}"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: Math.trunc(system.info.u / 3600)
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "{0, plural, one {# hour} other {# hours}}"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: Math.trunc(system.info.u / 60)
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "{0, plural, one {# minute} few {# minutes} many {# minutes} other {# minutes}}"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: table.getFilteredSelectedRowModel().rows.length
|
||||
#. placeholder {1}: table.getFilteredRowModel().rows.length
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "{0} of {1} row(s) selected."
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "{count, plural, one {{countString} day} other {{countString} days}}"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "{count, plural, one {{countString} hour} other {{countString} hours}}"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 hour"
|
||||
msgstr ""
|
||||
@@ -234,6 +231,10 @@ msgstr ""
|
||||
msgid "Cancel"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Capacity"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
msgid "Caution - potential data loss"
|
||||
msgstr ""
|
||||
@@ -279,6 +280,10 @@ msgstr ""
|
||||
msgid "Click on a container to view more information."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Click on a device to view more information."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Click on a system to view more information."
|
||||
msgstr ""
|
||||
@@ -397,6 +402,11 @@ msgstr ""
|
||||
msgid "Current state"
|
||||
msgstr ""
|
||||
|
||||
#. Power Cycles
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Cycles"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Dashboard"
|
||||
msgstr ""
|
||||
@@ -418,6 +428,10 @@ msgstr ""
|
||||
msgid "Detail"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Device"
|
||||
msgstr ""
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Discharging"
|
||||
@@ -548,6 +562,10 @@ msgstr ""
|
||||
msgid "Fahrenheit (°F)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Failed Attributes:"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/api.ts
|
||||
msgid "Failed to authenticate"
|
||||
msgstr ""
|
||||
@@ -568,6 +586,7 @@ msgstr ""
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Filter..."
|
||||
msgstr ""
|
||||
@@ -576,6 +595,10 @@ msgstr ""
|
||||
msgid "Fingerprint"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Firmware"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/alerts/alerts-sheet.tsx
|
||||
msgid "For <0>{min}</0> {min, plural, one {minute} other {minutes}}"
|
||||
msgstr ""
|
||||
@@ -730,6 +753,10 @@ msgstr ""
|
||||
msgid "Memory usage of docker containers"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Model"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
@@ -761,11 +788,18 @@ msgstr ""
|
||||
msgid "No results found."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "No results."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "No S.M.A.R.T. attributes available for this device."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "No systems found."
|
||||
@@ -884,6 +918,11 @@ msgstr ""
|
||||
msgid "Port"
|
||||
msgstr ""
|
||||
|
||||
#. Power On Time
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Power On"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Precise utilization at the recorded time"
|
||||
@@ -943,6 +982,14 @@ msgstr ""
|
||||
msgid "Rows per page"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "S.M.A.R.T. Details"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "S.M.A.R.T. Self-Test"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Save address using enter key or comma. Leave blank to disable email notifications."
|
||||
msgstr ""
|
||||
@@ -972,6 +1019,10 @@ msgstr ""
|
||||
msgid "Sent"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Serial Number"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr ""
|
||||
@@ -1005,6 +1056,7 @@ msgid "State"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Status"
|
||||
@@ -1043,6 +1095,7 @@ msgid "Table"
|
||||
msgstr ""
|
||||
|
||||
#. Temperature label in systems table
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Temp"
|
||||
msgstr ""
|
||||
@@ -1168,6 +1221,10 @@ msgstr ""
|
||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Type"
|
||||
msgstr ""
|
||||
|
||||
#. Temperature / network units
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Unit preferences"
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user