mirror of
https://github.com/henrygd/beszel.git
synced 2026-09-14 03:54:40 +00:00
Compare commits
17
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6a7b2772d9 | ||
|
|
086091a0fe | ||
|
|
f204dc17e6 | ||
|
|
5fe1583655 | ||
|
|
6d82ee70b1 | ||
|
|
bb270e02a8 | ||
|
|
312c109138 | ||
|
|
c938368089 | ||
|
|
8d6a5d5f6e | ||
|
|
98687be2f2 | ||
|
|
e39e153ca0 | ||
|
|
5b87f7d7cb | ||
|
|
9a0aa5a89e | ||
|
|
997adc19bb | ||
|
|
08d813620c | ||
|
|
6cb302fcf6 | ||
|
|
59eed073c3 |
+4
-4
@@ -48,7 +48,7 @@ type Agent struct {
|
||||
keys []gossh.PublicKey // SSH public keys
|
||||
smartManager *SmartManager // Manages SMART data
|
||||
systemdManager *systemdManager // Manages systemd services
|
||||
zfsManager *ZfsManager // Manages ZFS pool and dataset data
|
||||
storagePoolManager *StoragePoolManager // Manages storage pool and dataset data
|
||||
}
|
||||
|
||||
// NewAgent creates a new agent with the given data directory for persisting data.
|
||||
@@ -122,12 +122,12 @@ func NewAgent(dataDir ...string) (agent *Agent, err error) {
|
||||
// initialize handler registry
|
||||
agent.handlerRegistry = NewHandlerRegistry()
|
||||
|
||||
agent.zfsManager = newZfsManager()
|
||||
agent.storagePoolManager = newStoragePoolManager()
|
||||
|
||||
// ZFS_INTERVAL env var to update ZFS detail data at this interval
|
||||
// Retain ZFS_INTERVAL for the shared storage pool detail refresh interval.
|
||||
if zfsIntervalEnv, exists := utils.GetEnv("ZFS_INTERVAL"); exists {
|
||||
if duration, err := time.ParseDuration(zfsIntervalEnv); err == nil && duration > 0 {
|
||||
agent.zfsManager.detailInterval = duration
|
||||
agent.storagePoolManager.detailInterval = duration
|
||||
agent.systemDetails.ZfsInterval = duration
|
||||
slog.Info("ZFS_INTERVAL", "duration", duration)
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
// Package btrfs reads btrfs filesystem state from sysfs.
|
||||
package btrfs
|
||||
|
||||
// Filesystem is a mounted btrfs filesystem read from /sys/fs/btrfs/<uuid>.
|
||||
type Filesystem struct {
|
||||
UUID string // stable filesystem UUID from sysfs
|
||||
MountID string // kernel filesystem identity for matching monitored mounts
|
||||
IODevice string // sole member block-device name, empty for multi-device/unknown pools
|
||||
Name string // label, else first mountpoint, else UUID
|
||||
Size uint64 // effective usable capacity, or raw member capacity when Raw
|
||||
Raw bool // capacity and usage are physical bytes, unsuitable for disk alerts
|
||||
Alloc uint64 // raw bytes allocated to data, metadata and system chunks
|
||||
Health string // ONLINE, or DEGRADED when a device is missing
|
||||
NRead uint64 // cumulative bytes read across member devices
|
||||
NWrite uint64 // cumulative bytes written across member devices
|
||||
Devices []Device
|
||||
}
|
||||
|
||||
// Device is one member device (devinfo/<devid>) with its error counters.
|
||||
type Device struct {
|
||||
Name string // "devid N"; sysfs does not expose the block device path
|
||||
State string // ONLINE or MISSING
|
||||
ReadErrs uint64
|
||||
WriteErrs uint64
|
||||
CorruptionErrs uint64
|
||||
}
|
||||
@@ -0,0 +1,285 @@
|
||||
//go:build linux
|
||||
|
||||
package btrfs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"github.com/henrygd/beszel/agent/utils"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
var (
|
||||
sysfsPath = "/sys/fs/btrfs"
|
||||
mountsPath = "/proc/self/mounts"
|
||||
mountinfoPath = "/proc/self/mountinfo"
|
||||
mountUUID = MountID
|
||||
deviceSize = ioctlDeviceSize
|
||||
filesystemUsage = statfsUsage
|
||||
)
|
||||
|
||||
// Filesystems returns all mounted btrfs filesystems, or nil when there are none.
|
||||
func Filesystems() ([]Filesystem, error) {
|
||||
entries, err := os.ReadDir(sysfsPath)
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return nil, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mounts := mountpointsByDevice()
|
||||
var filesystems []Filesystem
|
||||
for _, entry := range entries {
|
||||
if !entry.IsDir() || entry.Name() == "features" {
|
||||
continue
|
||||
}
|
||||
fs, err := readFilesystem(filepath.Join(sysfsPath, entry.Name()), mounts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("btrfs %s: %w", entry.Name(), err)
|
||||
}
|
||||
filesystems = append(filesystems, fs)
|
||||
}
|
||||
return filesystems, nil
|
||||
}
|
||||
|
||||
func readFilesystem(dir string, mounts map[string]string) (Filesystem, error) {
|
||||
fs := Filesystem{UUID: filepath.Base(dir), Name: utils.ReadStringFile(filepath.Join(dir, "label")), Health: "UNKNOWN"}
|
||||
for _, kind := range []string{"data", "metadata", "system"} {
|
||||
if value, ok := utils.ReadUintFile(filepath.Join(dir, "allocation", kind, "disk_used")); ok {
|
||||
fs.Alloc += value
|
||||
}
|
||||
}
|
||||
// devices/<name> links to the block device's sysfs directory.
|
||||
devices, err := os.ReadDir(filepath.Join(dir, "devices"))
|
||||
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||
return fs, err
|
||||
}
|
||||
mountpoint := mounts["uuid:"+fs.UUID]
|
||||
if fs.Name == "" {
|
||||
fs.Name = mountpoint
|
||||
}
|
||||
var backingSize uint64
|
||||
for _, dev := range devices {
|
||||
if mountpoint == "" {
|
||||
mountpoint = mounts[dev.Name()]
|
||||
}
|
||||
if fs.Name == "" {
|
||||
fs.Name = mountpoint
|
||||
}
|
||||
devDir := filepath.Join(dir, "devices", dev.Name())
|
||||
if size, ok := utils.ReadUintFile(filepath.Join(devDir, "size")); ok {
|
||||
backingSize += size * 512
|
||||
}
|
||||
if stat := strings.Fields(utils.ReadStringFile(filepath.Join(devDir, "stat"))); len(stat) >= 7 {
|
||||
fs.NRead += parseUint(stat[2]) * 512
|
||||
fs.NWrite += parseUint(stat[6]) * 512
|
||||
}
|
||||
}
|
||||
devids, err := os.ReadDir(filepath.Join(dir, "devinfo"))
|
||||
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||
return fs, err
|
||||
}
|
||||
capacityAvailable := len(devids) > 0
|
||||
healthKnown := len(devids) > 0
|
||||
for _, devid := range devids {
|
||||
devDir := filepath.Join(dir, "devinfo", devid.Name())
|
||||
// Replacement targets do not add filesystem capacity.
|
||||
replaceTarget, _ := utils.ReadUintFile(filepath.Join(devDir, "replace_target"))
|
||||
if replaceTarget != 1 {
|
||||
devid, err := strconv.ParseUint(devid.Name(), 10, 64)
|
||||
if err != nil {
|
||||
return fs, err
|
||||
}
|
||||
size, err := deviceSize(mountpoint, devid)
|
||||
if err != nil {
|
||||
capacityAvailable = false
|
||||
}
|
||||
fs.Size += size
|
||||
}
|
||||
dev := Device{Name: "devid " + devid.Name(), State: "ONLINE"}
|
||||
missing := utils.ReadStringFile(filepath.Join(devDir, "missing"))
|
||||
if missing != "0" && missing != "1" {
|
||||
healthKnown = false
|
||||
dev.State = "UNKNOWN"
|
||||
}
|
||||
if missing == "1" {
|
||||
dev.State = "MISSING"
|
||||
fs.Health = "DEGRADED"
|
||||
}
|
||||
for line := range strings.Lines(utils.ReadStringFile(filepath.Join(devDir, "error_stats"))) {
|
||||
if fields := strings.Fields(line); len(fields) == 2 {
|
||||
switch fields[0] {
|
||||
case "read_errs":
|
||||
dev.ReadErrs = parseUint(fields[1])
|
||||
case "write_errs":
|
||||
dev.WriteErrs = parseUint(fields[1])
|
||||
case "corruption_errs":
|
||||
dev.CorruptionErrs = parseUint(fields[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
fs.Devices = append(fs.Devices, dev)
|
||||
}
|
||||
// Use one capacity source for the whole filesystem: device IDs cannot be
|
||||
// reliably matched to block-device names in sysfs. A partial ioctl result
|
||||
// must not be added to the complete backing-device total.
|
||||
if !capacityAvailable {
|
||||
fs.Size = backingSize
|
||||
}
|
||||
if fs.Health != "DEGRADED" && healthKnown {
|
||||
fs.Health = "ONLINE"
|
||||
}
|
||||
fs.MountID = mountUUID(mountpoint)
|
||||
if len(devices) == 1 && len(devids) == 1 && fs.Health == "ONLINE" {
|
||||
fs.IODevice = devices[0].Name()
|
||||
}
|
||||
fs.Raw = true
|
||||
if used, available, err := filesystemUsage(mountpoint); err == nil {
|
||||
// Effective capacity excludes reserved/unavailable space, so Size-Alloc
|
||||
// is available to applications and the usage ratio matches df.
|
||||
fs.Size, fs.Alloc, fs.Raw = used+available, used, false
|
||||
}
|
||||
if fs.Name == "" {
|
||||
fs.Name = filepath.Base(dir)
|
||||
}
|
||||
return fs, nil
|
||||
}
|
||||
|
||||
// mountpointsByDevice prefers UUID matches from mountinfo and retains source
|
||||
// device names as a fallback for environments where FS_INFO is unavailable.
|
||||
func mountpointsByDevice() map[string]string {
|
||||
mounts := mountpointsByUUID(utils.ReadStringFile(mountinfoPath), mountUUID)
|
||||
for line := range strings.Lines(utils.ReadStringFile(mountsPath)) {
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) < 3 || fields[2] != "btrfs" {
|
||||
continue
|
||||
}
|
||||
device := fields[0]
|
||||
if resolved, err := filepath.EvalSymlinks(device); err == nil {
|
||||
device = resolved
|
||||
}
|
||||
if _, seen := mounts[filepath.Base(device)]; !seen {
|
||||
mounts[filepath.Base(device)] = unescapeMountPath(fields[1])
|
||||
}
|
||||
}
|
||||
return mounts
|
||||
}
|
||||
|
||||
func parseUint(s string) uint64 {
|
||||
n, _ := strconv.ParseUint(s, 10, 64)
|
||||
return n
|
||||
}
|
||||
|
||||
// ioctlDeviceSize reads Btrfs's recorded device size, which can be smaller
|
||||
// than the block device after a filesystem resize. BTRFS_IOC_DEV_INFO is
|
||||
// _IOWR(0x94, 30, struct btrfs_ioctl_dev_info_args), a 4096-byte ABI structure.
|
||||
func ioctlDeviceSize(mountpoint string, devid uint64) (uint64, error) {
|
||||
if mountpoint == "" {
|
||||
return 0, errors.New("no accessible mountpoint")
|
||||
}
|
||||
f, err := os.Open(mountpoint)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer f.Close()
|
||||
args := struct {
|
||||
Devid uint64
|
||||
UUID [16]byte
|
||||
BytesUsed uint64
|
||||
TotalBytes uint64
|
||||
Reserved [4096 - 40]byte
|
||||
}{Devid: devid}
|
||||
_, _, errno := unix.Syscall(unix.SYS_IOCTL, f.Fd(), 0xd000941e, uintptr(unsafe.Pointer(&args)))
|
||||
if errno != 0 {
|
||||
return 0, errno
|
||||
}
|
||||
return args.TotalBytes, nil
|
||||
}
|
||||
|
||||
// The filesystem magic is unsigned even when Statfs_t.Type is int32.
|
||||
func isBtrfs(stat *unix.Statfs_t) bool {
|
||||
return uint32(stat.Type) == unix.BTRFS_SUPER_MAGIC
|
||||
}
|
||||
|
||||
func statfsUsage(path string) (used, available uint64, err error) {
|
||||
if path == "" {
|
||||
return 0, 0, errors.New("no accessible mountpoint")
|
||||
}
|
||||
var stat unix.Statfs_t
|
||||
if err = unix.Statfs(path, &stat); err != nil {
|
||||
return
|
||||
}
|
||||
if !isBtrfs(&stat) {
|
||||
return 0, 0, errors.New("mountpoint is not Btrfs")
|
||||
}
|
||||
blockSize := uint64(stat.Bsize)
|
||||
return (stat.Blocks - min(stat.Blocks, stat.Bfree)) * blockSize, min(stat.Blocks, stat.Bavail) * blockSize, nil
|
||||
}
|
||||
|
||||
// MountID returns the filesystem UUID via BTRFS_IOC_FS_INFO. Unlike statfs
|
||||
// f_fsid, this identity is shared by all subvolumes and bind mounts.
|
||||
func MountID(path string) string {
|
||||
if path == "" {
|
||||
return ""
|
||||
}
|
||||
var stat unix.Statfs_t
|
||||
if unix.Statfs(path, &stat) != nil || !isBtrfs(&stat) {
|
||||
return ""
|
||||
}
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
defer f.Close()
|
||||
args := struct {
|
||||
MaxID uint64
|
||||
NumDevices uint64
|
||||
FSID [16]byte
|
||||
Reserved [992]byte
|
||||
}{}
|
||||
// _IOR(0x94, 31, 1024). Reuse the platform's read-direction bits;
|
||||
// MIPS/PowerPC use a different encoding than asm-generic.
|
||||
request := uintptr(unix.FS_IOC_GETFLAGS&0xe0000000) | 0x0400941f
|
||||
_, _, errno := unix.Syscall(unix.SYS_IOCTL, f.Fd(), request, uintptr(unsafe.Pointer(&args)))
|
||||
if errno != 0 {
|
||||
return ""
|
||||
}
|
||||
id := args.FSID
|
||||
return fmt.Sprintf("%x-%x-%x-%x-%x", id[:4], id[4:6], id[6:8], id[8:10], id[10:])
|
||||
}
|
||||
|
||||
// Btrfs mountinfo device numbers can be virtual (0:N), so query the UUID
|
||||
// through the mount instead of comparing those numbers with sysfs block devs.
|
||||
// Retry another path when a bind mount is inaccessible. Once resolved, reuse
|
||||
// the result for that mount device to avoid opening every Docker bind mount.
|
||||
func mountpointsByUUID(mountinfo string, identify func(string) string) map[string]string {
|
||||
mounts := make(map[string]string)
|
||||
resolved := make(map[string]bool)
|
||||
for line := range strings.Lines(mountinfo) {
|
||||
before, after, ok := strings.Cut(line, " - ")
|
||||
fields, fs := strings.Fields(before), strings.Fields(after)
|
||||
if !ok || len(fields) < 6 || len(fs) < 3 || fs[0] != "btrfs" || resolved[fields[2]] {
|
||||
continue
|
||||
}
|
||||
path := unescapeMountPath(fields[4])
|
||||
uuid := identify(path)
|
||||
if uuid == "" {
|
||||
continue
|
||||
}
|
||||
resolved[fields[2]] = true
|
||||
if mounts["uuid:"+uuid] == "" {
|
||||
mounts["uuid:"+uuid] = path
|
||||
}
|
||||
}
|
||||
return mounts
|
||||
}
|
||||
|
||||
func unescapeMountPath(path string) string {
|
||||
return strings.NewReplacer(`\040`, " ", `\011`, "\t", `\012`, "\n", `\134`, `\`).Replace(path)
|
||||
}
|
||||
@@ -0,0 +1,274 @@
|
||||
//go:build testing && linux
|
||||
|
||||
package btrfs
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/henrygd/beszel/agent/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func TestFilesystems(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
oldSysfs, oldMounts := sysfsPath, mountsPath
|
||||
sysfsPath, mountsPath = root, filepath.Join(root, "mounts")
|
||||
t.Cleanup(func() { sysfsPath, mountsPath = oldSysfs, oldMounts })
|
||||
|
||||
fsDir := filepath.Join(root, "1b2c3d4e-0000-0000-0000-000000000000")
|
||||
write := func(rel, content string) {
|
||||
path := filepath.Join(fsDir, rel)
|
||||
require.NoError(t, os.MkdirAll(filepath.Dir(path), 0o755))
|
||||
require.NoError(t, os.WriteFile(path, []byte(content), 0o644))
|
||||
}
|
||||
require.NoError(t, os.MkdirAll(filepath.Join(root, "features"), 0o755))
|
||||
oldUsage := filesystemUsage
|
||||
filesystemUsage = func(string) (uint64, uint64, error) { return 0, 0, os.ErrNotExist }
|
||||
t.Cleanup(func() { filesystemUsage = oldUsage })
|
||||
oldDeviceSize := deviceSize
|
||||
t.Cleanup(func() { deviceSize = oldDeviceSize })
|
||||
deviceSize = func(_ string, devid uint64) (uint64, error) {
|
||||
value, _ := utils.ReadUintFile(filepath.Join(fsDir, "recorded-size", strconv.FormatUint(devid, 10)))
|
||||
return value, nil
|
||||
}
|
||||
// Recorded member capacities differ from the unchanged backing devices.
|
||||
write("recorded-size/1", "256000\n")
|
||||
write("recorded-size/2", "128000\n")
|
||||
write("label", "tank\n")
|
||||
write("allocation/data/disk_used", "4096\n")
|
||||
write("allocation/metadata/disk_used", "2048\n")
|
||||
write("allocation/system/disk_used", "1024\n")
|
||||
write("devices/sda/size", "1000\n")
|
||||
write("devices/sda/stat", "10 0 200 0 20 0 400 0 0 0 0\n")
|
||||
write("devices/sdb/size", "1000\n")
|
||||
write("devices/sdb/stat", "10 0 100 0 20 0 100 0 0 0 0\n")
|
||||
write("devinfo/1/missing", "0\n")
|
||||
write("devinfo/1/error_stats", "write_errs 1\nread_errs 2\nflush_errs 0\ncorruption_errs 3\ngeneration_errs 0\n")
|
||||
write("devinfo/2/missing", "1\n")
|
||||
|
||||
filesystems, err := Filesystems()
|
||||
require.NoError(t, err)
|
||||
require.Len(t, filesystems, 1)
|
||||
assert.Equal(t, Filesystem{
|
||||
UUID: "1b2c3d4e-0000-0000-0000-000000000000", Raw: true, Name: "tank", Size: 384000, Alloc: 7168, Health: "DEGRADED", NRead: 153600, NWrite: 256000,
|
||||
Devices: []Device{
|
||||
{Name: "devid 1", State: "ONLINE", ReadErrs: 2, WriteErrs: 1, CorruptionErrs: 3},
|
||||
{Name: "devid 2", State: "MISSING"},
|
||||
},
|
||||
}, filesystems[0])
|
||||
|
||||
// Unlabeled filesystems fall back to the first mountpoint, then the UUID.
|
||||
write("label", "\n")
|
||||
require.NoError(t, os.WriteFile(mountsPath, []byte(
|
||||
"/dev/sdz1 /other btrfs rw 0 0\n/dev/sdb /mnt/storage btrfs rw 0 0\n/dev/sdb /mnt/storage/sub btrfs rw,subvol=/sub 0 0\n",
|
||||
), 0o644))
|
||||
filesystems, err = Filesystems()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "/mnt/storage", filesystems[0].Name)
|
||||
|
||||
require.NoError(t, os.Remove(mountsPath))
|
||||
filesystems, err = Filesystems()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "1b2c3d4e-0000-0000-0000-000000000000", filesystems[0].Name)
|
||||
write("devinfo/3/replace_target", "1\n")
|
||||
write("recorded-size/3", "512000\n")
|
||||
filesystems, err = Filesystems()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, uint64(384000), filesystems[0].Size, "replacement target must not inflate capacity")
|
||||
|
||||
deviceSize = func(string, uint64) (uint64, error) { return 0, os.ErrPermission }
|
||||
filesystems, err = Filesystems()
|
||||
require.NoError(t, err)
|
||||
require.Len(t, filesystems, 1)
|
||||
assert.Equal(t, uint64(1024000), filesystems[0].Size)
|
||||
assert.Equal(t, "DEGRADED", filesystems[0].Health)
|
||||
assert.Equal(t, uint64(153600), filesystems[0].NRead)
|
||||
|
||||
// A partial ioctl result must not be mixed with the backing-device total.
|
||||
deviceSize = func(_ string, devid uint64) (uint64, error) {
|
||||
if devid == 2 {
|
||||
return 0, os.ErrPermission
|
||||
}
|
||||
return 256000, nil
|
||||
}
|
||||
filesystems, err = Filesystems()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, uint64(1024000), filesystems[0].Size)
|
||||
|
||||
// With no mount visible (e.g. Docker), the real lookup falls back too.
|
||||
deviceSize = ioctlDeviceSize
|
||||
filesystems, err = Filesystems()
|
||||
require.NoError(t, err)
|
||||
require.Len(t, filesystems, 1)
|
||||
assert.Equal(t, uint64(1024000), filesystems[0].Size)
|
||||
|
||||
filesystemUsage = func(string) (uint64, uint64, error) { return 100, 900, nil }
|
||||
filesystems, err = Filesystems()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, uint64(1000), filesystems[0].Size)
|
||||
assert.Equal(t, uint64(100), filesystems[0].Alloc)
|
||||
assert.False(t, filesystems[0].Raw)
|
||||
}
|
||||
|
||||
func TestFilesystemsNoBtrfs(t *testing.T) {
|
||||
oldPath := sysfsPath
|
||||
sysfsPath = filepath.Join(t.TempDir(), "missing")
|
||||
t.Cleanup(func() { sysfsPath = oldPath })
|
||||
|
||||
filesystems, err := Filesystems()
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, filesystems)
|
||||
}
|
||||
|
||||
func TestIoctlDeviceSizeFailure(t *testing.T) {
|
||||
_, err := ioctlDeviceSize("", 1)
|
||||
require.Error(t, err)
|
||||
_, err = ioctlDeviceSize(t.TempDir(), 1)
|
||||
require.Error(t, err)
|
||||
assert.ErrorIs(t, err, unix.ENOTTY)
|
||||
}
|
||||
|
||||
func TestMountpointsDecodeEscapes(t *testing.T) {
|
||||
oldMounts := mountsPath
|
||||
mountsPath = filepath.Join(t.TempDir(), "mounts")
|
||||
t.Cleanup(func() { mountsPath = oldMounts })
|
||||
require.NoError(t, os.WriteFile(mountsPath, []byte("/dev/test-btrfs /mnt/my\\040data btrfs rw 0 0\n"), 0o644))
|
||||
assert.Equal(t, "/mnt/my data", mountpointsByDevice()["test-btrfs"])
|
||||
}
|
||||
|
||||
func TestFilesystemWithoutDevinfo(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
require.NoError(t, os.MkdirAll(filepath.Join(root, "devices", "sda"), 0755))
|
||||
require.NoError(t, os.WriteFile(filepath.Join(root, "devices", "sda", "size"), []byte("1000"), 0644))
|
||||
fs, err := readFilesystem(root, nil)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, uint64(512000), fs.Size)
|
||||
assert.True(t, fs.Raw)
|
||||
assert.Equal(t, "UNKNOWN", fs.Health)
|
||||
assert.Empty(t, fs.Devices)
|
||||
|
||||
require.NoError(t, os.MkdirAll(filepath.Join(root, "devinfo", "1"), 0755))
|
||||
fs, err = readFilesystem(root, nil)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "UNKNOWN", fs.Health)
|
||||
require.Len(t, fs.Devices, 1)
|
||||
assert.Equal(t, "UNKNOWN", fs.Devices[0].State)
|
||||
|
||||
// Some older interfaces lack the devices directory too.
|
||||
fs, err = readFilesystem(t.TempDir(), nil)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "UNKNOWN", fs.Health)
|
||||
}
|
||||
|
||||
func TestLocalBtrfsUsage(t *testing.T) {
|
||||
path := os.Getenv("BESZEL_TEST_BTRFS_MOUNT")
|
||||
if path == "" {
|
||||
t.Skip("set BESZEL_TEST_BTRFS_MOUNT for read-only live validation")
|
||||
}
|
||||
used, available, err := statfsUsage(path)
|
||||
require.NoError(t, err)
|
||||
filesystems, err := Filesystems()
|
||||
require.NoError(t, err)
|
||||
for _, fs := range filesystems {
|
||||
if !fs.Raw && fs.Alloc == used && fs.Size == used+available {
|
||||
t.Logf("pool=%s used=%d available=%d effective_capacity=%d", fs.Name, used, available, fs.Size)
|
||||
return
|
||||
}
|
||||
}
|
||||
t.Fatal("collector did not report the mounted filesystem's usable capacity")
|
||||
}
|
||||
|
||||
func TestMountID(t *testing.T) {
|
||||
assert.Empty(t, MountID(""))
|
||||
assert.Empty(t, MountID(filepath.Join(t.TempDir(), "missing")))
|
||||
path := os.Getenv("BESZEL_TEST_BTRFS_MOUNT")
|
||||
if path == "" {
|
||||
t.Skip("set BESZEL_TEST_BTRFS_MOUNT for live identity validation")
|
||||
}
|
||||
id := MountID(path)
|
||||
require.NotEmpty(t, id)
|
||||
assert.Equal(t, id, MountID(filepath.Join(path, ".")))
|
||||
}
|
||||
|
||||
func TestMountinfoUUIDLookup(t *testing.T) {
|
||||
info := `1 0 0:40 /@ /inaccessible ro shared:1 - btrfs /dev/mapper/unavailable rw
|
||||
2 0 0:40 /@/docker/hosts /etc/hosts ro - btrfs /dev/mapper/unavailable rw
|
||||
3 0 0:40 /@/docker/hostname /etc/hostname ro - btrfs /dev/mapper/unavailable rw
|
||||
4 0 0:41 /subvol /extra-filesystems/my\040disk ro master:2 - btrfs /dev/missing rw
|
||||
5 0 0:42 / /ext4 ro - ext4 /dev/mapper/unavailable rw
|
||||
malformed
|
||||
6 0 0:43 / /bad ro - btrfs
|
||||
`
|
||||
var calls []string
|
||||
mounts := mountpointsByUUID(info, func(path string) string {
|
||||
calls = append(calls, path)
|
||||
switch path {
|
||||
case "/etc/hosts":
|
||||
return "root-uuid"
|
||||
case "/extra-filesystems/my disk":
|
||||
return "extra-uuid"
|
||||
}
|
||||
return ""
|
||||
})
|
||||
assert.Equal(t, map[string]string{"uuid:root-uuid": "/etc/hosts", "uuid:extra-uuid": "/extra-filesystems/my disk"}, mounts)
|
||||
assert.Equal(t, []string{"/inaccessible", "/etc/hosts", "/extra-filesystems/my disk"}, calls)
|
||||
}
|
||||
|
||||
func TestDockerFilesystemWithoutDeviceNodes(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
oldSysfs, oldMounts, oldInfo, oldUUID, oldUsage := sysfsPath, mountsPath, mountinfoPath, mountUUID, filesystemUsage
|
||||
t.Cleanup(func() {
|
||||
sysfsPath, mountsPath, mountinfoPath, mountUUID, filesystemUsage = oldSysfs, oldMounts, oldInfo, oldUUID, oldUsage
|
||||
})
|
||||
sysfsPath = filepath.Join(root, "sysfs")
|
||||
mountsPath = filepath.Join(root, "missing-mounts")
|
||||
mountinfoPath = filepath.Join(root, "mountinfo")
|
||||
uuid := "11111111-1111-4111-8111-111111111111"
|
||||
dir := filepath.Join(sysfsPath, uuid)
|
||||
for path, content := range map[string]string{"devices/dm-0/size": "1000", "devinfo/1/missing": "0"} {
|
||||
target := filepath.Join(dir, path)
|
||||
require.NoError(t, os.MkdirAll(filepath.Dir(target), 0755))
|
||||
require.NoError(t, os.WriteFile(target, []byte(content), 0644))
|
||||
}
|
||||
require.NoError(t, os.WriteFile(mountinfoPath, []byte("2 1 0:40 /@/docker/hosts /etc/hosts ro - btrfs /dev/mapper/not-in-container rw\n"), 0644))
|
||||
mountUUID = func(path string) string {
|
||||
if path == "/etc/hosts" {
|
||||
return uuid
|
||||
}
|
||||
return ""
|
||||
}
|
||||
filesystemUsage = func(path string) (uint64, uint64, error) { require.Equal(t, "/etc/hosts", path); return 100, 900, nil }
|
||||
fs, err := Filesystems()
|
||||
require.NoError(t, err)
|
||||
require.Len(t, fs, 1)
|
||||
assert.Equal(t, uuid, fs[0].MountID)
|
||||
assert.Equal(t, "dm-0", fs[0].IODevice)
|
||||
assert.False(t, fs[0].Raw)
|
||||
assert.Equal(t, uint64(1000), fs[0].Size)
|
||||
}
|
||||
|
||||
func TestLivePoolMountIdentity(t *testing.T) {
|
||||
path := os.Getenv("BESZEL_TEST_BTRFS_MOUNT")
|
||||
if path == "" {
|
||||
t.Skip("set BESZEL_TEST_BTRFS_MOUNT for live validation")
|
||||
}
|
||||
id := MountID(path)
|
||||
require.NotEmpty(t, id)
|
||||
pools, err := Filesystems()
|
||||
require.NoError(t, err)
|
||||
for _, pool := range pools {
|
||||
if pool.UUID != id {
|
||||
continue
|
||||
}
|
||||
assert.Equal(t, id, pool.MountID)
|
||||
assert.False(t, pool.Raw)
|
||||
t.Logf("uuid=%s mount_identity=%s io_device=%s raw=%v", pool.UUID, pool.MountID, pool.IODevice, pool.Raw)
|
||||
return
|
||||
}
|
||||
t.Fatal("mounted Btrfs filesystem was not discovered")
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
//go:build !linux
|
||||
|
||||
package btrfs
|
||||
|
||||
import "errors"
|
||||
|
||||
func Filesystems() ([]Filesystem, error) {
|
||||
return nil, errors.ErrUnsupported
|
||||
}
|
||||
|
||||
func MountID(string) string { return "" }
|
||||
+6
-1
@@ -30,6 +30,11 @@ const (
|
||||
wsDeadline = 120 * time.Second
|
||||
)
|
||||
|
||||
// errNoHubURL is returned when HUB_URL is unset. This is not a failure
|
||||
// condition: an agent configured with only a public key runs in SSH-only mode,
|
||||
// where the hub dials the agent and no outbound WebSocket client is expected.
|
||||
var errNoHubURL = errors.New("HUB_URL environment variable not set")
|
||||
|
||||
type caCertFileError struct {
|
||||
err error
|
||||
}
|
||||
@@ -63,7 +68,7 @@ type WebSocketClient struct {
|
||||
func newWebSocketClient(agent *Agent) (client *WebSocketClient, err error) {
|
||||
hubURLStr, exists := utils.GetEnv("HUB_URL")
|
||||
if !exists {
|
||||
return nil, errors.New("HUB_URL environment variable not set")
|
||||
return nil, errNoHubURL
|
||||
}
|
||||
|
||||
client = &WebSocketClient{}
|
||||
|
||||
@@ -32,6 +32,28 @@ import (
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
// TestNewWebSocketClientNoHubURL verifies that an unset HUB_URL returns the
|
||||
// errNoHubURL sentinel rather than an opaque error. Callers rely on this to
|
||||
// distinguish SSH-only mode -- a supported configuration in which the hub dials
|
||||
// the agent -- from an actual misconfiguration.
|
||||
func TestNewWebSocketClientNoHubURL(t *testing.T) {
|
||||
agent := createTestAgent(t)
|
||||
|
||||
// t.Setenv registers restoration of the original value; unset afterwards so
|
||||
// GetEnv's LookupEnv reports the variable as absent rather than empty.
|
||||
t.Setenv("BESZEL_AGENT_HUB_URL", "")
|
||||
os.Unsetenv("BESZEL_AGENT_HUB_URL")
|
||||
t.Setenv("HUB_URL", "")
|
||||
os.Unsetenv("HUB_URL")
|
||||
t.Setenv("BESZEL_AGENT_TOKEN", "test-token")
|
||||
|
||||
client, err := newWebSocketClient(agent)
|
||||
|
||||
require.Error(t, err)
|
||||
assert.Nil(t, client)
|
||||
assert.ErrorIs(t, err, errNoHubURL)
|
||||
}
|
||||
|
||||
// TestNewWebSocketClient tests WebSocket client creation
|
||||
func TestNewWebSocketClient(t *testing.T) {
|
||||
agent := createTestAgent(t)
|
||||
|
||||
@@ -91,7 +91,15 @@ func (c *ConnectionManager) Start(serverOptions ServerOptions) error {
|
||||
if errors.As(err, &caCertErr) {
|
||||
return err
|
||||
}
|
||||
slog.Warn("Error creating WebSocket client", "err", err)
|
||||
disableSSH, _ := utils.GetEnv("DISABLE_SSH")
|
||||
if errors.Is(err, errNoHubURL) && disableSSH != "true" {
|
||||
// SSH-only mode: the hub dials the agent, so there is nothing to warn
|
||||
// about. With SSH also disabled there is no connection method at all,
|
||||
// so that case still warns.
|
||||
slog.Debug("WebSocket client not configured", "err", err)
|
||||
} else {
|
||||
slog.Warn("Error creating WebSocket client", "err", err)
|
||||
}
|
||||
}
|
||||
c.wsClient = wsClient
|
||||
|
||||
|
||||
+14
-14
@@ -18,11 +18,11 @@ import (
|
||||
// fsRegistrationContext holds the shared lookup state needed to resolve a
|
||||
// filesystem into the tracked fsStats key and metadata.
|
||||
type fsRegistrationContext struct {
|
||||
filesystem string // device part of optional FILESYSTEM env var
|
||||
filesystemName string // optional custom name from FILESYSTEM=device__name
|
||||
isWindows bool
|
||||
efPath string // path to extra filesystems (default "/extra-filesystems")
|
||||
diskIoCounters map[string]disk.IOCountersStat
|
||||
filesystem string // device part of optional FILESYSTEM env var
|
||||
filesystemName string // optional custom name from FILESYSTEM=device__name
|
||||
isWindows bool
|
||||
efPath string // path to extra filesystems (default "/extra-filesystems")
|
||||
diskIoCounters map[string]disk.IOCountersStat
|
||||
}
|
||||
|
||||
// diskDiscovery groups the transient state for a single initializeDiskInfo run so
|
||||
@@ -325,11 +325,11 @@ func (a *Agent) initializeDiskInfo() {
|
||||
}
|
||||
slog.Debug("Disk I/O", "diskstats", diskIoCounters)
|
||||
ctx := fsRegistrationContext{
|
||||
filesystem: filesystem,
|
||||
filesystemName: filesystemName,
|
||||
isWindows: isWindows,
|
||||
diskIoCounters: diskIoCounters,
|
||||
efPath: "/extra-filesystems",
|
||||
filesystem: filesystem,
|
||||
filesystemName: filesystemName,
|
||||
isWindows: isWindows,
|
||||
diskIoCounters: diskIoCounters,
|
||||
efPath: "/extra-filesystems",
|
||||
}
|
||||
|
||||
// Get the appropriate root mount point for this system
|
||||
@@ -540,8 +540,8 @@ func (a *Agent) initializeDiskIoStats(diskIoCounters map[string]disk.IOCountersS
|
||||
// ZFS datasets have no /proc/diskstats entry, so they are excluded from
|
||||
// I/O tracking instead of warning about a missing device (#1541).
|
||||
var zfsMountpoints map[string]bool
|
||||
if a.zfsManager != nil {
|
||||
zfsMountpoints = a.zfsManager.ZfsMountpoints()
|
||||
if a.storagePoolManager != nil {
|
||||
zfsMountpoints = a.storagePoolManager.ZfsMountpoints()
|
||||
}
|
||||
for device, stats := range a.fsStats {
|
||||
if zfsMountpoints[stats.Mountpoint] {
|
||||
@@ -574,8 +574,8 @@ func (a *Agent) updateDiskUsage(systemStats *system.Stats) {
|
||||
// ZFS dataset mountpoints use `zfs list` values because statfs(2) reports
|
||||
// dataset-level usage that excludes child datasets (#1541).
|
||||
var zfsUsage map[string]zfsDatasetUsage
|
||||
if a.zfsManager != nil {
|
||||
zfsUsage = a.zfsManager.DatasetUsage()
|
||||
if a.storagePoolManager != nil {
|
||||
zfsUsage = a.storagePoolManager.DatasetUsage()
|
||||
}
|
||||
|
||||
// disk usage
|
||||
|
||||
+11
-10
@@ -4,6 +4,7 @@ package agent
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/henrygd/beszel/agent/zfs"
|
||||
"github.com/henrygd/beszel/internal/entities/system"
|
||||
@@ -16,8 +17,8 @@ import (
|
||||
// is a ZFS dataset reports `zfs list` usage (which includes child datasets)
|
||||
// instead of the dataset-scoped statfs values (#1541).
|
||||
func TestUpdateDiskUsageZfsMountpoint(t *testing.T) {
|
||||
zm := &ZfsManager{}
|
||||
zm.datasetsFn = func() ([]zfs.Dataset, error) {
|
||||
zm := &StoragePoolManager{detailInterval: time.Hour, backends: []*poolBackend{{name: "zfs"}}}
|
||||
zm.backends[0].datasetsFn = func() ([]zfs.Dataset, error) {
|
||||
return []zfs.Dataset{
|
||||
{Name: "tank", Used: 12000000000000, Avail: 11999000000000, Mountpoint: "/tank"},
|
||||
}, nil
|
||||
@@ -26,7 +27,7 @@ func TestUpdateDiskUsageZfsMountpoint(t *testing.T) {
|
||||
fsStats: map[string]*system.FsStats{
|
||||
"tank": {Root: false, Mountpoint: "/tank"},
|
||||
},
|
||||
zfsManager: zm,
|
||||
storagePoolManager: zm,
|
||||
}
|
||||
|
||||
var stats system.Stats
|
||||
@@ -43,8 +44,8 @@ func TestUpdateDiskUsageZfsMountpoint(t *testing.T) {
|
||||
// TestUpdateDiskUsageZfsRootPopulatesSystemStats verifies the root disk values
|
||||
// are derived from ZFS usage when the root mountpoint is a ZFS dataset.
|
||||
func TestUpdateDiskUsageZfsRootPopulatesSystemStats(t *testing.T) {
|
||||
zm := &ZfsManager{}
|
||||
zm.datasetsFn = func() ([]zfs.Dataset, error) {
|
||||
zm := &StoragePoolManager{detailInterval: time.Hour, backends: []*poolBackend{{name: "zfs"}}}
|
||||
zm.backends[0].datasetsFn = func() ([]zfs.Dataset, error) {
|
||||
return []zfs.Dataset{
|
||||
{Name: "rpool/ROOT/pve-1", Used: 900000000000, Avail: 300000000000, Mountpoint: "/"},
|
||||
}, nil
|
||||
@@ -53,7 +54,7 @@ func TestUpdateDiskUsageZfsRootPopulatesSystemStats(t *testing.T) {
|
||||
fsStats: map[string]*system.FsStats{
|
||||
"rpool/ROOT/pve-1": {Root: true, Mountpoint: "/"},
|
||||
},
|
||||
zfsManager: zm,
|
||||
storagePoolManager: zm,
|
||||
}
|
||||
|
||||
var stats system.Stats
|
||||
@@ -85,8 +86,8 @@ func TestUpdateDiskUsageWithoutZfsManager(t *testing.T) {
|
||||
// TestInitializeDiskIoStatsSkipsZfsMountpoints verifies ZFS filesystems are
|
||||
// excluded from diskstats I/O tracking instead of warning about a missing device.
|
||||
func TestInitializeDiskIoStatsSkipsZfsMountpoints(t *testing.T) {
|
||||
zm := &ZfsManager{}
|
||||
zm.datasetsFn = func() ([]zfs.Dataset, error) {
|
||||
zm := &StoragePoolManager{detailInterval: time.Hour, backends: []*poolBackend{{name: "zfs"}}}
|
||||
zm.backends[0].datasetsFn = func() ([]zfs.Dataset, error) {
|
||||
return []zfs.Dataset{{Name: "tank", Mountpoint: "/tank"}}, nil
|
||||
}
|
||||
agent := &Agent{
|
||||
@@ -94,8 +95,8 @@ func TestInitializeDiskIoStatsSkipsZfsMountpoints(t *testing.T) {
|
||||
"tank": {Root: false, Mountpoint: "/tank"},
|
||||
"sda1": {Root: false, Mountpoint: "/mnt/data"},
|
||||
},
|
||||
zfsManager: zm,
|
||||
diskPrev: make(map[uint16]map[string]prevDisk),
|
||||
storagePoolManager: zm,
|
||||
diskPrev: make(map[uint16]map[string]prevDisk),
|
||||
}
|
||||
|
||||
agent.initializeDiskIoStats(map[string]disk.IOCountersStat{
|
||||
|
||||
+22
-8
@@ -65,10 +65,14 @@ type dockerManager struct {
|
||||
dockerVersionChecked bool // Whether a version probe has completed successfully
|
||||
isWindows bool // Whether the Docker Engine API is running on Windows
|
||||
buf *bytes.Buffer // Buffer to store and read response bodies
|
||||
apiStats *container.ApiStats // Reusable API stats object
|
||||
excludeContainers []string // Patterns to exclude containers by name
|
||||
usingPodman bool // Whether the Docker Engine API is running on Podman
|
||||
|
||||
registryClient *http.Client // Client for registry requests; nil uses a client with a 10-second timeout
|
||||
imageUpdatesMutex sync.RWMutex // Protects imageUpdates, its entries, and imageUpdatesRunning
|
||||
imageUpdates map[string]*imageUpdateStatus // Shared update status keyed by normalized image reference
|
||||
imageUpdatesRunning bool // Whether a background image-update batch is in progress
|
||||
|
||||
// Cache-time-aware tracking for CPU stats (similar to cpu.go)
|
||||
// Maps cache time intervals to container-specific CPU usage tracking
|
||||
lastCpuContainer map[uint16]map[string]uint64 // cacheTimeMs -> containerId -> last cpu container usage
|
||||
@@ -161,6 +165,9 @@ func (dm *dockerManager) getDockerStats(cacheTimeMs uint16) ([]*container.Stats,
|
||||
clear(dm.validIds)
|
||||
}
|
||||
|
||||
// Only schedule auxiliary work here; metrics never wait for image discovery.
|
||||
dm.refreshImageUpdates(dm.apiContainerList, time.Now())
|
||||
|
||||
var failedContainers []*container.ApiInfo
|
||||
|
||||
for _, ctr := range dm.apiContainerList {
|
||||
@@ -506,6 +513,17 @@ func (dm *dockerManager) updateContainerStats(ctr *container.ApiInfo, cacheTimeM
|
||||
}
|
||||
}
|
||||
|
||||
// Read and decode the response before locking shared stats to avoid blocking
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("container stats request failed: %s", resp.Status)
|
||||
}
|
||||
res := &container.ApiStats{}
|
||||
if err := json.NewDecoder(resp.Body).Decode(res); err != nil {
|
||||
return err
|
||||
}
|
||||
updateAvailable := dm.cachedImageUpdate(ctr.Image)
|
||||
|
||||
dm.containerStatsMutex.Lock()
|
||||
defer dm.containerStatsMutex.Unlock()
|
||||
|
||||
@@ -520,6 +538,9 @@ func (dm *dockerManager) updateContainerStats(ctr *container.ApiInfo, cacheTimeM
|
||||
stats.Status = statusText
|
||||
stats.Health = health
|
||||
|
||||
stats.Image = ctr.Image
|
||||
stats.UpdateAvailable = updateAvailable
|
||||
|
||||
if len(ctr.Ports) > 0 {
|
||||
stats.Ports = convertContainerPortsToString(ctr)
|
||||
}
|
||||
@@ -532,12 +553,6 @@ func (dm *dockerManager) updateContainerStats(ctr *container.ApiInfo, cacheTimeM
|
||||
stats.NetworkSent = 0
|
||||
stats.NetworkRecv = 0
|
||||
|
||||
res := dm.apiStats
|
||||
res.Networks = nil
|
||||
if err := dm.decode(resp, res); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Initialize CPU tracking for this cache time interval
|
||||
dm.initializeCpuTracking(cacheTimeMs)
|
||||
|
||||
@@ -695,7 +710,6 @@ func newDockerManager(agent *Agent) *dockerManager {
|
||||
containerStatsMap: make(map[string]*container.Stats),
|
||||
sem: make(chan struct{}, 5),
|
||||
apiContainerList: []*container.ApiInfo{},
|
||||
apiStats: &container.ApiStats{},
|
||||
excludeContainers: excludeContainers,
|
||||
|
||||
// Initialize cache-time-aware tracking structures
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/distribution/reference"
|
||||
"github.com/henrygd/beszel/internal/entities/container"
|
||||
)
|
||||
|
||||
const imageUpdateInterval = time.Hour
|
||||
|
||||
type imageUpdateStatus struct {
|
||||
available bool
|
||||
checkedAt time.Time
|
||||
}
|
||||
|
||||
func normalizedImageReference(image string) string {
|
||||
named, err := reference.ParseNormalizedNamed(image)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
// Digest-pinned references cannot move to a new version.
|
||||
if _, pinned := named.(reference.Digested); pinned {
|
||||
return ""
|
||||
}
|
||||
return reference.TagNameOnly(named).String()
|
||||
}
|
||||
|
||||
// refreshImageUpdates starts at most one background batch. Neither its network
|
||||
// work nor its completion is part of the container metrics wait group.
|
||||
func (dm *dockerManager) refreshImageUpdates(containers []*container.ApiInfo, now time.Time) {
|
||||
dm.imageUpdatesMutex.Lock()
|
||||
defer dm.imageUpdatesMutex.Unlock()
|
||||
if dm.imageUpdatesRunning {
|
||||
return
|
||||
}
|
||||
if dm.imageUpdates == nil {
|
||||
dm.imageUpdates = make(map[string]*imageUpdateStatus)
|
||||
}
|
||||
active := make(map[string]struct{}, len(containers))
|
||||
pending := make(map[string]*imageUpdateStatus)
|
||||
for _, ctr := range containers {
|
||||
if len(ctr.Names) > 0 && dm.shouldExcludeContainer(ctr.Names[0][1:]) {
|
||||
continue
|
||||
}
|
||||
key := normalizedImageReference(ctr.Image)
|
||||
if key == "" {
|
||||
continue
|
||||
}
|
||||
active[key] = struct{}{}
|
||||
entry := dm.imageUpdates[key]
|
||||
if entry == nil {
|
||||
entry = &imageUpdateStatus{}
|
||||
dm.imageUpdates[key] = entry
|
||||
}
|
||||
if entry.checkedAt.IsZero() || now.Sub(entry.checkedAt) >= imageUpdateInterval {
|
||||
pending[key] = entry
|
||||
}
|
||||
}
|
||||
for key := range dm.imageUpdates {
|
||||
if _, ok := active[key]; !ok {
|
||||
delete(dm.imageUpdates, key)
|
||||
}
|
||||
}
|
||||
if len(pending) == 0 {
|
||||
return
|
||||
}
|
||||
dm.imageUpdatesRunning = true
|
||||
go func() {
|
||||
// Limit auxiliary requests even on hosts running many different images.
|
||||
sem := make(chan struct{}, 2)
|
||||
var wg sync.WaitGroup
|
||||
for key, entry := range pending {
|
||||
sem <- struct{}{}
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
defer func() { <-sem }()
|
||||
available, err := dm.checkImageUpdate(key)
|
||||
if err != nil {
|
||||
available = false
|
||||
slog.Debug("Image update check failed", "image", key, "err", err)
|
||||
}
|
||||
dm.imageUpdatesMutex.Lock()
|
||||
entry.available = available
|
||||
entry.checkedAt = time.Now()
|
||||
dm.imageUpdatesMutex.Unlock()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
dm.imageUpdatesMutex.Lock()
|
||||
dm.imageUpdatesRunning = false
|
||||
dm.imageUpdatesMutex.Unlock()
|
||||
}()
|
||||
}
|
||||
|
||||
func (dm *dockerManager) cachedImageUpdate(image string) bool {
|
||||
key := normalizedImageReference(image)
|
||||
dm.imageUpdatesMutex.RLock()
|
||||
defer dm.imageUpdatesMutex.RUnlock()
|
||||
entry := dm.imageUpdates[key]
|
||||
return entry != nil && entry.available
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
//go:build testing
|
||||
|
||||
package agent
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/fxamacker/cbor/v2"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/henrygd/beszel/internal/entities/container"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func waitForImageUpdates(t *testing.T, dm *dockerManager) {
|
||||
t.Helper()
|
||||
require.Eventually(t, func() bool {
|
||||
dm.imageUpdatesMutex.RLock()
|
||||
defer dm.imageUpdatesMutex.RUnlock()
|
||||
return !dm.imageUpdatesRunning
|
||||
}, time.Second*3, time.Millisecond)
|
||||
}
|
||||
|
||||
func TestImageUpdateCacheAndStats(t *testing.T) {
|
||||
local := "sha256:" + strings.Repeat("a", 64)
|
||||
remote := "sha256:" + strings.Repeat("b", 64)
|
||||
var inspections, lookups atomic.Int32
|
||||
var fail atomic.Bool
|
||||
var upToDate atomic.Bool
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch {
|
||||
case strings.HasPrefix(r.URL.Path, "/images/"):
|
||||
inspections.Add(1)
|
||||
fmt.Fprintf(w, `{"RepoDigests":["docker.io/library/nginx@%s"]}`, local)
|
||||
case r.URL.Path == "/containers/json":
|
||||
fmt.Fprint(w, `[{"Id":"aaaaaaaaaaaa","Names":["/one"],"Image":"nginx","Status":"Up 2 hours"},{"Id":"bbbbbbbbbbbb","Names":["/two"],"Image":"docker.io/library/nginx:latest","Status":"Up 2 hours"}]`)
|
||||
case strings.Contains(r.URL.Path, "/stats"):
|
||||
fmt.Fprint(w, `{"memory_stats":{"usage":1048576},"cpu_stats":{},"networks":{}}`)
|
||||
default:
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
dm := newDockerManagerForVersionTest(server)
|
||||
dm.dockerVersionChecked = true
|
||||
dm.registryClient = &http.Client{Timeout: time.Second, Transport: roundTripFunc(func(r *http.Request) (*http.Response, error) {
|
||||
if fail.Load() {
|
||||
return nil, fmt.Errorf("registry unavailable")
|
||||
}
|
||||
response := &http.Response{StatusCode: 200, Header: make(http.Header), Body: io.NopCloser(strings.NewReader(`{"token":"test"}`))}
|
||||
if r.Method == http.MethodHead {
|
||||
lookups.Add(1)
|
||||
digest := remote
|
||||
if upToDate.Load() {
|
||||
digest = local
|
||||
}
|
||||
response.Header.Set("Docker-Content-Digest", digest)
|
||||
}
|
||||
return response, nil
|
||||
})}
|
||||
stats, err := dm.getDockerStats(defaultCacheTimeMs)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, stats, 2)
|
||||
waitForImageUpdates(t, dm)
|
||||
require.EqualValues(t, 1, lookups.Load())
|
||||
require.EqualValues(t, 1, inspections.Load())
|
||||
stats, err = dm.getDockerStats(defaultCacheTimeMs)
|
||||
require.NoError(t, err)
|
||||
for _, stat := range stats {
|
||||
require.True(t, stat.UpdateAvailable)
|
||||
if stat.Id == "aaaaaaaaaaaa" {
|
||||
require.Equal(t, "nginx", stat.Image)
|
||||
} else {
|
||||
require.Equal(t, "docker.io/library/nginx:latest", stat.Image)
|
||||
}
|
||||
}
|
||||
require.EqualValues(t, 1, lookups.Load())
|
||||
|
||||
expire := func() {
|
||||
dm.imageUpdatesMutex.Lock()
|
||||
dm.imageUpdates["docker.io/library/nginx:latest"].checkedAt = time.Now().Add(-imageUpdateInterval)
|
||||
dm.imageUpdatesMutex.Unlock()
|
||||
}
|
||||
upToDate.Store(true)
|
||||
expire()
|
||||
_, err = dm.getDockerStats(defaultCacheTimeMs)
|
||||
require.NoError(t, err)
|
||||
waitForImageUpdates(t, dm)
|
||||
require.EqualValues(t, 2, lookups.Load())
|
||||
require.False(t, dm.cachedImageUpdate("nginx:latest"))
|
||||
|
||||
// An expired positive result is cleared on failure, and the failure itself
|
||||
// is cached so realtime stats do not retry a broken registry every second.
|
||||
dm.imageUpdatesMutex.Lock()
|
||||
dm.imageUpdates["docker.io/library/nginx:latest"].available = true
|
||||
dm.imageUpdatesMutex.Unlock()
|
||||
fail.Store(true)
|
||||
expire()
|
||||
_, err = dm.getDockerStats(defaultCacheTimeMs)
|
||||
require.NoError(t, err)
|
||||
waitForImageUpdates(t, dm)
|
||||
failedInspections := inspections.Load()
|
||||
stats, err = dm.getDockerStats(defaultCacheTimeMs)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, stats, 2)
|
||||
require.Equal(t, failedInspections, inspections.Load())
|
||||
for _, stat := range stats {
|
||||
require.False(t, stat.UpdateAvailable)
|
||||
require.Equal(t, 1.0, stat.Mem)
|
||||
}
|
||||
}
|
||||
|
||||
func TestImageDiscoveryDoesNotBlockStats(t *testing.T) {
|
||||
started := make(chan struct{}, 1)
|
||||
release := make(chan struct{})
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if strings.HasPrefix(r.URL.Path, "/images/") {
|
||||
fmt.Fprintf(w, `{"RepoDigests":["example.com/app@sha256:%s"]}`, strings.Repeat("a", 64))
|
||||
} else {
|
||||
fmt.Fprint(w, `{"memory_stats":{"usage":1048576}}`)
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
dm := newDockerManagerForVersionTest(server)
|
||||
defer func() { close(release); waitForImageUpdates(t, dm) }()
|
||||
dm.registryClient = &http.Client{Transport: roundTripFunc(func(r *http.Request) (*http.Response, error) {
|
||||
started <- struct{}{}
|
||||
<-release
|
||||
return nil, fmt.Errorf("timeout")
|
||||
})}
|
||||
ctr := &container.ApiInfo{IdShort: "aaaaaaaaaaaa", Image: "example.com/app", Names: []string{"/one"}}
|
||||
dm.refreshImageUpdates([]*container.ApiInfo{ctr}, time.Now())
|
||||
select {
|
||||
case <-started:
|
||||
case <-time.After(3 * time.Second):
|
||||
t.Fatal("check did not start")
|
||||
}
|
||||
done := make(chan error, 1)
|
||||
go func() { done <- dm.updateContainerStats(ctr, defaultCacheTimeMs) }()
|
||||
select {
|
||||
case err := <-done:
|
||||
require.NoError(t, err)
|
||||
case <-time.After(time.Second):
|
||||
t.Fatal("registry blocked stats")
|
||||
}
|
||||
dm.imageUpdatesMutex.RLock()
|
||||
require.True(t, dm.imageUpdatesRunning)
|
||||
dm.imageUpdatesMutex.RUnlock()
|
||||
}
|
||||
|
||||
func TestNormalizeImageUpdateReferences(t *testing.T) {
|
||||
require.Equal(t, normalizedImageReference("nginx"), normalizedImageReference("docker.io/library/nginx:latest"))
|
||||
require.Empty(t, normalizedImageReference("bad reference"))
|
||||
require.Empty(t, normalizedImageReference("nginx@sha256:"+strings.Repeat("a", 64)))
|
||||
}
|
||||
|
||||
// A stats request can return headers promptly and then stall while reading its
|
||||
// body. The stats-map mutex must remain available during that read.
|
||||
func TestStatsResponseBodyDoesNotHoldStatsLock(t *testing.T) {
|
||||
started := make(chan struct{})
|
||||
release := make(chan struct{})
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.(http.Flusher).Flush()
|
||||
close(started)
|
||||
<-release
|
||||
fmt.Fprint(w, `{"memory_stats":{"usage":1048576}}`)
|
||||
}))
|
||||
defer server.Close()
|
||||
dm := newDockerManagerForVersionTest(server)
|
||||
done := make(chan error, 1)
|
||||
go func() {
|
||||
done <- dm.updateContainerStats(&container.ApiInfo{IdShort: "aaaaaaaaaaaa", Names: []string{"/one"}, Image: "nginx"}, defaultCacheTimeMs)
|
||||
}()
|
||||
<-started
|
||||
locked := make(chan struct{})
|
||||
go func() { dm.containerStatsMutex.Lock(); dm.containerStatsMutex.Unlock(); close(locked) }()
|
||||
select {
|
||||
case <-locked:
|
||||
case <-time.After(time.Second):
|
||||
close(release)
|
||||
<-done
|
||||
t.Fatal("Docker response body held the stats mutex")
|
||||
}
|
||||
close(release)
|
||||
require.NoError(t, <-done)
|
||||
}
|
||||
|
||||
func TestImageUpdateStatsEncoding(t *testing.T) {
|
||||
original := container.Stats{Image: "nginx:latest", UpdateAvailable: true}
|
||||
encoded, err := cbor.Marshal(original)
|
||||
require.NoError(t, err)
|
||||
var fields map[int]any
|
||||
require.NoError(t, cbor.Unmarshal(encoded, &fields))
|
||||
require.Equal(t, true, fields[11])
|
||||
require.Equal(t, "nginx:latest", fields[8])
|
||||
var decoded container.Stats
|
||||
require.NoError(t, cbor.Unmarshal(encoded, &decoded))
|
||||
require.True(t, decoded.UpdateAvailable)
|
||||
require.Equal(t, original.Image, decoded.Image)
|
||||
encoded, err = json.Marshal(original)
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, string(encoded), `"u":true`)
|
||||
}
|
||||
|
||||
func TestImageUpdateCacheExpiryBoundaryAndPruning(t *testing.T) {
|
||||
now := time.Now()
|
||||
key := normalizedImageReference("nginx")
|
||||
dm := &dockerManager{imageUpdates: map[string]*imageUpdateStatus{
|
||||
key: {available: true, checkedAt: now},
|
||||
"unused.example/image:latest": {checkedAt: now},
|
||||
}}
|
||||
dm.refreshImageUpdates([]*container.ApiInfo{{Image: "nginx"}}, now.Add(imageUpdateInterval-time.Nanosecond))
|
||||
require.False(t, dm.imageUpdatesRunning)
|
||||
require.Len(t, dm.imageUpdates, 1)
|
||||
require.True(t, dm.cachedImageUpdate("nginx:latest"))
|
||||
dm.refreshImageUpdates(nil, now)
|
||||
require.Empty(t, dm.imageUpdates)
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
_ "crypto/sha256"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/distribution/reference"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
const imageRegistryTimeout = 10 * time.Second
|
||||
|
||||
const imageManifestAccept = "application/vnd.docker.distribution.manifest.list.v2+json, " +
|
||||
"application/vnd.docker.distribution.manifest.v2+json, " +
|
||||
"application/vnd.oci.image.manifest.v1+json, " +
|
||||
"application/vnd.oci.image.index.v1+json"
|
||||
|
||||
// checkImageUpdate compares the digest recorded by Docker for image with the
|
||||
// digest currently advertised by its registry. A digest-pinned reference is
|
||||
// immutable and therefore never has an update available.
|
||||
func (dm *dockerManager) checkImageUpdate(image string) (bool, error) {
|
||||
named, err := reference.ParseNormalizedNamed(image)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("parse image reference %q: %w", image, err)
|
||||
}
|
||||
if _, pinned := named.(reference.Digested); pinned {
|
||||
return false, nil
|
||||
}
|
||||
named = reference.TagNameOnly(named)
|
||||
|
||||
registry := reference.Domain(named)
|
||||
repository := reference.Path(named)
|
||||
tag := named.(reference.Tagged).Tag()
|
||||
|
||||
localDigest, err := dm.inspectImageDigest(image, registry, repository)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
remoteDigest, err := dm.registryImageDigest(registry, repository, tag)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return remoteDigest != localDigest, nil
|
||||
}
|
||||
|
||||
// inspectImageDigest reads Docker's image metadata without using dm.decode.
|
||||
// The checker runs in the image-discovery goroutine, so it must not hold any
|
||||
// of the container statistics locks while waiting on the Docker API.
|
||||
func (dm *dockerManager) inspectImageDigest(image, registry, repository string) (string, error) {
|
||||
if dm.client == nil {
|
||||
return "", fmt.Errorf("inspect image %q: Docker client is unavailable", image)
|
||||
}
|
||||
|
||||
endpoint := "http://localhost/images/" + url.PathEscape(image) + "/json"
|
||||
resp, err := dm.client.Get(endpoint)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("inspect image %q: %w", image, err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return "", fmt.Errorf("inspect image %q failed: %s", image, responseStatus(resp))
|
||||
}
|
||||
|
||||
var inspect struct {
|
||||
RepoDigests []string `json:"RepoDigests"`
|
||||
}
|
||||
if err := json.NewDecoder(resp.Body).Decode(&inspect); err != nil {
|
||||
return "", fmt.Errorf("decode image inspect %q: %w", image, err)
|
||||
}
|
||||
if len(inspect.RepoDigests) == 0 {
|
||||
return "", fmt.Errorf("inspect image %q returned no repository digests", image)
|
||||
}
|
||||
|
||||
localDigest, ok := matchingRepositoryDigest(inspect.RepoDigests, registry, repository)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("inspect image %q returned no valid digest for %s/%s", image, registry, repository)
|
||||
}
|
||||
return localDigest, nil
|
||||
}
|
||||
|
||||
// matchingRepositoryDigest returns a valid digest belonging to the requested
|
||||
// repository. Docker can return multiple RepoDigests for one local image; an
|
||||
// unrelated first entry must never be used for the comparison.
|
||||
func matchingRepositoryDigest(repoDigests []string, registry, repository string) (string, bool) {
|
||||
for _, repoDigest := range repoDigests {
|
||||
repoDigest = strings.TrimSpace(repoDigest)
|
||||
at := strings.LastIndexByte(repoDigest, '@')
|
||||
if at <= 0 || at == len(repoDigest)-1 || strings.Contains(repoDigest[:at], "@") {
|
||||
continue
|
||||
}
|
||||
|
||||
repoRef, err := reference.ParseNormalizedNamed(repoDigest[:at])
|
||||
if err != nil || reference.Path(repoRef) != repository || !sameRegistry(reference.Domain(repoRef), registry) {
|
||||
continue
|
||||
}
|
||||
if _, hasTag := repoRef.(reference.Tagged); hasTag {
|
||||
continue
|
||||
}
|
||||
|
||||
d, err := digest.Parse(repoDigest[at+1:])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
return d.String(), true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
func sameRegistry(left, right string) bool {
|
||||
left = canonicalRegistry(left)
|
||||
right = canonicalRegistry(right)
|
||||
return left == right ||
|
||||
(left == "ghcr.io" && right == "lscr.io") ||
|
||||
(left == "lscr.io" && right == "ghcr.io")
|
||||
}
|
||||
|
||||
func canonicalRegistry(registry string) string {
|
||||
if registry == "index.docker.io" {
|
||||
return "docker.io"
|
||||
}
|
||||
return registry
|
||||
}
|
||||
|
||||
func (dm *dockerManager) registryImageDigest(registry, repository, tag string) (string, error) {
|
||||
client := dm.registryClient
|
||||
if client == nil {
|
||||
client = &http.Client{Timeout: imageRegistryTimeout}
|
||||
}
|
||||
|
||||
token, err := dm.registryToken(client, registry, repository)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
host := registry
|
||||
if registry == "docker.io" {
|
||||
host = "registry-1.docker.io"
|
||||
}
|
||||
manifestURL := "https://" + host + "/v2/" + repository + "/manifests/" + url.PathEscape(tag)
|
||||
req, err := http.NewRequest(http.MethodHead, manifestURL, nil)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("create manifest request: %w", err)
|
||||
}
|
||||
req.Header.Set("Accept", imageManifestAccept)
|
||||
if token != "" {
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("fetch manifest %s:%s: %w", registry, repository, err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return "", fmt.Errorf("manifest request for %s:%s failed: %s", repository, tag, responseStatus(resp))
|
||||
}
|
||||
|
||||
remote := strings.TrimSpace(resp.Header.Get("Docker-Content-Digest"))
|
||||
d, err := digest.Parse(remote)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("manifest request for %s:%s returned invalid digest: %w", repository, tag, err)
|
||||
}
|
||||
return d.String(), nil
|
||||
}
|
||||
|
||||
func (dm *dockerManager) registryToken(client *http.Client, registry, repository string) (string, error) {
|
||||
var authURL string
|
||||
switch registry {
|
||||
case "docker.io":
|
||||
authURL = "https://auth.docker.io/token?service=registry.docker.io&scope=" + url.QueryEscape("repository:"+repository+":pull")
|
||||
case "ghcr.io", "lscr.io":
|
||||
// lscr.io is the LinuxServer alias for its GHCR-backed images.
|
||||
authURL = "https://ghcr.io/token?service=ghcr.io&scope=" + url.QueryEscape("repository:"+repository+":pull")
|
||||
default:
|
||||
// Anonymous registries remain supported, as they were before the
|
||||
// authenticated Docker Hub and GHCR paths were added.
|
||||
return "", nil
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, authURL, nil)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("create registry auth request: %w", err)
|
||||
}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("fetch registry auth token for %s: %w", repository, err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return "", fmt.Errorf("registry auth request for %s failed: %s", repository, responseStatus(resp))
|
||||
}
|
||||
|
||||
var tokenResponse struct {
|
||||
Token string `json:"token"`
|
||||
AccessToken string `json:"access_token"`
|
||||
}
|
||||
if err := json.NewDecoder(resp.Body).Decode(&tokenResponse); err != nil {
|
||||
return "", fmt.Errorf("decode registry auth response for %s: %w", repository, err)
|
||||
}
|
||||
token := strings.TrimSpace(tokenResponse.Token)
|
||||
if token == "" {
|
||||
token = strings.TrimSpace(tokenResponse.AccessToken)
|
||||
}
|
||||
if token == "" {
|
||||
return "", fmt.Errorf("registry auth response for %s contained no token", repository)
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func responseStatus(resp *http.Response) string {
|
||||
if resp.Status != "" {
|
||||
return resp.Status
|
||||
}
|
||||
return http.StatusText(resp.StatusCode)
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
//go:build testing
|
||||
|
||||
package agent
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type registryTransportFunc func(*http.Request) (*http.Response, error)
|
||||
|
||||
func (fn registryTransportFunc) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
return fn(req)
|
||||
}
|
||||
|
||||
func registryResponse(status int, body string) *http.Response {
|
||||
return &http.Response{
|
||||
StatusCode: status,
|
||||
Status: fmt.Sprintf("%d %s", status, http.StatusText(status)),
|
||||
Header: make(http.Header),
|
||||
Body: io.NopCloser(strings.NewReader(body)),
|
||||
}
|
||||
}
|
||||
|
||||
func registryDigest(fill byte) string {
|
||||
return "sha256:" + strings.Repeat(string(fill), 64)
|
||||
}
|
||||
|
||||
func newRegistryChecker(t *testing.T, inspectBody string, transport http.RoundTripper) *dockerManager {
|
||||
t.Helper()
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if strings.HasPrefix(r.URL.Path, "/images/") {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, _ = io.WriteString(w, inspectBody)
|
||||
return
|
||||
}
|
||||
http.NotFound(w, r)
|
||||
}))
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
return &dockerManager{
|
||||
client: newDockerManagerForVersionTest(server).client,
|
||||
registryClient: &http.Client{Transport: transport},
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckImageUpdateUsesInspectAndManifestDigests(t *testing.T) {
|
||||
local := registryDigest('a')
|
||||
remote := registryDigest('b')
|
||||
var authCalls, manifestCalls atomic.Int32
|
||||
dm := newRegistryChecker(t, fmt.Sprintf(`{"RepoDigests":["docker.io/library/alpine@%s"]}`, local), registryTransportFunc(func(req *http.Request) (*http.Response, error) {
|
||||
switch {
|
||||
case req.Method == http.MethodGet && req.URL.Host == "auth.docker.io":
|
||||
authCalls.Add(1)
|
||||
require.Equal(t, "/token", req.URL.Path)
|
||||
return registryResponse(http.StatusOK, `{"token":"test-token"}`), nil
|
||||
case req.Method == http.MethodHead && req.URL.Host == "registry-1.docker.io":
|
||||
manifestCalls.Add(1)
|
||||
require.Equal(t, "/v2/library/alpine/manifests/latest", req.URL.Path)
|
||||
require.Equal(t, "Bearer test-token", req.Header.Get("Authorization"))
|
||||
resp := registryResponse(http.StatusOK, "")
|
||||
resp.Header.Set("Docker-Content-Digest", remote)
|
||||
return resp, nil
|
||||
default:
|
||||
return registryResponse(http.StatusNotFound, ""), nil
|
||||
}
|
||||
}))
|
||||
|
||||
available, err := dm.checkImageUpdate("alpine")
|
||||
require.NoError(t, err)
|
||||
require.True(t, available)
|
||||
require.EqualValues(t, 1, authCalls.Load())
|
||||
require.EqualValues(t, 1, manifestCalls.Load())
|
||||
}
|
||||
|
||||
func TestCheckImageUpdateReportsUnknownInspectState(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
body string
|
||||
}{
|
||||
{name: "missing field", body: `{}`},
|
||||
{name: "empty field", body: `{"RepoDigests":[]}`},
|
||||
{name: "malformed reference", body: `{"RepoDigests":["not-a-repo-digest"]}`},
|
||||
{name: "wrong repository", body: `{"RepoDigests":["docker.io/library/busybox@` + registryDigest('a') + `"]}`},
|
||||
{name: "malformed digest", body: `{"RepoDigests":["docker.io/library/alpine@sha256:not-a-digest"]}`},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
var registryCalls atomic.Int32
|
||||
dm := newRegistryChecker(t, test.body, registryTransportFunc(func(req *http.Request) (*http.Response, error) {
|
||||
registryCalls.Add(1)
|
||||
return registryResponse(http.StatusOK, `{"token":"unexpected"}`), nil
|
||||
}))
|
||||
|
||||
available, err := dm.checkImageUpdate("alpine")
|
||||
require.Error(t, err)
|
||||
require.False(t, available)
|
||||
require.EqualValues(t, 0, registryCalls.Load(), "invalid local state must not query a registry")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckImageUpdateChecksInspectAuthAndManifestStatuses(t *testing.T) {
|
||||
local := registryDigest('a')
|
||||
validInspect := fmt.Sprintf(`{"RepoDigests":["docker.io/library/alpine@%s"]}`, local)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
inspectCode int
|
||||
authCode int
|
||||
manifestCode int
|
||||
remote string
|
||||
want string
|
||||
}{
|
||||
{name: "inspect status", inspectCode: http.StatusNotFound, want: "inspect image"},
|
||||
{name: "auth status", inspectCode: http.StatusOK, authCode: http.StatusUnauthorized, want: "registry auth"},
|
||||
{name: "manifest status", inspectCode: http.StatusOK, authCode: http.StatusOK, manifestCode: http.StatusNotFound, remote: local, want: "manifest request"},
|
||||
{name: "missing digest", inspectCode: http.StatusOK, authCode: http.StatusOK, manifestCode: http.StatusOK, want: "invalid digest"},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if test.inspectCode != http.StatusOK && strings.HasPrefix(r.URL.Path, "/images/") {
|
||||
w.WriteHeader(test.inspectCode)
|
||||
return
|
||||
}
|
||||
_, _ = io.WriteString(w, validInspect)
|
||||
}))
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
calls := 0
|
||||
dm := &dockerManager{client: newDockerManagerForVersionTest(server).client, registryClient: &http.Client{Transport: registryTransportFunc(func(req *http.Request) (*http.Response, error) {
|
||||
calls++
|
||||
if req.Method == http.MethodGet {
|
||||
return registryResponse(test.authCode, `{"token":"test"}`), nil
|
||||
}
|
||||
response := registryResponse(test.manifestCode, "")
|
||||
response.Header.Set("Docker-Content-Digest", test.remote)
|
||||
return response, nil
|
||||
})}}
|
||||
|
||||
_, err := dm.checkImageUpdate("alpine")
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), test.want)
|
||||
if test.inspectCode != http.StatusOK {
|
||||
require.Zero(t, calls)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckImageUpdateSupportsAnonymousAndLSCRRegistries(t *testing.T) {
|
||||
t.Run("anonymous registry", func(t *testing.T) {
|
||||
local := registryDigest('a')
|
||||
var calls atomic.Int32
|
||||
dm := newRegistryChecker(t, fmt.Sprintf(`{"RepoDigests":["example.com/app@%s"]}`, local), registryTransportFunc(func(req *http.Request) (*http.Response, error) {
|
||||
calls.Add(1)
|
||||
require.Equal(t, http.MethodHead, req.Method)
|
||||
require.Equal(t, "example.com", req.URL.Host)
|
||||
resp := registryResponse(http.StatusOK, "")
|
||||
resp.Header.Set("Docker-Content-Digest", local)
|
||||
return resp, nil
|
||||
}))
|
||||
available, err := dm.checkImageUpdate("example.com/app")
|
||||
require.NoError(t, err)
|
||||
require.False(t, available)
|
||||
require.EqualValues(t, 1, calls.Load())
|
||||
})
|
||||
|
||||
t.Run("lscr ghcr alias", func(t *testing.T) {
|
||||
local := registryDigest('a')
|
||||
var authCalls, manifestCalls atomic.Int32
|
||||
dm := newRegistryChecker(t, fmt.Sprintf(`{"RepoDigests":["ghcr.io/linuxserver/app@%s"]}`, local), registryTransportFunc(func(req *http.Request) (*http.Response, error) {
|
||||
if req.Method == http.MethodGet {
|
||||
authCalls.Add(1)
|
||||
return registryResponse(http.StatusOK, `{"token":"test"}`), nil
|
||||
}
|
||||
manifestCalls.Add(1)
|
||||
require.Equal(t, "lscr.io", req.URL.Host)
|
||||
resp := registryResponse(http.StatusOK, "")
|
||||
resp.Header.Set("Docker-Content-Digest", local)
|
||||
return resp, nil
|
||||
}))
|
||||
available, err := dm.checkImageUpdate("lscr.io/linuxserver/app")
|
||||
require.NoError(t, err)
|
||||
require.False(t, available)
|
||||
require.EqualValues(t, 1, authCalls.Load())
|
||||
require.EqualValues(t, 1, manifestCalls.Load())
|
||||
})
|
||||
}
|
||||
|
||||
func TestCheckImageUpdateSkipsPinnedDigest(t *testing.T) {
|
||||
image := "docker.io/library/alpine@" + registryDigest('a')
|
||||
dm := &dockerManager{}
|
||||
available, err := dm.checkImageUpdate(image)
|
||||
require.NoError(t, err)
|
||||
require.False(t, available)
|
||||
}
|
||||
@@ -1184,7 +1184,6 @@ func TestUpdateContainerStatsPodmanCpuCalculation(t *testing.T) {
|
||||
}
|
||||
})},
|
||||
containerStatsMap: make(map[string]*container.Stats),
|
||||
apiStats: &container.ApiStats{},
|
||||
usingPodman: true,
|
||||
lastCpuContainer: map[uint16]map[string]uint64{
|
||||
defaultCacheTimeMs: {"0123456789ab": prevCpuUsage},
|
||||
@@ -1676,7 +1675,6 @@ func TestUpdateContainerStatsUsesPodmanInspectHealthFallback(t *testing.T) {
|
||||
}
|
||||
})},
|
||||
containerStatsMap: make(map[string]*container.Stats),
|
||||
apiStats: &container.ApiStats{},
|
||||
usingPodman: true,
|
||||
lastCpuContainer: make(map[uint16]map[string]uint64),
|
||||
lastCpuSystem: make(map[uint16]map[string]uint64),
|
||||
|
||||
+2
-2
@@ -186,14 +186,14 @@ func (h *GetSmartDataHandler) Handle(hctx *HandlerContext) error {
|
||||
type GetZfsDataHandler struct{}
|
||||
|
||||
func (h *GetZfsDataHandler) Handle(hctx *HandlerContext) error {
|
||||
if hctx.Agent.zfsManager == nil {
|
||||
if hctx.Agent.storagePoolManager == nil {
|
||||
return hctx.SendResponse(nil, hctx.RequestID)
|
||||
}
|
||||
var req common.ZfsDataRequest
|
||||
if err := cbor.Unmarshal(hctx.Request.Data, &req); err != nil {
|
||||
return err
|
||||
}
|
||||
return hctx.SendResponse(hctx.Agent.zfsManager.GetDetail(req.Force), hctx.RequestID)
|
||||
return hctx.SendResponse(hctx.Agent.storagePoolManager.GetDetail(req.Force), hctx.RequestID)
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -34,19 +34,19 @@ func TestNewAgentResponseSmartData(t *testing.T) {
|
||||
|
||||
func TestGetZfsDataHandlerForceRefresh(t *testing.T) {
|
||||
poolCalls := 0
|
||||
zm := &ZfsManager{detailInterval: time.Hour}
|
||||
zm.poolStatsFn = func() ([]zfs.PoolStat, error) {
|
||||
zm := &StoragePoolManager{detailInterval: time.Hour, backends: []*poolBackend{{name: "zfs"}}}
|
||||
zm.backends[0].poolStatsFn = func() ([]zfs.PoolStat, error) {
|
||||
poolCalls++
|
||||
return []zfs.PoolStat{{Name: "tank", Alloc: uint64(poolCalls)}}, nil
|
||||
}
|
||||
zm.poolStatusesFn = func() ([]zfs.PoolStatus, error) { return nil, nil }
|
||||
zm.datasetsFn = func() ([]zfs.Dataset, error) { return nil, nil }
|
||||
zm.backends[0].poolStatusesFn = func() ([]zfs.PoolStatus, error) { return nil, nil }
|
||||
zm.backends[0].datasetsFn = func() ([]zfs.Dataset, error) { return nil, nil }
|
||||
zm.GetDetail(false)
|
||||
|
||||
requestData, err := cbor.Marshal(common.ZfsDataRequest{Force: true})
|
||||
assert.NoError(t, err)
|
||||
ctx := &HandlerContext{
|
||||
Agent: &Agent{zfsManager: zm},
|
||||
Agent: &Agent{storagePoolManager: zm},
|
||||
Request: &common.HubRequest[cbor.RawMessage]{
|
||||
Action: common.GetZfsData,
|
||||
Data: requestData,
|
||||
|
||||
@@ -0,0 +1,462 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log/slog"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/henrygd/beszel/agent/btrfs"
|
||||
"github.com/henrygd/beszel/agent/zfs"
|
||||
"github.com/henrygd/beszel/internal/entities/system"
|
||||
zfsentity "github.com/henrygd/beszel/internal/entities/zfs"
|
||||
)
|
||||
|
||||
// zfsDatasetUsage holds usage values for a ZFS dataset mountpoint.
|
||||
type zfsDatasetUsage struct {
|
||||
used uint64
|
||||
avail uint64
|
||||
}
|
||||
|
||||
// datasetUsageRefreshInterval controls how often `zfs list` is re-run for the
|
||||
// mountpoint usage map. Dataset inventory changes rarely.
|
||||
const datasetUsageRefreshInterval = 5 * time.Minute
|
||||
|
||||
// poolStatsRefreshInterval controls how often `zpool list` is re-run for pool
|
||||
// capacity. Health and I/O are read from procfs on Linux, so the utility only
|
||||
// needs to refresh slow-moving space accounting.
|
||||
const poolStatsRefreshInterval = time.Minute
|
||||
|
||||
// btrfsFilesystems is the btrfs source; overridable in tests.
|
||||
var btrfsFilesystems = btrfs.Filesystems
|
||||
|
||||
type poolKernelSample struct {
|
||||
nread uint64
|
||||
nwrite uint64
|
||||
at time.Time
|
||||
}
|
||||
|
||||
// StoragePoolManager combines independent backend inventories. Metrics and
|
||||
// dataset usage require the agent lock; GetDetail is safe for concurrent calls.
|
||||
type StoragePoolManager struct {
|
||||
backends []*poolBackend
|
||||
detailInterval time.Duration
|
||||
}
|
||||
|
||||
// poolBackend owns one backend's collectors and caches. Collector functions
|
||||
// are immutable after construction and may run concurrently for metrics/details.
|
||||
type poolBackend struct {
|
||||
name string
|
||||
poolStatsFn func() ([]zfs.PoolStat, error) // capacity/health source
|
||||
datasetsFn func() ([]zfs.Dataset, error) // dataset inventory source
|
||||
kernelStatsFn func() ([]zfs.PoolKernelStat, error) // procfs pool state/I/O source
|
||||
poolStatusesFn func() ([]zfs.PoolStatus, error) // scrub/vdev detail source
|
||||
|
||||
poolData []zfs.PoolStat // cached pool inventory (TTL below)
|
||||
lastPoolStats time.Time
|
||||
kernelSamples map[string]poolKernelSample
|
||||
|
||||
datasetUsage map[string]zfsDatasetUsage // mountpoint -> usage
|
||||
lastUsageRefresh time.Time
|
||||
|
||||
// Detail data (pools, vdevs, scrub, datasets) is cached and refreshed on
|
||||
// an interval. Accessed from handler goroutines, so it is mutex-protected.
|
||||
detailMu sync.Mutex
|
||||
detail *zfsentity.ZfsData
|
||||
lastDetailRefresh time.Time
|
||||
detailFailed bool
|
||||
}
|
||||
|
||||
func newStoragePoolManager() *StoragePoolManager {
|
||||
return &StoragePoolManager{
|
||||
backends: []*poolBackend{newZfsBackend(), newBtrfsBackend()},
|
||||
detailInterval: time.Hour,
|
||||
}
|
||||
}
|
||||
|
||||
func newZfsBackend() *poolBackend {
|
||||
return &poolBackend{
|
||||
name: "zfs",
|
||||
poolStatsFn: optionalPoolSource(zfs.PoolStats),
|
||||
datasetsFn: zfs.Datasets,
|
||||
kernelStatsFn: optionalPoolSource(zfs.PoolKernelStats),
|
||||
poolStatusesFn: optionalPoolSource(zfs.PoolStatuses),
|
||||
}
|
||||
}
|
||||
|
||||
func newBtrfsBackend() *poolBackend {
|
||||
return &poolBackend{
|
||||
name: "btrfs",
|
||||
poolStatsFn: btrfsSource(btrfsPoolStats),
|
||||
kernelStatsFn: btrfsSource(btrfsKernelStats),
|
||||
poolStatusesFn: btrfsSource(btrfsPoolStatuses),
|
||||
}
|
||||
}
|
||||
|
||||
// datasets is optional: only backends that expose datasets provide a collector.
|
||||
func (b *poolBackend) datasets() ([]zfs.Dataset, error) {
|
||||
if b.datasetsFn == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return b.datasetsFn()
|
||||
}
|
||||
|
||||
// A missing utility/interface is a successfully observed absent backend.
|
||||
func optionalPoolSource[T any](source func() ([]T, error)) func() ([]T, error) {
|
||||
return func() ([]T, error) {
|
||||
items, err := source()
|
||||
if errors.Is(err, zfs.ErrNoZfs) || errors.Is(err, exec.ErrNotFound) || errors.Is(err, errors.ErrUnsupported) {
|
||||
return nil, nil
|
||||
}
|
||||
return items, err
|
||||
}
|
||||
}
|
||||
|
||||
func btrfsSource[T any](convert func(btrfs.Filesystem) T) func() ([]T, error) {
|
||||
return func() ([]T, error) {
|
||||
filesystems, err := optionalPoolSource(btrfsFilesystems)()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items := make([]T, 0, len(filesystems))
|
||||
for _, fs := range filesystems {
|
||||
items = append(items, convert(fs))
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Update refreshes systemStats.ZfsPools with the latest pool data. I/O
|
||||
// throughput and health come from inexpensive kernel kstats on Linux. Pool
|
||||
// capacity and dataset usage come from separately cached utility calls. The
|
||||
// pool map is empty when both backends are absent.
|
||||
func (m *StoragePoolManager) Update(systemStats *system.Stats) {
|
||||
// Rebuild the combined map so successful pool removals clear old samples.
|
||||
systemStats.ZfsPools = nil
|
||||
for _, backend := range m.backends {
|
||||
backend.updateBackendStats(systemStats)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *poolBackend) updateBackendStats(systemStats *system.Stats) {
|
||||
pools := b.poolStats()
|
||||
if len(pools) == 0 {
|
||||
b.kernelSamples = nil
|
||||
return
|
||||
}
|
||||
|
||||
kernelStats, ioRates := b.kernelStats()
|
||||
|
||||
if systemStats.ZfsPools == nil {
|
||||
systemStats.ZfsPools = make(map[string]*system.ZfsPool, len(pools))
|
||||
}
|
||||
for i := range pools {
|
||||
pool := &pools[i]
|
||||
// Full precision, matching the dataset values below; the frontend
|
||||
// formats any magnitude.
|
||||
stats := &system.ZfsPool{
|
||||
DisplayName: pool.DisplayName,
|
||||
Raw: pool.Raw,
|
||||
Total: float64(pool.Size) / (1024 * 1024 * 1024),
|
||||
Used: float64(pool.Alloc) / (1024 * 1024 * 1024),
|
||||
Health: pool.Health,
|
||||
}
|
||||
if kernel, exists := kernelStats[pool.Name]; exists && kernel.Health != "" {
|
||||
stats.Health = kernel.Health
|
||||
}
|
||||
if io, exists := ioRates[pool.Name]; exists {
|
||||
stats.ReadBytes = io.NRead
|
||||
stats.WriteBytes = io.NWrite
|
||||
}
|
||||
slog.Debug("Storage pool sample", "backend", b.name, "pool", pool.Name, "health", stats.Health, "used_gb", stats.Used, "read_bps", stats.ReadBytes, "write_bps", stats.WriteBytes)
|
||||
systemStats.ZfsPools[pool.Name] = stats
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// poolStats returns the cached pool inventory, calling its collector at most
|
||||
// every poolStatsRefreshInterval. On failure the previous inventory is
|
||||
// retained and the refresh is retried on the next cadence.
|
||||
func (b *poolBackend) poolStats() []zfs.PoolStat {
|
||||
if b.lastPoolStats.IsZero() || time.Since(b.lastPoolStats) >= poolStatsRefreshInterval {
|
||||
pools, err := b.poolStatsFn()
|
||||
if err != nil {
|
||||
slog.Debug("Storage pool stats unavailable", "backend", b.name, "err", err)
|
||||
} else {
|
||||
b.poolData = pools
|
||||
}
|
||||
b.lastPoolStats = time.Now()
|
||||
}
|
||||
return b.poolData
|
||||
}
|
||||
|
||||
// kernelStats reads cumulative pool counters and converts them to per-second
|
||||
// rates. Counter decreases indicate a pool export/import and reset the
|
||||
// baseline instead of producing an underflow spike.
|
||||
func (b *poolBackend) kernelStats() (map[string]zfs.PoolKernelStat, map[string]zfs.PoolIoStats) {
|
||||
if b.kernelStatsFn == nil {
|
||||
return nil, nil
|
||||
}
|
||||
stats, err := b.kernelStatsFn()
|
||||
if err != nil {
|
||||
slog.Debug("Storage pool kernel stats unavailable", "backend", b.name, "err", err)
|
||||
return nil, nil
|
||||
}
|
||||
now := time.Now()
|
||||
byName := make(map[string]zfs.PoolKernelStat, len(stats))
|
||||
rates := make(map[string]zfs.PoolIoStats, len(stats))
|
||||
nextSamples := make(map[string]poolKernelSample, len(stats))
|
||||
for _, stat := range stats {
|
||||
byName[stat.Name] = stat
|
||||
if previous, ok := b.kernelSamples[stat.Name]; ok && now.After(previous.at) &&
|
||||
stat.NRead >= previous.nread && stat.NWrite >= previous.nwrite {
|
||||
seconds := now.Sub(previous.at).Seconds()
|
||||
rates[stat.Name] = zfs.PoolIoStats{
|
||||
NRead: uint64(float64(stat.NRead-previous.nread) / seconds),
|
||||
NWrite: uint64(float64(stat.NWrite-previous.nwrite) / seconds),
|
||||
}
|
||||
}
|
||||
nextSamples[stat.Name] = poolKernelSample{nread: stat.NRead, nwrite: stat.NWrite, at: now}
|
||||
}
|
||||
b.kernelSamples = nextSamples
|
||||
return byName, rates
|
||||
}
|
||||
|
||||
// refreshDatasetUsage re-runs `zfs list` when the refresh window has elapsed
|
||||
// and rebuilds the mountpoint-keyed usage map.
|
||||
func (b *poolBackend) refreshDatasetUsage() {
|
||||
if !b.lastUsageRefresh.IsZero() && time.Since(b.lastUsageRefresh) < datasetUsageRefreshInterval {
|
||||
return
|
||||
}
|
||||
datasets, err := b.datasets()
|
||||
if err != nil {
|
||||
slog.Debug("Storage pool dataset usage unavailable", "backend", b.name, "err", err)
|
||||
} else {
|
||||
usage := make(map[string]zfsDatasetUsage, len(datasets))
|
||||
for _, ds := range datasets {
|
||||
if ds.Mountpoint != "" && ds.Mountpoint != "-" {
|
||||
usage[ds.Mountpoint] = zfsDatasetUsage{used: ds.Used, avail: ds.Avail}
|
||||
}
|
||||
}
|
||||
b.datasetUsage = usage
|
||||
}
|
||||
b.lastUsageRefresh = time.Now()
|
||||
}
|
||||
|
||||
// DatasetUsage returns ZFS dataset usage keyed by mountpoint, refreshed at
|
||||
// most every datasetUsageRefreshInterval. On failure the previous map is
|
||||
// retained and a debug log is emitted.
|
||||
func (m *StoragePoolManager) DatasetUsage() map[string]zfsDatasetUsage {
|
||||
for _, backend := range m.backends {
|
||||
if backend.name == "zfs" {
|
||||
backend.refreshDatasetUsage()
|
||||
return backend.datasetUsage
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetDetail combines backend snapshots, identifying successful inventories so
|
||||
// the hub can accept partial updates without deleting failed backend records.
|
||||
func (m *StoragePoolManager) GetDetail(force bool) *zfsentity.ZfsData {
|
||||
data := &zfsentity.ZfsData{Complete: true}
|
||||
for _, backend := range m.backends {
|
||||
snapshot := backend.getBackendDetail(force, m.detailInterval)
|
||||
data.Pools = append(data.Pools, snapshot.Pools...)
|
||||
if snapshot.Complete {
|
||||
data.CompleteBackends = append(data.CompleteBackends, backend.name)
|
||||
} else {
|
||||
data.Complete = false
|
||||
}
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func (b *poolBackend) getBackendDetail(force bool, interval time.Duration) *zfsentity.ZfsData {
|
||||
b.detailMu.Lock()
|
||||
defer b.detailMu.Unlock()
|
||||
|
||||
if force || b.detailFailed || b.detail == nil || time.Since(b.lastDetailRefresh) >= interval {
|
||||
if data, err := b.collectDetail(b.detail); err != nil {
|
||||
b.detailFailed = true
|
||||
slog.Debug("Storage pool detail collection failed", "backend", b.name, "err", err)
|
||||
if b.detail == nil {
|
||||
return &zfsentity.ZfsData{}
|
||||
}
|
||||
return &zfsentity.ZfsData{Pools: b.detail.Pools}
|
||||
} else {
|
||||
b.detailFailed = false
|
||||
b.detail = data
|
||||
b.lastDetailRefresh = time.Now()
|
||||
}
|
||||
}
|
||||
if b.detail == nil {
|
||||
return &zfsentity.ZfsData{}
|
||||
}
|
||||
return b.detail
|
||||
}
|
||||
|
||||
// collectDetail builds a ZfsData payload from the current system state.
|
||||
func (b *poolBackend) collectDetail(previous *zfsentity.ZfsData) (*zfsentity.ZfsData, error) {
|
||||
pools, err := b.poolStatsFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(pools) == 0 {
|
||||
return &zfsentity.ZfsData{Pools: []*zfsentity.PoolDetail{}, Complete: true}, nil
|
||||
}
|
||||
|
||||
statuses, statusErr := b.poolStatusesFn()
|
||||
if statusErr != nil {
|
||||
slog.Debug("Storage pool status unavailable", "backend", b.name, "err", statusErr)
|
||||
}
|
||||
datasets, datasetsErr := b.datasets()
|
||||
if datasetsErr != nil {
|
||||
slog.Debug("Storage pool datasets unavailable", "backend", b.name, "err", datasetsErr)
|
||||
}
|
||||
|
||||
statusByPool := make(map[string]zfs.PoolStatus, len(statuses))
|
||||
for _, st := range statuses {
|
||||
statusByPool[st.Name] = st
|
||||
}
|
||||
|
||||
previousByPool := make(map[string]*zfsentity.PoolDetail)
|
||||
if previous != nil {
|
||||
for _, pool := range previous.Pools {
|
||||
if pool != nil {
|
||||
previousByPool[pool.Name] = pool
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data := &zfsentity.ZfsData{Pools: make([]*zfsentity.PoolDetail, 0, len(pools)), Complete: true}
|
||||
for i := range pools {
|
||||
p := &pools[i]
|
||||
detail := &zfsentity.PoolDetail{
|
||||
DisplayName: p.DisplayName,
|
||||
Raw: p.Raw,
|
||||
Name: p.Name,
|
||||
Health: p.Health,
|
||||
Size: p.Size,
|
||||
Alloc: p.Alloc,
|
||||
Free: p.Free,
|
||||
}
|
||||
if st, ok := statusByPool[p.Name]; statusErr == nil && ok {
|
||||
if st.Scrub.State != "" && st.Scrub.State != "NONE" {
|
||||
detail.Scrub = &zfsentity.Scrub{
|
||||
State: st.Scrub.State,
|
||||
Progress: st.Scrub.Progress,
|
||||
Errors: st.Scrub.Errors,
|
||||
}
|
||||
}
|
||||
for _, v := range st.Vdevs {
|
||||
detail.Vdevs = append(detail.Vdevs, &zfsentity.Vdev{
|
||||
Name: v.Name,
|
||||
State: v.State,
|
||||
ReadErrs: v.ReadErrs,
|
||||
WriteErrs: v.WriteErrs,
|
||||
ChecksumErrs: v.ChecksumErrs,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if cached := previousByPool[p.Name]; cached != nil {
|
||||
detail.Scrub = cached.Scrub
|
||||
detail.Vdevs = cached.Vdevs
|
||||
}
|
||||
}
|
||||
if datasetsErr == nil {
|
||||
foundDataset := false
|
||||
for _, ds := range datasets {
|
||||
if poolOfDataset(ds.Name) == p.Name {
|
||||
foundDataset = true
|
||||
detail.Datasets = append(detail.Datasets, &zfsentity.Dataset{
|
||||
Name: ds.Name,
|
||||
Used: ds.Used,
|
||||
Avail: ds.Avail,
|
||||
Mountpoint: ds.Mountpoint,
|
||||
})
|
||||
}
|
||||
}
|
||||
if !foundDataset {
|
||||
if cached := previousByPool[p.Name]; cached != nil {
|
||||
detail.Datasets = cached.Datasets
|
||||
}
|
||||
}
|
||||
} else if cached := previousByPool[p.Name]; cached != nil {
|
||||
detail.Datasets = cached.Datasets
|
||||
}
|
||||
data.Pools = append(data.Pools, detail)
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// poolOfDataset returns the pool name for a dataset name (everything before
|
||||
// the first '/'). Datasets without a separator belong to a pool of the same
|
||||
// name.
|
||||
func poolOfDataset(name string) string {
|
||||
if idx := strings.IndexByte(name, '/'); idx >= 0 {
|
||||
return name[:idx]
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
// ZfsMountpoints returns the set of mountpoints backed by ZFS datasets.
|
||||
func (m *StoragePoolManager) ZfsMountpoints() map[string]bool {
|
||||
usage := m.DatasetUsage()
|
||||
mountpoints := make(map[string]bool, len(usage))
|
||||
for mountpoint := range usage {
|
||||
mountpoints[mountpoint] = true
|
||||
}
|
||||
return mountpoints
|
||||
}
|
||||
|
||||
func btrfsPoolStats(fs btrfs.Filesystem) zfs.PoolStat {
|
||||
return zfs.PoolStat{MountID: fs.MountID, IODevice: fs.IODevice, Raw: fs.Raw, DisplayName: fs.Name, Name: "b:" + fs.UUID, Size: fs.Size, Alloc: fs.Alloc, Free: fs.Size - min(fs.Alloc, fs.Size), Health: fs.Health}
|
||||
}
|
||||
|
||||
func btrfsKernelStats(fs btrfs.Filesystem) zfs.PoolKernelStat {
|
||||
return zfs.PoolKernelStat{Name: "b:" + fs.UUID, Health: fs.Health, NRead: fs.NRead, NWrite: fs.NWrite}
|
||||
}
|
||||
|
||||
func btrfsPoolStatuses(fs btrfs.Filesystem) zfs.PoolStatus {
|
||||
status := zfs.PoolStatus{Name: "b:" + fs.UUID, State: fs.Health, Scrub: zfs.ScrubStatus{State: "NONE"}}
|
||||
for _, dev := range fs.Devices {
|
||||
status.Vdevs = append(status.Vdevs, zfs.VdevStatus{
|
||||
Name: dev.Name, State: dev.State,
|
||||
ReadErrs: dev.ReadErrs, WriteErrs: dev.WriteErrs, ChecksumErrs: dev.CorruptionErrs,
|
||||
})
|
||||
}
|
||||
return status
|
||||
}
|
||||
|
||||
// markDuplicateCharts leaves pool telemetry and detail intact, but tells the
|
||||
// hub which charts already have a filesystem equivalent. Only exact kernel
|
||||
// filesystem and I/O-device matches qualify; labels are never used.
|
||||
func (m *StoragePoolManager) markDuplicateCharts(stats *system.Stats, filesystems map[string]*system.FsStats, mountID func(string) string) {
|
||||
identities := make(map[string]string, len(filesystems))
|
||||
for device, fs := range filesystems {
|
||||
if fs.DiskTotal > 0 {
|
||||
identities[device] = mountID(fs.Mountpoint)
|
||||
}
|
||||
}
|
||||
for _, backend := range m.backends {
|
||||
for _, pool := range backend.poolData {
|
||||
sample := stats.ZfsPools[pool.Name]
|
||||
if sample == nil || pool.MountID == "" {
|
||||
continue
|
||||
}
|
||||
for device, identity := range identities {
|
||||
if identity != pool.MountID {
|
||||
continue
|
||||
}
|
||||
// Raw physical usage is not equivalent to a filesystem usage chart.
|
||||
sample.HideUsage = !pool.Raw
|
||||
if pool.IODevice != "" && pool.IODevice == device {
|
||||
sample.HideIO = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,505 @@
|
||||
//go:build testing
|
||||
|
||||
package agent
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/henrygd/beszel/agent/btrfs"
|
||||
"github.com/henrygd/beszel/agent/zfs"
|
||||
"github.com/henrygd/beszel/internal/entities/system"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestOptionalPoolSource(t *testing.T) {
|
||||
failure := errors.New("timeout")
|
||||
for _, err := range []error{nil, zfs.ErrNoZfs, fmt.Errorf("zpool: %w", exec.ErrNotFound), errors.ErrUnsupported, failure} {
|
||||
_, got := optionalPoolSource(func() ([]zfs.PoolStat, error) { return nil, err })()
|
||||
if err == failure {
|
||||
assert.ErrorIs(t, got, failure)
|
||||
} else {
|
||||
assert.NoError(t, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type poolTestBackend struct {
|
||||
name string
|
||||
err error
|
||||
alloc uint64
|
||||
read uint64
|
||||
empty bool
|
||||
}
|
||||
|
||||
func (state *poolTestBackend) backend() *poolBackend {
|
||||
name := "zfs"
|
||||
if strings.HasPrefix(state.name, "b:") {
|
||||
name = "btrfs"
|
||||
}
|
||||
return &poolBackend{
|
||||
name: name,
|
||||
poolStatsFn: func() ([]zfs.PoolStat, error) {
|
||||
if state.empty {
|
||||
return nil, state.err
|
||||
}
|
||||
return []zfs.PoolStat{{Name: state.name, Size: 100, Alloc: state.alloc}}, state.err
|
||||
},
|
||||
kernelStatsFn: func() ([]zfs.PoolKernelStat, error) {
|
||||
return []zfs.PoolKernelStat{{Name: state.name, NRead: state.read}}, state.err
|
||||
},
|
||||
poolStatusesFn: func() ([]zfs.PoolStatus, error) { return nil, nil },
|
||||
datasetsFn: func() ([]zfs.Dataset, error) { return nil, nil },
|
||||
}
|
||||
}
|
||||
|
||||
func TestIndependentPoolBackendCaches(t *testing.T) {
|
||||
for _, failed := range []int{0, 1} {
|
||||
t.Run([]string{"zfs", "btrfs"}[failed], func(t *testing.T) {
|
||||
states := []*poolTestBackend{{name: "tank", alloc: 10}, {name: "b:uuid", alloc: 10}}
|
||||
managers := []*poolBackend{states[0].backend(), states[1].backend()}
|
||||
zm := &StoragePoolManager{backends: managers, detailInterval: time.Hour}
|
||||
var stats system.Stats
|
||||
zm.Update(&stats)
|
||||
require.Len(t, stats.ZfsPools, 2)
|
||||
require.True(t, zm.GetDetail(true).Complete)
|
||||
baseline := poolKernelSample{at: time.Now().Add(-time.Second)}
|
||||
for i, m := range managers {
|
||||
m.lastPoolStats = time.Time{}
|
||||
m.kernelSamples[states[i].name] = baseline
|
||||
states[i].alloc = 20
|
||||
states[i].read = 100
|
||||
}
|
||||
states[failed].err = errors.New("collection failed")
|
||||
zm.Update(&stats)
|
||||
healthy := 1 - failed
|
||||
assert.Equal(t, uint64(10), managers[failed].poolData[0].Alloc)
|
||||
assert.Equal(t, uint64(20), managers[healthy].poolData[0].Alloc)
|
||||
assert.Equal(t, baseline, managers[failed].kernelSamples[states[failed].name])
|
||||
assert.Zero(t, stats.ZfsPools[states[failed].name].ReadBytes)
|
||||
assert.Positive(t, stats.ZfsPools[states[healthy].name].ReadBytes)
|
||||
partial := zm.GetDetail(true)
|
||||
assert.False(t, partial.Complete)
|
||||
assert.False(t, partial.CanRefreshPool(states[failed].name))
|
||||
assert.True(t, partial.CanRefreshPool(states[healthy].name))
|
||||
assert.Equal(t, uint64(10), partial.Pools[failed].Alloc)
|
||||
assert.Equal(t, uint64(20), partial.Pools[healthy].Alloc)
|
||||
assert.False(t, zm.GetDetail(false).Complete, "a failed forced refresh must not become complete from cache")
|
||||
|
||||
// Successful empty inventory removes only the healthy backend's pool.
|
||||
states[healthy].empty = true
|
||||
managers[healthy].lastPoolStats = time.Time{}
|
||||
zm.Update(&stats)
|
||||
require.Len(t, stats.ZfsPools, 1)
|
||||
assert.Contains(t, stats.ZfsPools, states[failed].name)
|
||||
partial = zm.GetDetail(true)
|
||||
require.Len(t, partial.Pools, 1)
|
||||
assert.True(t, partial.CanRefreshPool(states[healthy].name))
|
||||
|
||||
// Recovery uses the retained I/O baseline, then normal removal works.
|
||||
states[failed].err = nil
|
||||
managers[failed].lastPoolStats = time.Time{}
|
||||
zm.Update(&stats)
|
||||
assert.Positive(t, stats.ZfsPools[states[failed].name].ReadBytes)
|
||||
assert.True(t, zm.GetDetail(true).Complete)
|
||||
states[failed].empty = true
|
||||
managers[failed].lastPoolStats = time.Time{}
|
||||
zm.Update(&stats)
|
||||
assert.Empty(t, stats.ZfsPools)
|
||||
assert.Empty(t, zm.GetDetail(true).Pools)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIndependentBackendsWithoutCache(t *testing.T) {
|
||||
z := &poolTestBackend{name: "tank", err: errors.New("ZFS failure")}
|
||||
b := &poolTestBackend{name: "b:uuid", alloc: 20}
|
||||
zm := &StoragePoolManager{backends: []*poolBackend{z.backend(), b.backend()}, detailInterval: time.Hour}
|
||||
var stats system.Stats
|
||||
zm.Update(&stats)
|
||||
require.Len(t, stats.ZfsPools, 1)
|
||||
assert.Contains(t, stats.ZfsPools, "b:uuid")
|
||||
detail := zm.GetDetail(true)
|
||||
require.Len(t, detail.Pools, 1)
|
||||
assert.False(t, detail.Complete)
|
||||
assert.Equal(t, []string{"btrfs"}, detail.CompleteBackends)
|
||||
}
|
||||
|
||||
func TestConcurrentBackendDetailsAndMetrics(t *testing.T) {
|
||||
zm := &StoragePoolManager{backends: []*poolBackend{(&poolTestBackend{name: "tank"}).backend(), (&poolTestBackend{name: "b:uuid"}).backend()}, detailInterval: time.Hour}
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < 3; i++ {
|
||||
wg.Add(1)
|
||||
go func(metrics bool) {
|
||||
defer wg.Done()
|
||||
for j := 0; j < 10; j++ {
|
||||
if metrics {
|
||||
zm.Update(&system.Stats{})
|
||||
} else {
|
||||
zm.GetDetail(true)
|
||||
}
|
||||
}
|
||||
}(i == 0)
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func TestStoragePoolBackendOrder(t *testing.T) {
|
||||
z := (&poolTestBackend{name: "tank"}).backend()
|
||||
b := (&poolTestBackend{name: "b:uuid"}).backend()
|
||||
b.datasetsFn = nil // Btrfs does not expose datasets.
|
||||
z.datasetsFn = func() ([]zfs.Dataset, error) {
|
||||
return []zfs.Dataset{{Name: "tank/data", Mountpoint: "/tank", Used: 10}}, nil
|
||||
}
|
||||
m := &StoragePoolManager{backends: []*poolBackend{b, z}, detailInterval: time.Hour}
|
||||
var stats system.Stats
|
||||
m.Update(&stats)
|
||||
require.Len(t, stats.ZfsPools, 2)
|
||||
detail := m.GetDetail(true)
|
||||
require.True(t, detail.Complete)
|
||||
assert.Equal(t, []string{"btrfs", "zfs"}, detail.CompleteBackends)
|
||||
assert.Empty(t, detail.Pools[0].Datasets)
|
||||
assert.Len(t, detail.Pools[1].Datasets, 1)
|
||||
assert.Equal(t, uint64(10), m.DatasetUsage()["/tank"].used)
|
||||
|
||||
b.poolData[0].MountID = "uuid"
|
||||
b.poolData[0].IODevice = "sda"
|
||||
calls := 0
|
||||
m.markDuplicateCharts(&stats, map[string]*system.FsStats{
|
||||
"sda": {Mountpoint: "/", DiskTotal: 100},
|
||||
}, func(string) string { calls++; return "uuid" })
|
||||
assert.Equal(t, 1, calls, "resolve each filesystem once across all backends")
|
||||
assert.True(t, stats.ZfsPools["b:uuid"].HideUsage)
|
||||
assert.True(t, stats.ZfsPools["b:uuid"].HideIO)
|
||||
}
|
||||
|
||||
func TestUpdatePopulatesZfsPools(t *testing.T) {
|
||||
zm := &StoragePoolManager{detailInterval: time.Hour, backends: []*poolBackend{{name: "zfs"}}}
|
||||
zm.backends[0].poolStatsFn = func() ([]zfs.PoolStat, error) {
|
||||
return []zfs.PoolStat{{Name: "tank", Size: 23999000000000, Alloc: 12000000000000, Free: 11999000000000, Health: "DEGRADED"}}, nil
|
||||
}
|
||||
zm.backends[0].datasetsFn = func() ([]zfs.Dataset, error) {
|
||||
return []zfs.Dataset{
|
||||
{Name: "tank/apps", Used: 5000000000000, Avail: 11999000000000, Mountpoint: "/tank/apps"},
|
||||
{Name: "tank/backup", Used: 6000000000000, Avail: 11999000000000, Mountpoint: "/tank/backup"},
|
||||
// Small zvol (Proxmox VM EFI disk): must not round to zero.
|
||||
{Name: "rpool/vm-100-disk-2", Used: 4194304, Avail: 0, Mountpoint: "-"},
|
||||
}, nil
|
||||
}
|
||||
var kernelCalls int
|
||||
zm.backends[0].kernelStatsFn = func() ([]zfs.PoolKernelStat, error) {
|
||||
kernelCalls++
|
||||
return []zfs.PoolKernelStat{{
|
||||
Name: "tank", Health: "ONLINE",
|
||||
NRead: uint64(kernelCalls-1) * 1250, NWrite: uint64(kernelCalls-1) * 5120,
|
||||
}}, nil
|
||||
}
|
||||
|
||||
var stats system.Stats
|
||||
// The first kernel sample establishes the cumulative-counter baseline.
|
||||
zm.Update(&stats)
|
||||
zm.backends[0].kernelSamples["tank"] = poolKernelSample{at: time.Now().Add(-time.Second)}
|
||||
zm.Update(&stats)
|
||||
require.NotNil(t, stats.ZfsPools)
|
||||
require.Contains(t, stats.ZfsPools, "tank")
|
||||
assert.InDelta(t, 22350.8105, stats.ZfsPools["tank"].Total, 0.0001) // Size in GiB
|
||||
assert.InDelta(t, 11175.8709, stats.ZfsPools["tank"].Used, 0.0001) // Alloc in GiB
|
||||
assert.Equal(t, "ONLINE", stats.ZfsPools["tank"].Health)
|
||||
assert.InDelta(t, 1250, stats.ZfsPools["tank"].ReadBytes, 5)
|
||||
assert.InDelta(t, 5120, stats.ZfsPools["tank"].WriteBytes, 5)
|
||||
|
||||
}
|
||||
|
||||
// TestUpdateKernelStatsMissing verifies pools without a kernel sample report zero
|
||||
// I/O instead of erroring.
|
||||
func TestUpdateKernelStatsMissing(t *testing.T) {
|
||||
zm := &StoragePoolManager{detailInterval: time.Hour, backends: []*poolBackend{{name: "zfs"}}}
|
||||
zm.backends[0].poolStatsFn = func() ([]zfs.PoolStat, error) {
|
||||
return []zfs.PoolStat{{Name: "tank", Size: 1, Alloc: 1, Health: "ONLINE"}}, nil
|
||||
}
|
||||
zm.backends[0].datasetsFn = func() ([]zfs.Dataset, error) { return nil, nil }
|
||||
zm.backends[0].kernelStatsFn = func() ([]zfs.PoolKernelStat, error) {
|
||||
return nil, zfs.ErrNoZfs
|
||||
}
|
||||
|
||||
var stats system.Stats
|
||||
zm.Update(&stats)
|
||||
require.NotNil(t, stats.ZfsPools)
|
||||
assert.Equal(t, uint64(0), stats.ZfsPools["tank"].ReadBytes)
|
||||
assert.Equal(t, uint64(0), stats.ZfsPools["tank"].WriteBytes)
|
||||
}
|
||||
|
||||
func TestUpdateKernelCounterReset(t *testing.T) {
|
||||
zm := &StoragePoolManager{detailInterval: time.Hour, backends: []*poolBackend{{name: "zfs"}}}
|
||||
zm.backends[0].poolStatsFn = func() ([]zfs.PoolStat, error) {
|
||||
return []zfs.PoolStat{{Name: "tank", Health: "ONLINE"}}, nil
|
||||
}
|
||||
zm.backends[0].datasetsFn = func() ([]zfs.Dataset, error) { return nil, nil }
|
||||
zm.backends[0].kernelSamples = map[string]poolKernelSample{
|
||||
"tank": {nread: 100, nwrite: 200, at: time.Now().Add(-time.Second)},
|
||||
}
|
||||
zm.backends[0].kernelStatsFn = func() ([]zfs.PoolKernelStat, error) {
|
||||
return []zfs.PoolKernelStat{{Name: "tank", Health: "ONLINE", NRead: 10, NWrite: 20}}, nil
|
||||
}
|
||||
|
||||
var stats system.Stats
|
||||
zm.Update(&stats)
|
||||
assert.Equal(t, uint64(0), stats.ZfsPools["tank"].ReadBytes)
|
||||
assert.Equal(t, uint64(0), stats.ZfsPools["tank"].WriteBytes)
|
||||
}
|
||||
|
||||
func TestUpdateNoZfs(t *testing.T) {
|
||||
zm := &StoragePoolManager{detailInterval: time.Hour, backends: []*poolBackend{{name: "zfs"}}}
|
||||
calls := 0
|
||||
zm.backends[0].poolStatsFn = func() ([]zfs.PoolStat, error) {
|
||||
calls++
|
||||
return nil, zfs.ErrNoZfs
|
||||
}
|
||||
|
||||
var stats system.Stats
|
||||
zm.Update(&stats)
|
||||
zm.Update(&stats)
|
||||
assert.Nil(t, stats.ZfsPools)
|
||||
assert.Equal(t, 1, calls, "failed pool discovery should be cached until the next refresh interval")
|
||||
}
|
||||
|
||||
func TestUpdateEmptyPools(t *testing.T) {
|
||||
zm := &StoragePoolManager{detailInterval: time.Hour, backends: []*poolBackend{{name: "zfs"}}}
|
||||
calls := 0
|
||||
zm.backends[0].poolStatsFn = func() ([]zfs.PoolStat, error) {
|
||||
calls++
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var stats system.Stats
|
||||
zm.Update(&stats)
|
||||
zm.Update(&stats)
|
||||
assert.Nil(t, stats.ZfsPools)
|
||||
assert.Equal(t, 1, calls, "an empty pool inventory should be cached until the next refresh interval")
|
||||
}
|
||||
|
||||
func TestDatasetUsage(t *testing.T) {
|
||||
zm := &StoragePoolManager{detailInterval: time.Hour, backends: []*poolBackend{{name: "zfs"}}}
|
||||
calls := 0
|
||||
zm.backends[0].datasetsFn = func() ([]zfs.Dataset, error) {
|
||||
calls++
|
||||
return []zfs.Dataset{
|
||||
{Name: "tank", Used: 12000000000000, Avail: 11999000000000, Mountpoint: "/tank"},
|
||||
{Name: "tank/apps", Used: 1000000000000, Avail: 11999000000000, Mountpoint: "/tank/apps"},
|
||||
{Name: "rpool", Used: 900000000000, Avail: 300000000000, Mountpoint: "-"}, // zvol/unmounted: excluded
|
||||
}, nil
|
||||
}
|
||||
|
||||
usage := zm.DatasetUsage()
|
||||
require.Len(t, usage, 2)
|
||||
assert.Equal(t, zfsDatasetUsage{used: 12000000000000, avail: 11999000000000}, usage["/tank"])
|
||||
assert.Equal(t, zfsDatasetUsage{used: 1000000000000, avail: 11999000000000}, usage["/tank/apps"])
|
||||
assert.Equal(t, 1, calls)
|
||||
|
||||
// Second call within the refresh window must not re-run the collector.
|
||||
zm.DatasetUsage()
|
||||
assert.Equal(t, 1, calls)
|
||||
}
|
||||
|
||||
func TestDatasetUsageRefreshOnErrorKeepsPrevious(t *testing.T) {
|
||||
zm := &StoragePoolManager{detailInterval: time.Hour, backends: []*poolBackend{{name: "zfs"}}}
|
||||
zm.backends[0].datasetsFn = func() ([]zfs.Dataset, error) {
|
||||
return []zfs.Dataset{{Name: "tank", Used: 1, Avail: 1, Mountpoint: "/tank"}}, nil
|
||||
}
|
||||
assert.Len(t, zm.DatasetUsage(), 1)
|
||||
|
||||
// Force refresh window expiry, then a failing collector.
|
||||
zm.backends[0].lastUsageRefresh = time.Now().Add(-10 * time.Minute)
|
||||
zm.backends[0].datasetsFn = func() ([]zfs.Dataset, error) {
|
||||
return nil, zfs.ErrNoZfs
|
||||
}
|
||||
usage := zm.DatasetUsage()
|
||||
assert.Len(t, usage, 1, "previous usage should be retained on error")
|
||||
}
|
||||
|
||||
func TestGetDetailForceRefresh(t *testing.T) {
|
||||
zm := &StoragePoolManager{detailInterval: time.Hour, backends: []*poolBackend{{name: "zfs"}}}
|
||||
poolCalls := 0
|
||||
zm.backends[0].poolStatsFn = func() ([]zfs.PoolStat, error) {
|
||||
poolCalls++
|
||||
return []zfs.PoolStat{{Name: "tank", Alloc: uint64(poolCalls)}}, nil
|
||||
}
|
||||
zm.backends[0].poolStatusesFn = func() ([]zfs.PoolStatus, error) { return nil, nil }
|
||||
zm.backends[0].datasetsFn = func() ([]zfs.Dataset, error) { return nil, nil }
|
||||
|
||||
first := zm.GetDetail(false)
|
||||
assert.True(t, first.Complete)
|
||||
require.Len(t, first.Pools, 1)
|
||||
assert.Equal(t, uint64(1), first.Pools[0].Alloc)
|
||||
|
||||
cached := zm.GetDetail(false)
|
||||
require.Len(t, cached.Pools, 1)
|
||||
assert.Equal(t, uint64(1), cached.Pools[0].Alloc)
|
||||
assert.Equal(t, 1, poolCalls)
|
||||
|
||||
refreshed := zm.GetDetail(true)
|
||||
assert.True(t, refreshed.Complete)
|
||||
require.Len(t, refreshed.Pools, 1)
|
||||
assert.Equal(t, uint64(2), refreshed.Pools[0].Alloc)
|
||||
assert.Equal(t, 2, poolCalls)
|
||||
}
|
||||
|
||||
func TestGetDetailSuccessfulEmptyInventoryClearsCache(t *testing.T) {
|
||||
zm := &StoragePoolManager{detailInterval: time.Hour, backends: []*poolBackend{{name: "zfs"}}}
|
||||
zm.backends[0].poolStatsFn = func() ([]zfs.PoolStat, error) {
|
||||
return []zfs.PoolStat{{Name: "tank"}}, nil
|
||||
}
|
||||
zm.backends[0].poolStatusesFn = func() ([]zfs.PoolStatus, error) { return nil, nil }
|
||||
zm.backends[0].datasetsFn = func() ([]zfs.Dataset, error) { return nil, nil }
|
||||
|
||||
require.Len(t, zm.GetDetail(false).Pools, 1)
|
||||
zm.backends[0].poolStatsFn = func() ([]zfs.PoolStat, error) { return nil, nil }
|
||||
empty := zm.GetDetail(true)
|
||||
assert.True(t, empty.Complete)
|
||||
assert.Empty(t, empty.Pools)
|
||||
}
|
||||
|
||||
func TestGetDetailFailureReturnsIncompleteCachedInventory(t *testing.T) {
|
||||
zm := &StoragePoolManager{detailInterval: time.Hour, backends: []*poolBackend{{name: "zfs"}}}
|
||||
zm.backends[0].poolStatsFn = func() ([]zfs.PoolStat, error) {
|
||||
return []zfs.PoolStat{{Name: "tank"}}, nil
|
||||
}
|
||||
zm.backends[0].poolStatusesFn = func() ([]zfs.PoolStatus, error) {
|
||||
return []zfs.PoolStatus{{Name: "tank", Vdevs: []zfs.VdevStatus{{Name: "mirror-0"}}}}, nil
|
||||
}
|
||||
zm.backends[0].datasetsFn = func() ([]zfs.Dataset, error) {
|
||||
return []zfs.Dataset{{Name: "tank/data"}}, nil
|
||||
}
|
||||
first := zm.GetDetail(false)
|
||||
require.True(t, first.Complete)
|
||||
require.Len(t, first.Pools[0].Vdevs, 1)
|
||||
require.Len(t, first.Pools[0].Datasets, 1)
|
||||
|
||||
zm.backends[0].poolStatusesFn = func() ([]zfs.PoolStatus, error) { return nil, zfs.ErrNoZfs }
|
||||
zm.backends[0].datasetsFn = func() ([]zfs.Dataset, error) { return nil, zfs.ErrNoZfs }
|
||||
partial := zm.GetDetail(true)
|
||||
require.True(t, partial.Complete)
|
||||
require.Len(t, partial.Pools[0].Vdevs, 1)
|
||||
require.Len(t, partial.Pools[0].Datasets, 1)
|
||||
|
||||
zm.backends[0].poolStatsFn = func() ([]zfs.PoolStat, error) { return nil, zfs.ErrNoZfs }
|
||||
lastSuccessfulRefresh := zm.backends[0].lastDetailRefresh
|
||||
failed := zm.GetDetail(true)
|
||||
assert.False(t, failed.Complete)
|
||||
require.Len(t, failed.Pools, 1)
|
||||
assert.Equal(t, "tank", failed.Pools[0].Name)
|
||||
assert.Equal(t, lastSuccessfulRefresh, zm.backends[0].lastDetailRefresh)
|
||||
}
|
||||
|
||||
func TestZfsMountpoints(t *testing.T) {
|
||||
zm := &StoragePoolManager{detailInterval: time.Hour, backends: []*poolBackend{{name: "zfs"}}}
|
||||
zm.backends[0].datasetsFn = func() ([]zfs.Dataset, error) {
|
||||
return []zfs.Dataset{
|
||||
{Name: "tank", Mountpoint: "/tank"},
|
||||
{Name: "rpool/ROOT/pve-1", Mountpoint: "/"},
|
||||
}, nil
|
||||
}
|
||||
mountpoints := zm.ZfsMountpoints()
|
||||
assert.Len(t, mountpoints, 2)
|
||||
assert.True(t, mountpoints["/tank"])
|
||||
assert.True(t, mountpoints["/"])
|
||||
}
|
||||
|
||||
func TestBtrfsRawCapacityPropagates(t *testing.T) {
|
||||
zm := &StoragePoolManager{detailInterval: time.Hour, backends: []*poolBackend{{name: "zfs", poolStatsFn: func() ([]zfs.PoolStat, error) {
|
||||
return []zfs.PoolStat{btrfsPoolStats(btrfs.Filesystem{UUID: "raw", Name: "raw", Size: 200, Alloc: 100, Raw: true})}, nil
|
||||
},
|
||||
poolStatusesFn: func() ([]zfs.PoolStatus, error) { return nil, nil },
|
||||
datasetsFn: func() ([]zfs.Dataset, error) { return nil, nil }}}}
|
||||
var stats system.Stats
|
||||
zm.Update(&stats)
|
||||
require.True(t, stats.ZfsPools["b:raw"].Raw)
|
||||
detail := zm.GetDetail(true)
|
||||
require.True(t, detail.Complete)
|
||||
require.Len(t, detail.Pools, 1)
|
||||
assert.True(t, detail.Pools[0].Raw)
|
||||
}
|
||||
|
||||
func TestMarkDuplicatePoolCharts(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name, poolID, device string
|
||||
raw bool
|
||||
diskTotal float64
|
||||
wantUsage, wantIO bool
|
||||
}{
|
||||
{"single device root", "fs1", "dm-0", false, 100, true, true},
|
||||
{"multi device", "fs1", "", false, 100, true, false},
|
||||
{"different IO device", "fs1", "nvme0n1", false, 100, true, false},
|
||||
{"different filesystem", "fs2", "dm-0", false, 100, false, false},
|
||||
{"unknown identity", "", "dm-0", false, 100, false, false},
|
||||
{"raw usage", "fs1", "dm-0", true, 100, false, true},
|
||||
{"failed disk collection", "fs1", "dm-0", false, 0, false, false},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
zm := &StoragePoolManager{detailInterval: time.Hour, backends: []*poolBackend{{name: "zfs", poolData: []zfs.PoolStat{{Name: "arbitrary label", MountID: tc.poolID, IODevice: tc.device, Raw: tc.raw}}}}}
|
||||
stats := &system.Stats{ZfsPools: map[string]*system.ZfsPool{"arbitrary label": {}}}
|
||||
fs := map[string]*system.FsStats{"dm-0": {Root: true, Mountpoint: "/", DiskTotal: tc.diskTotal}}
|
||||
zm.markDuplicateCharts(stats, fs, func(string) string { return "fs1" })
|
||||
assert.Equal(t, tc.wantUsage, stats.ZfsPools["arbitrary label"].HideUsage)
|
||||
assert.Equal(t, tc.wantIO, stats.ZfsPools["arbitrary label"].HideIO)
|
||||
// Bind mounts and custom extra-filesystem names have the same identity.
|
||||
fs["dm-0"].Root = false
|
||||
fs["dm-0"].Mountpoint = "/extra-filesystems/storage"
|
||||
fs["dm-0"].Name = "custom name"
|
||||
stats.ZfsPools["arbitrary label"] = &system.ZfsPool{}
|
||||
zm.markDuplicateCharts(stats, fs, func(string) string { return "fs1" })
|
||||
assert.Equal(t, tc.wantUsage, stats.ZfsPools["arbitrary label"].HideUsage)
|
||||
assert.Equal(t, tc.wantIO, stats.ZfsPools["arbitrary label"].HideIO)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBtrfsPoolIdentities(t *testing.T) {
|
||||
old := btrfsFilesystems
|
||||
t.Cleanup(func() { btrfsFilesystems = old })
|
||||
label := "tank"
|
||||
btrfsFilesystems = func() ([]btrfs.Filesystem, error) {
|
||||
return []btrfs.Filesystem{
|
||||
{UUID: "11111111-1111-4111-8111-111111111111", Name: label, Size: 100, Health: "ONLINE", NRead: 100, Devices: []btrfs.Device{{Name: "first"}}},
|
||||
{UUID: "22222222-2222-4222-8222-222222222222", Name: "tank", Size: 200, Health: "DEGRADED", NRead: 200, Devices: []btrfs.Device{{Name: "second"}}},
|
||||
}, nil
|
||||
}
|
||||
zm := &StoragePoolManager{detailInterval: time.Hour, backends: []*poolBackend{{name: "zfs", poolStatsFn: func() ([]zfs.PoolStat, error) { return []zfs.PoolStat{{Name: "tank", Size: 300}}, nil },
|
||||
kernelStatsFn: func() ([]zfs.PoolKernelStat, error) { return []zfs.PoolKernelStat{{Name: "tank", NRead: 300}}, nil },
|
||||
poolStatusesFn: func() ([]zfs.PoolStatus, error) {
|
||||
return []zfs.PoolStatus{{Name: "tank", Vdevs: []zfs.VdevStatus{{Name: "zfs-device"}}}}, nil
|
||||
},
|
||||
|
||||
datasetsFn: func() ([]zfs.Dataset, error) { return []zfs.Dataset{{Name: "tank/data"}}, nil }}, newBtrfsBackend()}}
|
||||
first := "b:11111111-1111-4111-8111-111111111111"
|
||||
second := "b:22222222-2222-4222-8222-222222222222"
|
||||
var stats system.Stats
|
||||
zm.Update(&stats)
|
||||
require.Len(t, stats.ZfsPools, 3)
|
||||
assert.Contains(t, stats.ZfsPools, "tank")
|
||||
assert.Equal(t, "ONLINE", stats.ZfsPools[first].Health)
|
||||
assert.Equal(t, "DEGRADED", stats.ZfsPools[second].Health)
|
||||
assert.Equal(t, uint64(100), zm.backends[1].kernelSamples[first].nread)
|
||||
assert.Equal(t, uint64(200), zm.backends[1].kernelSamples[second].nread)
|
||||
detail := zm.GetDetail(true)
|
||||
require.Len(t, detail.Pools, 3)
|
||||
assert.Equal(t, "zfs-device", detail.Pools[0].Vdevs[0].Name)
|
||||
assert.Len(t, detail.Pools[0].Datasets, 1)
|
||||
assert.Equal(t, "first", detail.Pools[1].Vdevs[0].Name)
|
||||
assert.Empty(t, detail.Pools[1].Datasets)
|
||||
assert.Equal(t, "second", detail.Pools[2].Vdevs[0].Name)
|
||||
label = "renamed"
|
||||
zm.backends[0].lastPoolStats = time.Time{}
|
||||
zm.backends[1].lastPoolStats = time.Time{}
|
||||
zm.Update(&stats)
|
||||
require.Len(t, stats.ZfsPools, 3)
|
||||
assert.Equal(t, "renamed", stats.ZfsPools[first].DisplayName)
|
||||
assert.Equal(t, first, zm.GetDetail(true).Pools[1].Name)
|
||||
assert.Equal(t, "renamed", zm.GetDetail(true).Pools[1].DisplayName)
|
||||
}
|
||||
+4
-2
@@ -12,6 +12,7 @@ import (
|
||||
|
||||
"github.com/henrygd/beszel"
|
||||
"github.com/henrygd/beszel/agent/battery"
|
||||
"github.com/henrygd/beszel/agent/btrfs"
|
||||
"github.com/henrygd/beszel/agent/utils"
|
||||
"github.com/henrygd/beszel/agent/zfs"
|
||||
"github.com/henrygd/beszel/internal/entities/container"
|
||||
@@ -219,8 +220,9 @@ func (a *Agent) getSystemStats(cacheTimeMs uint16) system.Stats {
|
||||
// disk i/o (cache-aware per interval)
|
||||
a.updateDiskIo(cacheTimeMs, &systemStats)
|
||||
|
||||
// zfs pool stats
|
||||
a.zfsManager.Update(&systemStats)
|
||||
// storage pool stats
|
||||
a.storagePoolManager.Update(&systemStats)
|
||||
a.storagePoolManager.markDuplicateCharts(&systemStats, a.fsStats, btrfs.MountID)
|
||||
|
||||
// network stats (per cache interval)
|
||||
a.updateNetworkStats(cacheTimeMs, &systemStats)
|
||||
|
||||
+9
-5
@@ -33,11 +33,15 @@ var ErrNoZfs = errors.New("zfs utilities unavailable")
|
||||
|
||||
// PoolStat is a snapshot of a ZFS pool's capacity and health.
|
||||
type PoolStat struct {
|
||||
Name string
|
||||
Size uint64 // total capacity in bytes
|
||||
Alloc uint64 // allocated bytes
|
||||
Free uint64 // free bytes
|
||||
Health string // ONLINE, DEGRADED, FAULTED, ...
|
||||
DisplayName string // optional friendly name; Name remains the stable key
|
||||
MountID string // Btrfs filesystem identity, empty for other backends
|
||||
IODevice string // sole Btrfs member device, if known
|
||||
Raw bool // physical accounting rather than usable filesystem space
|
||||
Name string
|
||||
Size uint64 // total capacity in bytes
|
||||
Alloc uint64 // allocated bytes
|
||||
Free uint64 // free bytes
|
||||
Health string // ONLINE, DEGRADED, FAULTED, ...
|
||||
}
|
||||
|
||||
// PoolKernelStat is the inexpensive pool telemetry exposed by the ZFS kernel.
|
||||
|
||||
@@ -1,320 +0,0 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/henrygd/beszel/agent/zfs"
|
||||
"github.com/henrygd/beszel/internal/entities/system"
|
||||
zfsentity "github.com/henrygd/beszel/internal/entities/zfs"
|
||||
)
|
||||
|
||||
// zfsDatasetUsage holds usage values for a ZFS dataset mountpoint.
|
||||
type zfsDatasetUsage struct {
|
||||
used uint64
|
||||
avail uint64
|
||||
}
|
||||
|
||||
// datasetUsageRefreshInterval controls how often `zfs list` is re-run for the
|
||||
// mountpoint usage map. Dataset inventory changes rarely.
|
||||
const datasetUsageRefreshInterval = 5 * time.Minute
|
||||
|
||||
// poolStatsRefreshInterval controls how often `zpool list` is re-run for pool
|
||||
// capacity. Health and I/O are read from procfs on Linux, so the utility only
|
||||
// needs to refresh slow-moving space accounting.
|
||||
const poolStatsRefreshInterval = time.Minute
|
||||
|
||||
type poolKernelSample struct {
|
||||
nread uint64
|
||||
nwrite uint64
|
||||
at time.Time
|
||||
}
|
||||
|
||||
// ZfsManager collects ZFS pool and dataset statistics. Collection functions
|
||||
// are fields so unit tests can substitute them (same pattern as
|
||||
// diskDiscovery.usageFn). It is safe for concurrent use by a single goroutine
|
||||
// only; callers must hold the agent lock like updateDiskUsage does.
|
||||
type ZfsManager struct {
|
||||
poolStatsFn func() ([]zfs.PoolStat, error) // capacity/health source
|
||||
datasetsFn func() ([]zfs.Dataset, error) // dataset inventory source
|
||||
kernelStatsFn func() ([]zfs.PoolKernelStat, error) // procfs pool state/I/O source
|
||||
poolStatusesFn func() ([]zfs.PoolStatus, error) // scrub/vdev detail source
|
||||
|
||||
poolData []zfs.PoolStat // cached pool inventory (TTL below)
|
||||
lastPoolStats time.Time
|
||||
kernelSamples map[string]poolKernelSample
|
||||
|
||||
datasetUsage map[string]zfsDatasetUsage // mountpoint -> usage
|
||||
lastUsageRefresh time.Time
|
||||
|
||||
// Detail data (pools, vdevs, scrub, datasets) is cached and refreshed on
|
||||
// an interval. Accessed from handler goroutines, so it is mutex-protected.
|
||||
detailMu sync.Mutex
|
||||
detail *zfsentity.ZfsData
|
||||
lastDetailRefresh time.Time
|
||||
detailInterval time.Duration
|
||||
}
|
||||
|
||||
// newZfsManager creates a ZfsManager wired to the system's ZFS utilities.
|
||||
func newZfsManager() *ZfsManager {
|
||||
return &ZfsManager{
|
||||
poolStatsFn: zfs.PoolStats,
|
||||
datasetsFn: zfs.Datasets,
|
||||
kernelStatsFn: zfs.PoolKernelStats,
|
||||
poolStatusesFn: zfs.PoolStatuses,
|
||||
detailInterval: time.Hour,
|
||||
}
|
||||
}
|
||||
|
||||
// Update refreshes systemStats.ZfsPools with the latest pool data. I/O
|
||||
// throughput and health come from inexpensive kernel kstats on Linux. Pool
|
||||
// capacity and dataset usage come from separately cached utility calls. It is
|
||||
// a no-op when ZFS is absent.
|
||||
func (zm *ZfsManager) Update(systemStats *system.Stats) {
|
||||
pools := zm.poolStats()
|
||||
if len(pools) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
kernelStats, ioRates := zm.kernelStats()
|
||||
|
||||
if systemStats.ZfsPools == nil {
|
||||
systemStats.ZfsPools = make(map[string]*system.ZfsPool, len(pools))
|
||||
}
|
||||
for i := range pools {
|
||||
pool := &pools[i]
|
||||
// Full precision, matching the dataset values below; the frontend
|
||||
// formats any magnitude.
|
||||
stats := &system.ZfsPool{
|
||||
Total: float64(pool.Size) / (1024 * 1024 * 1024),
|
||||
Used: float64(pool.Alloc) / (1024 * 1024 * 1024),
|
||||
Health: pool.Health,
|
||||
}
|
||||
if kernel, exists := kernelStats[pool.Name]; exists && kernel.Health != "" {
|
||||
stats.Health = kernel.Health
|
||||
}
|
||||
if io, exists := ioRates[pool.Name]; exists {
|
||||
stats.ReadBytes = io.NRead
|
||||
stats.WriteBytes = io.NWrite
|
||||
}
|
||||
slog.Debug("ZFS pool sample", "pool", pool.Name, "health", stats.Health, "used_gb", stats.Used, "read_bps", stats.ReadBytes, "write_bps", stats.WriteBytes)
|
||||
systemStats.ZfsPools[pool.Name] = stats
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// poolStats returns the cached pool inventory, re-running `zpool list` at most
|
||||
// every poolStatsRefreshInterval. On failure the previous inventory is
|
||||
// retained and the refresh is retried on the next cadence.
|
||||
func (zm *ZfsManager) poolStats() []zfs.PoolStat {
|
||||
if zm.lastPoolStats.IsZero() || time.Since(zm.lastPoolStats) >= poolStatsRefreshInterval {
|
||||
pools, err := zm.poolStatsFn()
|
||||
if err != nil {
|
||||
slog.Debug("ZFS pool stats unavailable", "err", err)
|
||||
} else {
|
||||
zm.poolData = pools
|
||||
}
|
||||
zm.lastPoolStats = time.Now()
|
||||
}
|
||||
return zm.poolData
|
||||
}
|
||||
|
||||
// kernelStats reads cumulative pool counters and converts them to per-second
|
||||
// rates. Counter decreases indicate a pool export/import and reset the
|
||||
// baseline instead of producing an underflow spike.
|
||||
func (zm *ZfsManager) kernelStats() (map[string]zfs.PoolKernelStat, map[string]zfs.PoolIoStats) {
|
||||
if zm.kernelStatsFn == nil {
|
||||
return nil, nil
|
||||
}
|
||||
stats, err := zm.kernelStatsFn()
|
||||
if err != nil {
|
||||
slog.Debug("ZFS kernel stats unavailable", "err", err)
|
||||
return nil, nil
|
||||
}
|
||||
now := time.Now()
|
||||
byName := make(map[string]zfs.PoolKernelStat, len(stats))
|
||||
rates := make(map[string]zfs.PoolIoStats, len(stats))
|
||||
nextSamples := make(map[string]poolKernelSample, len(stats))
|
||||
for _, stat := range stats {
|
||||
byName[stat.Name] = stat
|
||||
if previous, ok := zm.kernelSamples[stat.Name]; ok && now.After(previous.at) &&
|
||||
stat.NRead >= previous.nread && stat.NWrite >= previous.nwrite {
|
||||
seconds := now.Sub(previous.at).Seconds()
|
||||
rates[stat.Name] = zfs.PoolIoStats{
|
||||
NRead: uint64(float64(stat.NRead-previous.nread) / seconds),
|
||||
NWrite: uint64(float64(stat.NWrite-previous.nwrite) / seconds),
|
||||
}
|
||||
}
|
||||
nextSamples[stat.Name] = poolKernelSample{nread: stat.NRead, nwrite: stat.NWrite, at: now}
|
||||
}
|
||||
zm.kernelSamples = nextSamples
|
||||
return byName, rates
|
||||
}
|
||||
|
||||
// refreshDatasetUsage re-runs `zfs list` when the refresh window has elapsed
|
||||
// and rebuilds the mountpoint-keyed usage map.
|
||||
func (zm *ZfsManager) refreshDatasetUsage() {
|
||||
if !zm.lastUsageRefresh.IsZero() && time.Since(zm.lastUsageRefresh) < datasetUsageRefreshInterval {
|
||||
return
|
||||
}
|
||||
datasets, err := zm.datasetsFn()
|
||||
if err != nil {
|
||||
slog.Debug("ZFS dataset usage unavailable", "err", err)
|
||||
} else {
|
||||
usage := make(map[string]zfsDatasetUsage, len(datasets))
|
||||
for _, ds := range datasets {
|
||||
if ds.Mountpoint != "" && ds.Mountpoint != "-" {
|
||||
usage[ds.Mountpoint] = zfsDatasetUsage{used: ds.Used, avail: ds.Avail}
|
||||
}
|
||||
}
|
||||
zm.datasetUsage = usage
|
||||
}
|
||||
zm.lastUsageRefresh = time.Now()
|
||||
}
|
||||
|
||||
// DatasetUsage returns ZFS dataset usage keyed by mountpoint, refreshed at
|
||||
// most every datasetUsageRefreshInterval. On failure the previous map is
|
||||
// retained and a debug log is emitted.
|
||||
func (zm *ZfsManager) DatasetUsage() map[string]zfsDatasetUsage {
|
||||
zm.refreshDatasetUsage()
|
||||
return zm.datasetUsage
|
||||
}
|
||||
|
||||
// GetDetail returns ZFS detail data (pool health, scrub, vdevs, datasets).
|
||||
// Scheduled requests use the cached snapshot until stale; manual requests can
|
||||
// force collection. On failure the previous snapshot is retained.
|
||||
func (zm *ZfsManager) GetDetail(force bool) *zfsentity.ZfsData {
|
||||
zm.detailMu.Lock()
|
||||
defer zm.detailMu.Unlock()
|
||||
|
||||
if force || zm.detail == nil || time.Since(zm.lastDetailRefresh) >= zm.detailInterval {
|
||||
if data, err := zm.collectDetail(zm.detail); err != nil {
|
||||
slog.Debug("ZFS detail collection failed", "err", err)
|
||||
if zm.detail == nil {
|
||||
return &zfsentity.ZfsData{}
|
||||
}
|
||||
return &zfsentity.ZfsData{Pools: zm.detail.Pools}
|
||||
} else {
|
||||
zm.detail = data
|
||||
zm.lastDetailRefresh = time.Now()
|
||||
}
|
||||
}
|
||||
if zm.detail == nil {
|
||||
return &zfsentity.ZfsData{}
|
||||
}
|
||||
return zm.detail
|
||||
}
|
||||
|
||||
// collectDetail builds a ZfsData payload from the current system state.
|
||||
func (zm *ZfsManager) collectDetail(previous *zfsentity.ZfsData) (*zfsentity.ZfsData, error) {
|
||||
pools, err := zm.poolStatsFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(pools) == 0 {
|
||||
return &zfsentity.ZfsData{Pools: []*zfsentity.PoolDetail{}, Complete: true}, nil
|
||||
}
|
||||
|
||||
statuses, statusErr := zm.poolStatusesFn()
|
||||
if statusErr != nil {
|
||||
slog.Debug("ZFS pool status unavailable", "err", statusErr)
|
||||
}
|
||||
datasets, datasetsErr := zm.datasetsFn()
|
||||
if datasetsErr != nil {
|
||||
slog.Debug("ZFS datasets unavailable", "err", datasetsErr)
|
||||
}
|
||||
|
||||
statusByPool := make(map[string]zfs.PoolStatus, len(statuses))
|
||||
for _, st := range statuses {
|
||||
statusByPool[st.Name] = st
|
||||
}
|
||||
|
||||
previousByPool := make(map[string]*zfsentity.PoolDetail)
|
||||
if previous != nil {
|
||||
for _, pool := range previous.Pools {
|
||||
if pool != nil {
|
||||
previousByPool[pool.Name] = pool
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data := &zfsentity.ZfsData{Pools: make([]*zfsentity.PoolDetail, 0, len(pools)), Complete: true}
|
||||
for i := range pools {
|
||||
p := &pools[i]
|
||||
detail := &zfsentity.PoolDetail{
|
||||
Name: p.Name,
|
||||
Health: p.Health,
|
||||
Size: p.Size,
|
||||
Alloc: p.Alloc,
|
||||
Free: p.Free,
|
||||
}
|
||||
if st, ok := statusByPool[p.Name]; statusErr == nil && ok {
|
||||
if st.Scrub.State != "" && st.Scrub.State != "NONE" {
|
||||
detail.Scrub = &zfsentity.Scrub{
|
||||
State: st.Scrub.State,
|
||||
Progress: st.Scrub.Progress,
|
||||
Errors: st.Scrub.Errors,
|
||||
}
|
||||
}
|
||||
for _, v := range st.Vdevs {
|
||||
detail.Vdevs = append(detail.Vdevs, &zfsentity.Vdev{
|
||||
Name: v.Name,
|
||||
State: v.State,
|
||||
ReadErrs: v.ReadErrs,
|
||||
WriteErrs: v.WriteErrs,
|
||||
ChecksumErrs: v.ChecksumErrs,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if cached := previousByPool[p.Name]; cached != nil {
|
||||
detail.Scrub = cached.Scrub
|
||||
detail.Vdevs = cached.Vdevs
|
||||
}
|
||||
}
|
||||
if datasetsErr == nil {
|
||||
foundDataset := false
|
||||
for _, ds := range datasets {
|
||||
if poolOfDataset(ds.Name) == p.Name {
|
||||
foundDataset = true
|
||||
detail.Datasets = append(detail.Datasets, &zfsentity.Dataset{
|
||||
Name: ds.Name,
|
||||
Used: ds.Used,
|
||||
Avail: ds.Avail,
|
||||
Mountpoint: ds.Mountpoint,
|
||||
})
|
||||
}
|
||||
}
|
||||
if !foundDataset {
|
||||
if cached := previousByPool[p.Name]; cached != nil {
|
||||
detail.Datasets = cached.Datasets
|
||||
}
|
||||
}
|
||||
} else if cached := previousByPool[p.Name]; cached != nil {
|
||||
detail.Datasets = cached.Datasets
|
||||
}
|
||||
data.Pools = append(data.Pools, detail)
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// poolOfDataset returns the pool name for a dataset name (everything before
|
||||
// the first '/'). Datasets without a separator belong to a pool of the same
|
||||
// name.
|
||||
func poolOfDataset(name string) string {
|
||||
if idx := strings.IndexByte(name, '/'); idx >= 0 {
|
||||
return name[:idx]
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
// ZfsMountpoints returns the set of mountpoints backed by ZFS datasets.
|
||||
func (zm *ZfsManager) ZfsMountpoints() map[string]bool {
|
||||
usage := zm.DatasetUsage()
|
||||
mountpoints := make(map[string]bool, len(usage))
|
||||
for mountpoint := range usage {
|
||||
mountpoints[mountpoint] = true
|
||||
}
|
||||
return mountpoints
|
||||
}
|
||||
@@ -1,245 +0,0 @@
|
||||
//go:build testing
|
||||
|
||||
package agent
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/henrygd/beszel/agent/zfs"
|
||||
"github.com/henrygd/beszel/internal/entities/system"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestUpdatePopulatesZfsPools(t *testing.T) {
|
||||
zm := &ZfsManager{}
|
||||
zm.poolStatsFn = func() ([]zfs.PoolStat, error) {
|
||||
return []zfs.PoolStat{{Name: "tank", Size: 23999000000000, Alloc: 12000000000000, Free: 11999000000000, Health: "DEGRADED"}}, nil
|
||||
}
|
||||
zm.datasetsFn = func() ([]zfs.Dataset, error) {
|
||||
return []zfs.Dataset{
|
||||
{Name: "tank/apps", Used: 5000000000000, Avail: 11999000000000, Mountpoint: "/tank/apps"},
|
||||
{Name: "tank/backup", Used: 6000000000000, Avail: 11999000000000, Mountpoint: "/tank/backup"},
|
||||
// Small zvol (Proxmox VM EFI disk): must not round to zero.
|
||||
{Name: "rpool/vm-100-disk-2", Used: 4194304, Avail: 0, Mountpoint: "-"},
|
||||
}, nil
|
||||
}
|
||||
var kernelCalls int
|
||||
zm.kernelStatsFn = func() ([]zfs.PoolKernelStat, error) {
|
||||
kernelCalls++
|
||||
return []zfs.PoolKernelStat{{
|
||||
Name: "tank", Health: "ONLINE",
|
||||
NRead: uint64(kernelCalls-1) * 1250, NWrite: uint64(kernelCalls-1) * 5120,
|
||||
}}, nil
|
||||
}
|
||||
|
||||
var stats system.Stats
|
||||
// The first kernel sample establishes the cumulative-counter baseline.
|
||||
zm.Update(&stats)
|
||||
zm.kernelSamples["tank"] = poolKernelSample{at: time.Now().Add(-time.Second)}
|
||||
zm.Update(&stats)
|
||||
require.NotNil(t, stats.ZfsPools)
|
||||
require.Contains(t, stats.ZfsPools, "tank")
|
||||
assert.InDelta(t, 22350.8105, stats.ZfsPools["tank"].Total, 0.0001) // Size in GiB
|
||||
assert.InDelta(t, 11175.8709, stats.ZfsPools["tank"].Used, 0.0001) // Alloc in GiB
|
||||
assert.Equal(t, "ONLINE", stats.ZfsPools["tank"].Health)
|
||||
assert.InDelta(t, 1250, stats.ZfsPools["tank"].ReadBytes, 5)
|
||||
assert.InDelta(t, 5120, stats.ZfsPools["tank"].WriteBytes, 5)
|
||||
|
||||
}
|
||||
|
||||
// TestUpdateKernelStatsMissing verifies pools without a kernel sample report zero
|
||||
// I/O instead of erroring.
|
||||
func TestUpdateKernelStatsMissing(t *testing.T) {
|
||||
zm := &ZfsManager{}
|
||||
zm.poolStatsFn = func() ([]zfs.PoolStat, error) {
|
||||
return []zfs.PoolStat{{Name: "tank", Size: 1, Alloc: 1, Health: "ONLINE"}}, nil
|
||||
}
|
||||
zm.datasetsFn = func() ([]zfs.Dataset, error) { return nil, nil }
|
||||
zm.kernelStatsFn = func() ([]zfs.PoolKernelStat, error) {
|
||||
return nil, zfs.ErrNoZfs
|
||||
}
|
||||
|
||||
var stats system.Stats
|
||||
zm.Update(&stats)
|
||||
require.NotNil(t, stats.ZfsPools)
|
||||
assert.Equal(t, uint64(0), stats.ZfsPools["tank"].ReadBytes)
|
||||
assert.Equal(t, uint64(0), stats.ZfsPools["tank"].WriteBytes)
|
||||
}
|
||||
|
||||
func TestUpdateKernelCounterReset(t *testing.T) {
|
||||
zm := &ZfsManager{}
|
||||
zm.poolStatsFn = func() ([]zfs.PoolStat, error) {
|
||||
return []zfs.PoolStat{{Name: "tank", Health: "ONLINE"}}, nil
|
||||
}
|
||||
zm.datasetsFn = func() ([]zfs.Dataset, error) { return nil, nil }
|
||||
zm.kernelSamples = map[string]poolKernelSample{
|
||||
"tank": {nread: 100, nwrite: 200, at: time.Now().Add(-time.Second)},
|
||||
}
|
||||
zm.kernelStatsFn = func() ([]zfs.PoolKernelStat, error) {
|
||||
return []zfs.PoolKernelStat{{Name: "tank", Health: "ONLINE", NRead: 10, NWrite: 20}}, nil
|
||||
}
|
||||
|
||||
var stats system.Stats
|
||||
zm.Update(&stats)
|
||||
assert.Equal(t, uint64(0), stats.ZfsPools["tank"].ReadBytes)
|
||||
assert.Equal(t, uint64(0), stats.ZfsPools["tank"].WriteBytes)
|
||||
}
|
||||
|
||||
func TestUpdateNoZfs(t *testing.T) {
|
||||
zm := &ZfsManager{}
|
||||
calls := 0
|
||||
zm.poolStatsFn = func() ([]zfs.PoolStat, error) {
|
||||
calls++
|
||||
return nil, zfs.ErrNoZfs
|
||||
}
|
||||
|
||||
var stats system.Stats
|
||||
zm.Update(&stats)
|
||||
zm.Update(&stats)
|
||||
assert.Nil(t, stats.ZfsPools)
|
||||
assert.Equal(t, 1, calls, "failed pool discovery should be cached until the next refresh interval")
|
||||
}
|
||||
|
||||
func TestUpdateEmptyPools(t *testing.T) {
|
||||
zm := &ZfsManager{}
|
||||
calls := 0
|
||||
zm.poolStatsFn = func() ([]zfs.PoolStat, error) {
|
||||
calls++
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var stats system.Stats
|
||||
zm.Update(&stats)
|
||||
zm.Update(&stats)
|
||||
assert.Nil(t, stats.ZfsPools)
|
||||
assert.Equal(t, 1, calls, "an empty pool inventory should be cached until the next refresh interval")
|
||||
}
|
||||
|
||||
func TestDatasetUsage(t *testing.T) {
|
||||
zm := &ZfsManager{}
|
||||
calls := 0
|
||||
zm.datasetsFn = func() ([]zfs.Dataset, error) {
|
||||
calls++
|
||||
return []zfs.Dataset{
|
||||
{Name: "tank", Used: 12000000000000, Avail: 11999000000000, Mountpoint: "/tank"},
|
||||
{Name: "tank/apps", Used: 1000000000000, Avail: 11999000000000, Mountpoint: "/tank/apps"},
|
||||
{Name: "rpool", Used: 900000000000, Avail: 300000000000, Mountpoint: "-"}, // zvol/unmounted: excluded
|
||||
}, nil
|
||||
}
|
||||
|
||||
usage := zm.DatasetUsage()
|
||||
require.Len(t, usage, 2)
|
||||
assert.Equal(t, zfsDatasetUsage{used: 12000000000000, avail: 11999000000000}, usage["/tank"])
|
||||
assert.Equal(t, zfsDatasetUsage{used: 1000000000000, avail: 11999000000000}, usage["/tank/apps"])
|
||||
assert.Equal(t, 1, calls)
|
||||
|
||||
// Second call within the refresh window must not re-run the collector.
|
||||
zm.DatasetUsage()
|
||||
assert.Equal(t, 1, calls)
|
||||
}
|
||||
|
||||
func TestDatasetUsageRefreshOnErrorKeepsPrevious(t *testing.T) {
|
||||
zm := &ZfsManager{}
|
||||
zm.datasetsFn = func() ([]zfs.Dataset, error) {
|
||||
return []zfs.Dataset{{Name: "tank", Used: 1, Avail: 1, Mountpoint: "/tank"}}, nil
|
||||
}
|
||||
assert.Len(t, zm.DatasetUsage(), 1)
|
||||
|
||||
// Force refresh window expiry, then a failing collector.
|
||||
zm.lastUsageRefresh = time.Now().Add(-10 * time.Minute)
|
||||
zm.datasetsFn = func() ([]zfs.Dataset, error) {
|
||||
return nil, zfs.ErrNoZfs
|
||||
}
|
||||
usage := zm.DatasetUsage()
|
||||
assert.Len(t, usage, 1, "previous usage should be retained on error")
|
||||
}
|
||||
|
||||
func TestGetDetailForceRefresh(t *testing.T) {
|
||||
zm := &ZfsManager{detailInterval: time.Hour}
|
||||
poolCalls := 0
|
||||
zm.poolStatsFn = func() ([]zfs.PoolStat, error) {
|
||||
poolCalls++
|
||||
return []zfs.PoolStat{{Name: "tank", Alloc: uint64(poolCalls)}}, nil
|
||||
}
|
||||
zm.poolStatusesFn = func() ([]zfs.PoolStatus, error) { return nil, nil }
|
||||
zm.datasetsFn = func() ([]zfs.Dataset, error) { return nil, nil }
|
||||
|
||||
first := zm.GetDetail(false)
|
||||
assert.True(t, first.Complete)
|
||||
require.Len(t, first.Pools, 1)
|
||||
assert.Equal(t, uint64(1), first.Pools[0].Alloc)
|
||||
|
||||
cached := zm.GetDetail(false)
|
||||
require.Len(t, cached.Pools, 1)
|
||||
assert.Equal(t, uint64(1), cached.Pools[0].Alloc)
|
||||
assert.Equal(t, 1, poolCalls)
|
||||
|
||||
refreshed := zm.GetDetail(true)
|
||||
assert.True(t, refreshed.Complete)
|
||||
require.Len(t, refreshed.Pools, 1)
|
||||
assert.Equal(t, uint64(2), refreshed.Pools[0].Alloc)
|
||||
assert.Equal(t, 2, poolCalls)
|
||||
}
|
||||
|
||||
func TestGetDetailSuccessfulEmptyInventoryClearsCache(t *testing.T) {
|
||||
zm := &ZfsManager{detailInterval: time.Hour}
|
||||
zm.poolStatsFn = func() ([]zfs.PoolStat, error) {
|
||||
return []zfs.PoolStat{{Name: "tank"}}, nil
|
||||
}
|
||||
zm.poolStatusesFn = func() ([]zfs.PoolStatus, error) { return nil, nil }
|
||||
zm.datasetsFn = func() ([]zfs.Dataset, error) { return nil, nil }
|
||||
|
||||
require.Len(t, zm.GetDetail(false).Pools, 1)
|
||||
zm.poolStatsFn = func() ([]zfs.PoolStat, error) { return nil, nil }
|
||||
empty := zm.GetDetail(true)
|
||||
assert.True(t, empty.Complete)
|
||||
assert.Empty(t, empty.Pools)
|
||||
}
|
||||
|
||||
func TestGetDetailFailureReturnsIncompleteCachedInventory(t *testing.T) {
|
||||
zm := &ZfsManager{detailInterval: time.Hour}
|
||||
zm.poolStatsFn = func() ([]zfs.PoolStat, error) {
|
||||
return []zfs.PoolStat{{Name: "tank"}}, nil
|
||||
}
|
||||
zm.poolStatusesFn = func() ([]zfs.PoolStatus, error) {
|
||||
return []zfs.PoolStatus{{Name: "tank", Vdevs: []zfs.VdevStatus{{Name: "mirror-0"}}}}, nil
|
||||
}
|
||||
zm.datasetsFn = func() ([]zfs.Dataset, error) {
|
||||
return []zfs.Dataset{{Name: "tank/data"}}, nil
|
||||
}
|
||||
first := zm.GetDetail(false)
|
||||
require.True(t, first.Complete)
|
||||
require.Len(t, first.Pools[0].Vdevs, 1)
|
||||
require.Len(t, first.Pools[0].Datasets, 1)
|
||||
|
||||
zm.poolStatusesFn = func() ([]zfs.PoolStatus, error) { return nil, zfs.ErrNoZfs }
|
||||
zm.datasetsFn = func() ([]zfs.Dataset, error) { return nil, zfs.ErrNoZfs }
|
||||
partial := zm.GetDetail(true)
|
||||
require.True(t, partial.Complete)
|
||||
require.Len(t, partial.Pools[0].Vdevs, 1)
|
||||
require.Len(t, partial.Pools[0].Datasets, 1)
|
||||
|
||||
zm.poolStatsFn = func() ([]zfs.PoolStat, error) { return nil, zfs.ErrNoZfs }
|
||||
lastSuccessfulRefresh := zm.lastDetailRefresh
|
||||
failed := zm.GetDetail(true)
|
||||
assert.False(t, failed.Complete)
|
||||
require.Len(t, failed.Pools, 1)
|
||||
assert.Equal(t, "tank", failed.Pools[0].Name)
|
||||
assert.Equal(t, lastSuccessfulRefresh, zm.lastDetailRefresh)
|
||||
}
|
||||
|
||||
func TestZfsMountpoints(t *testing.T) {
|
||||
zm := &ZfsManager{}
|
||||
zm.datasetsFn = func() ([]zfs.Dataset, error) {
|
||||
return []zfs.Dataset{
|
||||
{Name: "tank", Mountpoint: "/tank"},
|
||||
{Name: "rpool/ROOT/pve-1", Mountpoint: "/"},
|
||||
}, nil
|
||||
}
|
||||
mountpoints := zm.ZfsMountpoints()
|
||||
assert.Len(t, mountpoints, 2)
|
||||
assert.True(t, mountpoints["/tank"])
|
||||
assert.True(t, mountpoints["/"])
|
||||
}
|
||||
@@ -5,12 +5,13 @@ go 1.27.1
|
||||
require (
|
||||
github.com/blang/semver v3.5.1+incompatible
|
||||
github.com/coreos/go-systemd/v22 v22.7.0
|
||||
github.com/distribution/reference v0.6.0
|
||||
github.com/ebitengine/purego v0.11.0
|
||||
github.com/fxamacker/cbor/v2 v2.9.3
|
||||
github.com/gliderlabs/ssh v0.3.8
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/lxzan/gws v1.10.1
|
||||
github.com/nicholas-fedor/shoutrrr v0.19.0
|
||||
github.com/nicholas-fedor/shoutrrr v0.20.0
|
||||
github.com/opencontainers/go-digest v1.0.0
|
||||
github.com/pocketbase/dbx v1.12.0
|
||||
github.com/pocketbase/pocketbase v0.40.2
|
||||
github.com/shirou/gopsutil/v4 v4.26.8
|
||||
@@ -41,6 +42,7 @@ require (
|
||||
github.com/go-sql-driver/mysql v1.9.1 // indirect
|
||||
github.com/godbus/dbus/v5 v5.2.2 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.3 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/klauspost/compress v1.20.0 // indirect
|
||||
|
||||
@@ -15,6 +15,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6N
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
|
||||
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
||||
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/domodwyer/mailyak/v3 v3.6.2 h1:x3tGMsyFhTCaxp6ycgR0FE/bu5QiNp+hetUuCOBXMn8=
|
||||
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=
|
||||
@@ -54,8 +56,8 @@ github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArs
|
||||
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-20260902005441-ca85771921e4 h1:/6mPXfWmhv8eKck12I0YNIcIjwHtxP3YRIMKiEgTjWg=
|
||||
github.com/google/pprof v0.0.0-20260902005441-ca85771921e4/go.mod h1:jl5iWTm0/hd5PjEYEOuwAJ57L/CibdZfrqZ5XA5GrCk=
|
||||
github.com/google/pprof v0.0.0-20260906184651-6331bc6350fe h1:QAinXoAFJdGQYztXn3VpFey7KCwpedbZ/EkzbplQ0cY=
|
||||
github.com/google/pprof v0.0.0-20260906184651-6331bc6350fe/go.mod h1:jl5iWTm0/hd5PjEYEOuwAJ57L/CibdZfrqZ5XA5GrCk=
|
||||
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/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
@@ -83,12 +85,14 @@ github.com/mattn/go-isatty v0.0.24 h1:tGZZoVgT/KiqK1c8ocVLeDS8BSWMRd47J3Lbz7vsRe
|
||||
github.com/mattn/go-isatty v0.0.24/go.mod h1:nMCL3Zebbrt45jsMDgnfIwz6ydEQApk5oEI3HqDio6A=
|
||||
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.19.0 h1:Rl6bpK3DXuR2Trtx2JV8t+wjUwkHdRHrc8nBKoEpHr0=
|
||||
github.com/nicholas-fedor/shoutrrr v0.19.0/go.mod h1:Glfdi8AGTbnEn2k2+hW62n8oL0i9vqRVFtXaUIthNks=
|
||||
github.com/nicholas-fedor/shoutrrr v0.20.0 h1:hMAxIYlfAeZ1FcTDgU0kUOvVXUsOirWo8IWlnzGLkac=
|
||||
github.com/nicholas-fedor/shoutrrr v0.20.0/go.mod h1:hgde37yNWCXh8+N6WemyDRMNYLOFTf326GsBx8Z7CFA=
|
||||
github.com/onsi/ginkgo/v2 v2.32.1 h1:6tlvcDm/3sE8lGJbZ4+d4mO3RLy24/tQWOFzVSQNIfw=
|
||||
github.com/onsi/ginkgo/v2 v2.32.1/go.mod h1:+aXOY+vzZ5mu2iI2HpTZUPmM//oQfsNFX6gU9kNcA44=
|
||||
github.com/onsi/gomega v1.43.0 h1:VlG/1FxqNxhSO+lq/OHBNaaqwiBK/mO8JbVkX9Y+FeU=
|
||||
github.com/onsi/gomega v1.43.0/go.mod h1:REff/hsDsodHoKlWsP2mAPhu1+5/6hVYNf9rIEBpeSg=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pocketbase/dbx v1.12.0 h1:/oLErM+A0b4xI0PWTGPqSDVjzix48PqI/bng2l0PzoA=
|
||||
github.com/pocketbase/dbx v1.12.0/go.mod h1:xXRCIAKTHMgUCyCKZm55pUOdvFziJjQfXaWKhu2vhMs=
|
||||
|
||||
@@ -20,10 +20,10 @@ type hubLike interface {
|
||||
}
|
||||
|
||||
type AlertManager struct {
|
||||
hub hubLike
|
||||
stopOnce sync.Once
|
||||
pendingAlerts sync.Map
|
||||
alertsCache *AlertsCache
|
||||
hub hubLike
|
||||
stopOnce sync.Once
|
||||
pendingAlerts sync.Map
|
||||
alertsCache *AlertsCache
|
||||
}
|
||||
|
||||
type AlertMessageData struct {
|
||||
@@ -66,6 +66,7 @@ type SystemAlertGPUData struct {
|
||||
}
|
||||
|
||||
type SystemAlertZfsPool struct {
|
||||
Raw bool `json:"raw,omitempty"`
|
||||
Total float64 `json:"d"`
|
||||
Used float64 `json:"du"`
|
||||
}
|
||||
@@ -231,8 +232,20 @@ func (am *AlertManager) SendAlert(data AlertMessageData) error {
|
||||
am.hub.Logger().Error("Failed to unmarshal user settings", "err", err)
|
||||
}
|
||||
// send alerts via webhooks
|
||||
send := sendPublicNotification
|
||||
if len(userAlertSettings.Webhooks) > 0 {
|
||||
// Read the owner's current role at delivery time, including for URLs
|
||||
// saved before an admin was demoted. Never fall back on lookup failure.
|
||||
owner, err := am.hub.FindRecordById("users", data.UserID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("load notification owner: %w", err)
|
||||
}
|
||||
if owner.GetString("role") == "admin" {
|
||||
send = shoutrrr.Send
|
||||
}
|
||||
}
|
||||
for _, webhook := range userAlertSettings.Webhooks {
|
||||
if err := am.SendShoutrrrAlert(webhook, data.Title, data.Message, data.Link, data.LinkText); err != nil {
|
||||
if err := am.sendShoutrrrAlert(webhook, data.Title, data.Message, data.Link, data.LinkText, send); err != nil {
|
||||
am.hub.Logger().Error("Failed to send shoutrrr alert", "err", err)
|
||||
}
|
||||
}
|
||||
@@ -263,6 +276,10 @@ func (am *AlertManager) SendAlert(data AlertMessageData) error {
|
||||
|
||||
// SendShoutrrrAlert sends an alert via a Shoutrrr URL
|
||||
func (am *AlertManager) SendShoutrrrAlert(notificationUrl, title, message, link, linkText string) error {
|
||||
return am.sendShoutrrrAlert(notificationUrl, title, message, link, linkText, shoutrrr.Send)
|
||||
}
|
||||
|
||||
func (am *AlertManager) sendShoutrrrAlert(notificationUrl, title, message, link, linkText string, send func(string, string) error) error {
|
||||
// Parse the URL
|
||||
parsedURL, err := url.Parse(notificationUrl)
|
||||
if err != nil {
|
||||
@@ -305,7 +322,7 @@ func (am *AlertManager) SendShoutrrrAlert(notificationUrl, title, message, link,
|
||||
parsedURL.RawQuery = queryParams.Encode()
|
||||
// log.Println("URL after modification:", parsedURL.String())
|
||||
|
||||
err = shoutrrr.Send(parsedURL.String(), message)
|
||||
err = send(parsedURL.String(), message)
|
||||
|
||||
if err == nil {
|
||||
am.hub.Logger().Info("Sent shoutrrr alert", "title", title)
|
||||
|
||||
@@ -3,13 +3,11 @@ package alerts
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/henrygd/beszel/internal/hub/utils"
|
||||
"github.com/nicholas-fedor/shoutrrr"
|
||||
"github.com/pocketbase/dbx"
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
)
|
||||
@@ -147,72 +145,16 @@ func (am *AlertManager) SendTestNotification(e *core.RequestEvent) error {
|
||||
if err != nil || data.URL == "" {
|
||||
return e.BadRequestError("URL is required", err)
|
||||
}
|
||||
// Only allow admins to send test notifications to internal URLs
|
||||
send := shoutrrr.Send
|
||||
if !e.Auth.IsSuperuser() && e.Auth.GetString("role") != "admin" {
|
||||
internalURL, err := isInternalURL(data.URL)
|
||||
if err != nil {
|
||||
return e.BadRequestError(err.Error(), nil)
|
||||
}
|
||||
if internalURL {
|
||||
return e.ForbiddenError("Only admins can send to internal destinations", nil)
|
||||
}
|
||||
send = sendPublicNotification
|
||||
}
|
||||
err = am.sendShoutrrrAlert(data.URL, "Test Alert", "This is a notification from Beszel.", am.hub.Settings().Meta.AppURL, "View Beszel", send)
|
||||
if errors.Is(err, errInternalDestination) || errors.Is(err, errUnrestrictedService) {
|
||||
return e.ForbiddenError(err.Error(), nil)
|
||||
}
|
||||
err = am.SendShoutrrrAlert(data.URL, "Test Alert", "This is a notification from Beszel.", am.hub.Settings().Meta.AppURL, "View Beszel")
|
||||
if err != nil {
|
||||
return e.JSON(200, map[string]string{"err": err.Error()})
|
||||
}
|
||||
return e.JSON(200, map[string]bool{"err": false})
|
||||
}
|
||||
|
||||
// isInternalURL checks if the given shoutrrr URL points to an internal destination (localhost or private IP)
|
||||
func isInternalURL(rawURL string) (bool, error) {
|
||||
parsedURL, err := url.Parse(rawURL)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
host := parsedURL.Hostname()
|
||||
if host == "" {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if strings.EqualFold(host, "localhost") {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if ip := net.ParseIP(host); ip != nil {
|
||||
return isInternalIP(ip), nil
|
||||
}
|
||||
|
||||
// Some Shoutrrr URLs use the host position for service identifiers rather than a
|
||||
// network hostname (for example, discord://token@webhookid). Restrict DNS lookups
|
||||
// to names that look like actual hostnames so valid service URLs keep working.
|
||||
if !strings.Contains(host, ".") {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
ips, err := net.LookupIP(host)
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if slices.ContainsFunc(ips, isInternalIP) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
var cgnatNetwork = &net.IPNet{
|
||||
IP: net.IPv4(100, 64, 0, 0),
|
||||
Mask: net.CIDRMask(10, 32),
|
||||
}
|
||||
|
||||
func isInternalIP(ip net.IP) bool {
|
||||
return ip.IsPrivate() ||
|
||||
ip.IsLoopback() ||
|
||||
ip.IsUnspecified() ||
|
||||
ip.IsLinkLocalUnicast() ||
|
||||
ip.IsMulticast() ||
|
||||
cgnatNetwork.Contains(ip)
|
||||
}
|
||||
|
||||
@@ -7,10 +7,11 @@ import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
|
||||
"github.com/henrygd/beszel/internal/alerts"
|
||||
beszelTests "github.com/henrygd/beszel/internal/tests"
|
||||
pbTests "github.com/pocketbase/pocketbase/tests"
|
||||
|
||||
@@ -29,43 +30,6 @@ func jsonReader(v any) io.Reader {
|
||||
return bytes.NewReader(data)
|
||||
}
|
||||
|
||||
func TestIsInternalURL(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
url string
|
||||
internal bool
|
||||
}{
|
||||
{name: "loopback ipv4", url: "generic://127.0.0.1", internal: true},
|
||||
{name: "private ipv4", url: "generic://10.0.0.1", internal: true},
|
||||
{name: "localhost hostname", url: "generic://localhost", internal: true},
|
||||
{name: "localhost with path", url: "generic+http://localhost/api/v1/postStuff", internal: true},
|
||||
{name: "loopback with port and path", url: "generic+http://127.0.0.1:8080/api/v1/postStuff", internal: true},
|
||||
{name: "public hostname", url: "generic+https://beszel.dev/api/v1/postStuff", internal: false},
|
||||
{name: "cloud metadata ipv4", url: "generic://169.254.169.254", internal: true},
|
||||
{name: "link-local ipv4", url: "generic://169.254.1.1", internal: true},
|
||||
{name: "link-local ipv6", url: "generic://[fe80::1]", internal: true},
|
||||
{name: "mapped link-local ipv4", url: "generic://[::ffff:169.254.169.254]", internal: true},
|
||||
{name: "cgnat lower boundary", url: "generic://100.64.0.0", internal: true},
|
||||
{name: "cgnat upper boundary", url: "generic://100.127.255.255", internal: true},
|
||||
{name: "below cgnat", url: "generic://100.63.255.255", internal: false},
|
||||
{name: "above cgnat", url: "generic://100.128.0.0", internal: false},
|
||||
{name: "multicast ipv4", url: "generic://224.0.0.1", internal: true},
|
||||
{name: "multicast ipv6", url: "generic://[ff02::1]", internal: true},
|
||||
{name: "public ipv4", url: "generic://8.8.8.8", internal: false},
|
||||
{name: "public ipv6", url: "generic://[2001:4860:4860::8888]", internal: false},
|
||||
{name: "token style service url", url: "discord://abc123@123456789", internal: false},
|
||||
{name: "single label service url", url: "slack://token@team/channel", internal: false},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
internal, err := alerts.IsInternalURL(testCase.url)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, testCase.internal, internal)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserAlertsApi(t *testing.T) {
|
||||
hub, _ := beszelTests.NewTestHub(t.TempDir())
|
||||
defer hub.Cleanup()
|
||||
@@ -457,6 +421,17 @@ func TestSendTestNotification(t *testing.T) {
|
||||
hub, user := beszelTests.GetHubWithUser(t)
|
||||
defer hub.Cleanup()
|
||||
|
||||
var delivered atomic.Int32
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
delivered.Add(1)
|
||||
}))
|
||||
defer server.Close()
|
||||
localURL := "generic+" + server.URL
|
||||
|
||||
readonlyUser, err := beszelTests.CreateUserWithRole(hub, "readonly@example.com", "password123", "readonly")
|
||||
assert.NoError(t, err)
|
||||
readonlyToken, err := readonlyUser.NewAuthToken()
|
||||
assert.NoError(t, err)
|
||||
userToken, err := user.NewAuthToken()
|
||||
|
||||
adminUser, err := beszelTests.CreateUserWithRole(hub, "admin@example.com", "password123", "admin")
|
||||
@@ -481,11 +456,11 @@ func TestSendTestNotification(t *testing.T) {
|
||||
ExpectedContent: []string{"requires valid"},
|
||||
TestAppFactory: testAppFactory,
|
||||
Body: jsonReader(map[string]any{
|
||||
"url": "generic://127.0.0.1",
|
||||
"url": localURL,
|
||||
}),
|
||||
},
|
||||
{
|
||||
Name: "POST /test-notification - with external auth should succeed",
|
||||
Name: "POST /test-notification - invalid service reports error",
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/beszel/test-notification",
|
||||
TestAppFactory: testAppFactory,
|
||||
@@ -493,7 +468,7 @@ func TestSendTestNotification(t *testing.T) {
|
||||
"Authorization": userToken,
|
||||
},
|
||||
Body: jsonReader(map[string]any{
|
||||
"url": "generic://8.8.8.8",
|
||||
"url": "unknown://example.com",
|
||||
}),
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{"\"err\":"},
|
||||
@@ -535,10 +510,10 @@ func TestSendTestNotification(t *testing.T) {
|
||||
"Authorization": adminUserToken,
|
||||
},
|
||||
Body: jsonReader(map[string]any{
|
||||
"url": "generic://127.0.0.1",
|
||||
"url": localURL,
|
||||
}),
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{"\"err\":"},
|
||||
ExpectedContent: []string{"\"err\":false"},
|
||||
},
|
||||
{
|
||||
Name: "POST /test-notification - internal url with superuser auth should succeed",
|
||||
@@ -549,14 +524,28 @@ func TestSendTestNotification(t *testing.T) {
|
||||
"Authorization": superuserToken,
|
||||
},
|
||||
Body: jsonReader(map[string]any{
|
||||
"url": "generic://127.0.0.1",
|
||||
"url": localURL,
|
||||
}),
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{"\"err\":"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, url := range []string{localURL, "smtp://user:pass@127.0.0.1/?fromAddress=sender@example.com&toAddresses=recipient@example.com", "mqtt://127.0.0.1/topic"} {
|
||||
scenarios = append(scenarios, beszelTests.ApiScenario{
|
||||
Name: "readonly cannot send to " + url,
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/beszel/test-notification",
|
||||
TestAppFactory: testAppFactory,
|
||||
Headers: map[string]string{"Authorization": readonlyToken},
|
||||
Body: jsonReader(map[string]any{"url": url}),
|
||||
ExpectedStatus: 403,
|
||||
ExpectedContent: []string{"Only admins"},
|
||||
})
|
||||
}
|
||||
|
||||
for _, scenario := range scenarios {
|
||||
scenario.Test(t)
|
||||
}
|
||||
assert.EqualValues(t, 2, delivered.Load(), "only admin and superuser requests should reach the server")
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ func (am *AlertManager) HandleSystemAlerts(systemRecord *core.Record, data *syst
|
||||
}
|
||||
}
|
||||
for _, pool := range data.Stats.ZfsPools {
|
||||
if pool != nil && pool.Total > 0 {
|
||||
if pool != nil && !pool.Raw && pool.Total > 0 {
|
||||
usedPct := pool.Used / pool.Total * 100
|
||||
if usedPct > maxUsedPct {
|
||||
maxUsedPct = usedPct
|
||||
@@ -256,7 +256,7 @@ func (am *AlertManager) HandleSystemAlerts(systemRecord *core.Record, data *syst
|
||||
}
|
||||
// add zfs pool usage from historical record
|
||||
for key, pool := range stats.ZfsPools {
|
||||
if pool.Total > 0 {
|
||||
if !pool.Raw && pool.Total > 0 {
|
||||
zfsKey := zfsDiskAlertKey(key)
|
||||
if _, ok := alert.mapSums[zfsKey]; !ok {
|
||||
alert.mapSums[zfsKey] = 0.0
|
||||
@@ -319,6 +319,11 @@ func (am *AlertManager) HandleSystemAlerts(systemRecord *core.Record, data *syst
|
||||
if sumPct > maxPct {
|
||||
maxPct = sumPct
|
||||
alert.descriptor = diskAlertDescriptor(key)
|
||||
if poolKey, ok := strings.CutPrefix(key, "zfs:"); ok {
|
||||
if pool := data.Stats.ZfsPools[poolKey]; pool != nil && pool.DisplayName != "" {
|
||||
alert.descriptor = diskAlertDescriptor(zfsDiskAlertKey(pool.DisplayName))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
alert.val = float64(maxPct / float32(alert.count))
|
||||
@@ -370,7 +375,7 @@ func zfsDiskAlertKey(poolName string) string {
|
||||
|
||||
func diskAlertDescriptor(key string) string {
|
||||
if poolName, ok := strings.CutPrefix(key, "zfs:"); ok {
|
||||
return fmt.Sprintf("Usage of ZFS pool %s", poolName)
|
||||
return fmt.Sprintf("Usage of storage pool %s", poolName)
|
||||
}
|
||||
return fmt.Sprintf("Usage of %s", key)
|
||||
}
|
||||
|
||||
@@ -100,10 +100,6 @@ func (am *AlertManager) SetAlertTriggered(alert CachedAlertData, triggered bool)
|
||||
return am.setAlertTriggered(alert, triggered)
|
||||
}
|
||||
|
||||
func IsInternalURL(rawURL string) (bool, error) {
|
||||
return isInternalURL(rawURL)
|
||||
}
|
||||
|
||||
// BuildContainerLogExcerpt exposes buildContainerLogExcerpt for testing.
|
||||
func BuildContainerLogExcerpt(raw string) string {
|
||||
return buildContainerLogExcerpt(raw)
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
//go:build testing
|
||||
|
||||
package alerts_test
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
|
||||
"github.com/henrygd/beszel/internal/alerts"
|
||||
beszelTests "github.com/henrygd/beszel/internal/tests"
|
||||
"github.com/pocketbase/dbx"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestPersistedWebhooksUseCurrentOwnerRole(t *testing.T) {
|
||||
hub, user := beszelTests.GetHubWithUser(t)
|
||||
defer hub.Cleanup()
|
||||
am := alerts.NewTestAlertManagerWithoutWorker(hub)
|
||||
|
||||
var delivered atomic.Int32
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
delivered.Add(1)
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
settings, err := hub.FindFirstRecordByFilter("user_settings", "user={:user}", dbx.Params{"user": user.Id})
|
||||
require.NoError(t, err)
|
||||
settings.Set("settings", alerts.UserNotificationSettings{Webhooks: []string{"generic+" + server.URL}})
|
||||
require.NoError(t, hub.Save(settings))
|
||||
message := alerts.AlertMessageData{UserID: user.Id, Title: "Test", Message: "Persisted webhook"}
|
||||
|
||||
// Keep the same URL and manager while changing roles, so cached privileges
|
||||
// or treating previously saved URLs as trusted would fail this test.
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
role string
|
||||
want int32
|
||||
}{
|
||||
{"regular user", "user", 0},
|
||||
{"readonly user", "readonly", 0},
|
||||
{"promoted admin", "admin", 1},
|
||||
{"demoted admin", "user", 1},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
user.Set("role", tc.role)
|
||||
require.NoError(t, hub.Save(user))
|
||||
// Webhook errors are logged; SendAlert continues to email delivery.
|
||||
require.NoError(t, am.SendAlert(message))
|
||||
require.Equal(t, tc.want, delivered.Load())
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("missing owner fails closed", func(t *testing.T) {
|
||||
// Model an orphaned settings record without deleting it through the
|
||||
// normal user deletion cascade.
|
||||
const missingOwner = "missingowner123"
|
||||
settings.Set("user", missingOwner)
|
||||
require.NoError(t, hub.SaveNoValidate(settings))
|
||||
message.UserID = missingOwner
|
||||
err := am.SendAlert(message)
|
||||
require.ErrorContains(t, err, "load notification owner")
|
||||
require.EqualValues(t, 1, delivered.Load())
|
||||
})
|
||||
}
|
||||
@@ -46,12 +46,15 @@ func (am *AlertManager) handleZfsPoolHealthAlert(e *core.RecordEvent, oldHealth
|
||||
}
|
||||
|
||||
systemName := systemRecord.GetString("name")
|
||||
poolName := e.Record.GetString("name")
|
||||
poolName := e.Record.GetString("display_name")
|
||||
if poolName == "" {
|
||||
poolName = e.Record.GetString("name")
|
||||
}
|
||||
|
||||
title := fmt.Sprintf("ZFS pool %s on %s: %s", newHealth, systemName, poolName)
|
||||
message := fmt.Sprintf("ZFS pool %s (%s) was first observed as %s", poolName, systemName, newHealth)
|
||||
title := fmt.Sprintf("Storage pool %s on %s: %s", newHealth, systemName, poolName)
|
||||
message := fmt.Sprintf("Storage pool %s (%s) was first observed as %s", poolName, systemName, newHealth)
|
||||
if oldSeverity > 0 {
|
||||
message = fmt.Sprintf("ZFS pool %s (%s) health changed from %s to %s", poolName, systemName, oldHealth, newHealth)
|
||||
message = fmt.Sprintf("Storage pool %s (%s) health changed from %s to %s", poolName, systemName, oldHealth, newHealth)
|
||||
}
|
||||
|
||||
userIDs := systemRecord.GetStringSlice("users")
|
||||
@@ -116,7 +119,7 @@ func createZfsPoolHistoryRecord(app core.App, userID, systemID, alertID, poolNam
|
||||
record.Set("user", userID)
|
||||
record.Set("system", systemID)
|
||||
record.Set("alert_id", alertID)
|
||||
record.Set("name", "ZFS Pool: "+poolName)
|
||||
record.Set("name", "Storage Pool: "+poolName)
|
||||
return app.Save(record)
|
||||
}
|
||||
|
||||
|
||||
@@ -143,3 +143,37 @@ func TestDiskAlertZfsPoolMultiMinute(t *testing.T) {
|
||||
assert.False(t, diskAlert.GetBool("triggered"),
|
||||
"Alert should be resolved when ZFS pool average (50%%) drops below threshold (80%%)")
|
||||
}
|
||||
|
||||
func TestDiskAlertIgnoresRawPool(t *testing.T) {
|
||||
for _, minutes := range []int{0, 2} {
|
||||
hub, user := beszelTests.GetHubWithUser(t)
|
||||
systems, err := beszelTests.CreateSystems(hub, 1, user.Id, "up")
|
||||
require.NoError(t, err)
|
||||
alert, err := beszelTests.CreateRecord(hub, "alerts", map[string]any{"name": "Disk", "system": systems[0].Id, "user": user.Id, "value": 80, "min": minutes})
|
||||
require.NoError(t, err)
|
||||
pools := map[string]*system.ZfsPool{"btrfs": {Total: 100, Used: 99, Raw: true}}
|
||||
for _, offset := range []time.Duration{-180, -90, -60, -30} {
|
||||
data, err := json.Marshal(system.Stats{ZfsPools: pools})
|
||||
require.NoError(t, err)
|
||||
record, err := beszelTests.CreateRecord(hub, "system_stats", map[string]any{"system": systems[0].Id, "type": "1m", "stats": string(data)})
|
||||
require.NoError(t, err)
|
||||
record.SetRaw("created", time.Now().UTC().Add(offset*time.Second).Format(types.DefaultDateLayout))
|
||||
require.NoError(t, hub.SaveNoValidate(record))
|
||||
}
|
||||
require.NoError(t, hub.GetAlertManager().HandleSystemAlerts(systems[0], &system.CombinedData{Stats: system.Stats{ZfsPools: pools}}))
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
record, err := hub.FindRecordById("alerts", alert.Id)
|
||||
require.NoError(t, err)
|
||||
assert.False(t, record.GetBool("triggered"))
|
||||
if minutes > 0 {
|
||||
// A current usable sample must not make raw historical values eligible.
|
||||
pools["btrfs"].Raw = false
|
||||
require.NoError(t, hub.GetAlertManager().HandleSystemAlerts(systems[0], &system.CombinedData{Stats: system.Stats{ZfsPools: pools}}))
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
record, err = hub.FindRecordById("alerts", alert.Id)
|
||||
require.NoError(t, err)
|
||||
assert.False(t, record.GetBool("triggered"))
|
||||
}
|
||||
hub.Cleanup()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,6 @@ import (
|
||||
|
||||
func TestZfsDiskAlertKeyIsNamespaced(t *testing.T) {
|
||||
assert.Equal(t, "zfs:tank", zfsDiskAlertKey("tank"))
|
||||
assert.Equal(t, "Usage of ZFS pool tank", diskAlertDescriptor(zfsDiskAlertKey("tank")))
|
||||
assert.Equal(t, "Usage of storage pool tank", diskAlertDescriptor(zfsDiskAlertKey("tank")))
|
||||
assert.Equal(t, "Usage of tank", diskAlertDescriptor("tank"))
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ func TestZfsPoolAlertOnlineToDegraded(t *testing.T) {
|
||||
|
||||
assert.EqualValues(t, 1, hub.TestMailer.TotalSend(), "should have 1 email sent after pool became DEGRADED")
|
||||
lastMessage := hub.TestMailer.LastMessage()
|
||||
assert.Contains(t, lastMessage.Subject, "ZFS pool DEGRADED on test-system")
|
||||
assert.Contains(t, lastMessage.Subject, "Storage pool DEGRADED on test-system")
|
||||
assert.Contains(t, lastMessage.Subject, "tank")
|
||||
assert.Contains(t, lastMessage.Text, "ONLINE to DEGRADED")
|
||||
}
|
||||
@@ -76,7 +76,7 @@ func TestZfsPoolAlertDegradedToFaulted(t *testing.T) {
|
||||
|
||||
assert.EqualValues(t, 2, hub.TestMailer.TotalSend(), "should alert on initial DEGRADED state and later FAULTED transition")
|
||||
lastMessage := hub.TestMailer.LastMessage()
|
||||
assert.Contains(t, lastMessage.Subject, "ZFS pool FAULTED on test-system")
|
||||
assert.Contains(t, lastMessage.Subject, "Storage pool FAULTED on test-system")
|
||||
}
|
||||
|
||||
func TestZfsPoolAlertNoAlertOnRecovery(t *testing.T) {
|
||||
@@ -239,7 +239,7 @@ func TestZfsPoolAlertWritesHistory(t *testing.T) {
|
||||
history, err := hub.FindRecordsByFilter("alerts_history", "alert_id={:alert_id}", "", 0, 0, map[string]any{"alert_id": pool.Id})
|
||||
assert.NoError(t, err)
|
||||
require.Len(t, history, 1, "expected one history entry per user")
|
||||
assert.Equal(t, "ZFS Pool: tank", history[0].GetString("name"))
|
||||
assert.Equal(t, "Storage Pool: tank", history[0].GetString("name"))
|
||||
assert.Equal(t, system.Id, history[0].GetString("system"))
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
package alerts
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/nicholas-fedor/shoutrrr/pkg/router"
|
||||
"github.com/nicholas-fedor/shoutrrr/pkg/types"
|
||||
)
|
||||
|
||||
var (
|
||||
errInternalDestination = errors.New("Only admins can send to internal destinations")
|
||||
errUnrestrictedService = errors.New("Only admins can use this notification service") // Restrict services w/o custom connection support
|
||||
publicNotificationDialer = &net.Dialer{
|
||||
Timeout: 10 * time.Second,
|
||||
// Control checks each resolved address immediately before connecting.
|
||||
Control: func(_, address string, _ syscall.RawConn) error { return checkNotificationAddress(address) },
|
||||
}
|
||||
publicNotificationClient = newPublicNotificationClient()
|
||||
)
|
||||
|
||||
func newPublicNotificationClient() *http.Client {
|
||||
return &http.Client{
|
||||
Timeout: 15 * time.Second,
|
||||
Transport: &http.Transport{
|
||||
// Do not use proxies: they can resolve the target themselves and
|
||||
// bypass the destination check on our socket.
|
||||
DialContext: publicNotificationDialer.DialContext,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func checkNotificationAddress(address string) error {
|
||||
addr, err := netip.ParseAddrPort(address)
|
||||
if err != nil || addr.Addr().Zone() != "" {
|
||||
return errInternalDestination
|
||||
}
|
||||
ip := net.IP(addr.Addr().AsSlice())
|
||||
if !ip.IsGlobalUnicast() || isInternalIP(ip) {
|
||||
return errInternalDestination
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func sendPublicNotification(rawURL, message string) error {
|
||||
client := ¬ificationClient{Client: publicNotificationClient}
|
||||
service, err := newPublicNotificationService(rawURL, types.SenderOptions{HTTPClient: client, DialContext: client.dialContext})
|
||||
if err == nil {
|
||||
if closer, ok := service.(io.Closer); ok {
|
||||
defer closer.Close()
|
||||
}
|
||||
err = service.Send(message, &types.Params{})
|
||||
}
|
||||
// Some services format errors without preserving their error chain.
|
||||
if client.blocked.Load() {
|
||||
return errInternalDestination
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
type notificationClient struct {
|
||||
*http.Client
|
||||
blocked atomic.Bool
|
||||
}
|
||||
|
||||
func (c *notificationClient) Do(req *http.Request) (*http.Response, error) {
|
||||
response, err := c.Client.Do(req)
|
||||
if errors.Is(err, errInternalDestination) {
|
||||
c.blocked.Store(true)
|
||||
}
|
||||
return response, err
|
||||
}
|
||||
|
||||
func (c *notificationClient) dialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
conn, err := publicNotificationDialer.DialContext(ctx, network, address)
|
||||
if errors.Is(err, errInternalDestination) {
|
||||
c.blocked.Store(true)
|
||||
}
|
||||
return conn, err
|
||||
}
|
||||
|
||||
func newPublicNotificationService(rawURL string, opts types.SenderOptions) (types.Service, error) {
|
||||
r := &router.ServiceRouter{}
|
||||
scheme, serviceURL, err := r.ExtractServiceName(rawURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
service, err := r.NewService(scheme)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
httpSetter, httpOK := service.(types.HTTPClientSetter)
|
||||
dialSetter, dialOK := service.(types.DialContextSetter)
|
||||
if (!httpOK || opts.HTTPClient == nil) && (!dialOK || opts.DialContext == nil) {
|
||||
return nil, errUnrestrictedService
|
||||
}
|
||||
if serviceURL.Scheme != scheme {
|
||||
custom, ok := service.(types.CustomURLService)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%w: %s", router.ErrCustomURLsNotSupported, scheme)
|
||||
}
|
||||
serviceURL, err = custom.GetServiceURLFromCustom(serviceURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// Shoutrrr v0.20.0 CreateSenderWithOptions injects only AFTER Initialize.
|
||||
// Matrix can log in during Initialize, so inject before it as well.
|
||||
if httpOK {
|
||||
httpSetter.SetHTTPClient(opts.HTTPClient)
|
||||
}
|
||||
if dialOK {
|
||||
dialSetter.SetDialContext(opts.DialContext)
|
||||
}
|
||||
if err := service.Initialize(serviceURL, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Some initializers replace their HTTP client with a default client.
|
||||
if httpOK {
|
||||
httpSetter.SetHTTPClient(opts.HTTPClient)
|
||||
}
|
||||
if dialOK {
|
||||
dialSetter.SetDialContext(opts.DialContext)
|
||||
}
|
||||
return service, nil
|
||||
}
|
||||
|
||||
var cgnatNetwork = &net.IPNet{
|
||||
IP: net.IPv4(100, 64, 0, 0),
|
||||
Mask: net.CIDRMask(10, 32),
|
||||
}
|
||||
|
||||
func isInternalIP(ip net.IP) bool {
|
||||
return ip.IsPrivate() ||
|
||||
ip.IsLoopback() ||
|
||||
ip.IsUnspecified() ||
|
||||
ip.IsLinkLocalUnicast() ||
|
||||
ip.IsMulticast() ||
|
||||
cgnatNetwork.Contains(ip)
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
package alerts
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
|
||||
"github.com/nicholas-fedor/shoutrrr/pkg/types"
|
||||
"golang.org/x/net/dns/dnsmessage"
|
||||
)
|
||||
|
||||
func TestCheckNotificationAddress(t *testing.T) {
|
||||
for _, host := range []string{"127.0.0.1", "10.0.0.1", "172.16.0.1", "192.168.0.1", "169.254.169.254", "100.64.0.0", "100.127.255.255", "0.0.0.0", "224.0.0.1", "255.255.255.255", "::1", "::", "fc00::1", "fe80::1", "ff02::1", "::ffff:127.0.0.1", "::ffff:169.254.169.254", "fe80::1%lo", "localhost", "consul"} {
|
||||
t.Run(host, func(t *testing.T) {
|
||||
if err := checkNotificationAddress(net.JoinHostPort(host, "80")); !errors.Is(err, errInternalDestination) {
|
||||
t.Fatalf("expected blocked address, got %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
for _, host := range []string{"8.8.8.8", "100.63.255.255", "100.128.0.0", "2001:4860:4860::8888"} {
|
||||
if err := checkNotificationAddress(net.JoinHostPort(host, "443")); err != nil {
|
||||
t.Errorf("public address %s: %v", host, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPublicNotificationBlocksInternalRequests(t *testing.T) {
|
||||
var hits atomic.Int32
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
hits.Add(1)
|
||||
}))
|
||||
defer server.Close()
|
||||
host := strings.TrimPrefix(server.URL, "http://")
|
||||
for _, rawURL := range []string{
|
||||
"generic+http://" + host,
|
||||
"generic+https://" + host,
|
||||
"generic+http://localhost:" + strings.Split(host, ":")[1],
|
||||
"matrix://user:password@" + host + "/room?disabletls=yes",
|
||||
"mattermost://" + host + "/token?disabletls=yes",
|
||||
} {
|
||||
t.Run(rawURL, func(t *testing.T) {
|
||||
if err := sendPublicNotification(rawURL, "test"); !errors.Is(err, errInternalDestination) {
|
||||
t.Fatalf("expected internal destination error, got %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
if hits.Load() != 0 {
|
||||
t.Fatal("internal server received a request")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
type notificationRoundTripper func(*http.Request) (*http.Response, error)
|
||||
|
||||
func (f notificationRoundTripper) RoundTrip(r *http.Request) (*http.Response, error) { return f(r) }
|
||||
|
||||
func TestPublicNotificationRedirect(t *testing.T) {
|
||||
client := newPublicNotificationClient()
|
||||
defer client.CloseIdleConnections()
|
||||
transport := client.Transport
|
||||
client.Transport = notificationRoundTripper(func(r *http.Request) (*http.Response, error) {
|
||||
if r.URL.Host == "public.example" {
|
||||
return &http.Response{StatusCode: 307, Header: http.Header{"Location": {"http://127.0.0.1/"}}, Body: io.NopCloser(strings.NewReader("")), Request: r}, nil
|
||||
}
|
||||
return transport.RoundTrip(r)
|
||||
})
|
||||
_, err := client.Get("http://public.example/")
|
||||
if !errors.Is(err, errInternalDestination) {
|
||||
t.Fatalf("expected redirect to be blocked, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPublicNotificationServiceClient(t *testing.T) {
|
||||
for _, rawURL := range []string{"generic+http://public.example/path", "discord://token@123456789", "slack://hook:AAAAAAAAA-BBBBBBBBB-123456789123456789123456@webhook"} {
|
||||
t.Run(rawURL, func(t *testing.T) {
|
||||
var hits int
|
||||
client := &http.Client{Transport: notificationRoundTripper(func(r *http.Request) (*http.Response, error) {
|
||||
hits++
|
||||
body := `{"ok":true}`
|
||||
if strings.HasPrefix(rawURL, "slack:") {
|
||||
body = "ok"
|
||||
}
|
||||
return &http.Response{StatusCode: 200, Header: make(http.Header), Body: io.NopCloser(strings.NewReader(body)), Request: r}, nil
|
||||
})}
|
||||
service, err := newPublicNotificationService(rawURL, types.SenderOptions{HTTPClient: client})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := service.Send("test", &types.Params{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if hits == 0 {
|
||||
t.Fatal("injected client was not used")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPublicNotificationDNS(t *testing.T) {
|
||||
// Supply deterministic DNS responses over an in-memory TCP connection.
|
||||
// The first lookup sees a public IP; subsequent lookups see loopback.
|
||||
var rebound atomic.Bool
|
||||
resolver := &net.Resolver{PreferGo: true, Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
client, server := net.Pipe()
|
||||
go func() {
|
||||
defer server.Close()
|
||||
var size [2]byte
|
||||
if _, err := io.ReadFull(server, size[:]); err != nil {
|
||||
return
|
||||
}
|
||||
buf := make([]byte, binary.BigEndian.Uint16(size[:]))
|
||||
if _, err := io.ReadFull(server, buf); err != nil {
|
||||
return
|
||||
}
|
||||
var msg dnsmessage.Message
|
||||
if err := msg.Unpack(buf); err != nil {
|
||||
return
|
||||
}
|
||||
msg.Header.Response = true
|
||||
msg.Header.RecursionAvailable = true
|
||||
q := msg.Questions[0]
|
||||
if q.Type == dnsmessage.TypeA {
|
||||
ip := [4]byte{8, 8, 8, 8}
|
||||
if rebound.Load() {
|
||||
ip = [4]byte{127, 0, 0, 1}
|
||||
}
|
||||
msg.Answers = []dnsmessage.Resource{{Header: dnsmessage.ResourceHeader{Name: q.Name, Type: q.Type, Class: dnsmessage.ClassINET}, Body: &dnsmessage.AResource{A: ip}}}
|
||||
}
|
||||
buf, err := msg.Pack()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
binary.BigEndian.PutUint16(size[:], uint16(len(buf)))
|
||||
server.Write(append(size[:], buf...))
|
||||
}()
|
||||
return client, nil
|
||||
}}
|
||||
// These tests do not run in parallel; restore the process resolver afterward.
|
||||
previous := net.DefaultResolver
|
||||
net.DefaultResolver = resolver
|
||||
t.Cleanup(func() { net.DefaultResolver = previous })
|
||||
ips, err := resolver.LookupIP(context.Background(), "ip4", "rebind.example")
|
||||
if err != nil || len(ips) != 1 || !ips[0].Equal(net.IPv4(8, 8, 8, 8)) {
|
||||
t.Fatalf("initial DNS lookup: %v, %v", ips, err)
|
||||
}
|
||||
rebound.Store(true)
|
||||
client := newPublicNotificationClient()
|
||||
defer client.CloseIdleConnections()
|
||||
for _, host := range []string{"rebind.example", "consul"} {
|
||||
guarded := ¬ificationClient{Client: client}
|
||||
conn, dialErr := guarded.dialContext(context.Background(), "tcp", net.JoinHostPort(host, "25"))
|
||||
if conn != nil {
|
||||
conn.Close()
|
||||
}
|
||||
if !errors.Is(dialErr, errInternalDestination) || !guarded.blocked.Load() {
|
||||
t.Errorf("expected TCP dial-time rejection for %s, got %v", host, dialErr)
|
||||
}
|
||||
_, err := client.Get("http://" + host + "/")
|
||||
if !errors.Is(err, errInternalDestination) {
|
||||
t.Errorf("expected dial-time rejection for %s, got %v", host, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPublicNotificationTCP(t *testing.T) {
|
||||
for _, rawURL := range []string{
|
||||
"smtp://user:pass@HOST:25/?fromAddress=sender@example.com&toAddresses=recipient@example.com",
|
||||
"smtp://user:pass@HOST:465/?fromAddress=sender@example.com&toAddresses=recipient@example.com",
|
||||
"mqtt://HOST:1883/topic",
|
||||
"mqtts://HOST:8883/topic",
|
||||
} {
|
||||
t.Run(rawURL, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
t.Run("internal destination", func(t *testing.T) {
|
||||
err := sendPublicNotification(strings.ReplaceAll(rawURL, "HOST", "127.0.0.1"), "test")
|
||||
if !errors.Is(err, errInternalDestination) {
|
||||
t.Fatalf("expected blocked destination, got %v", err)
|
||||
}
|
||||
})
|
||||
t.Run("public destination uses injected dialer", func(t *testing.T) {
|
||||
var calls atomic.Int32
|
||||
stopped := errors.New("test dial stopped")
|
||||
service, err := newPublicNotificationService(strings.ReplaceAll(rawURL, "HOST", "8.8.8.8"), types.SenderOptions{
|
||||
DialContext: func(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
calls.Add(1)
|
||||
if network != "tcp" || !strings.HasPrefix(address, "8.8.8.8:") {
|
||||
t.Errorf("unexpected dial: %s %s", network, address)
|
||||
}
|
||||
if err := checkNotificationAddress(address); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
return nil, stopped
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if closer, ok := service.(io.Closer); ok {
|
||||
defer closer.Close()
|
||||
}
|
||||
if err := service.Send("test", &types.Params{}); err == nil {
|
||||
t.Fatal("expected dial failure")
|
||||
}
|
||||
if calls.Load() == 0 {
|
||||
t.Fatal("custom dialer was not used")
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,11 @@ COPY --from=builder /app/agent/test-data/amdgpu.ids /usr/share/libdrm/amdgpu.ids
|
||||
# Copy smartmontools binaries and config files
|
||||
COPY --from=smartmontools-builder /usr/sbin/smartctl /usr/sbin/smartctl
|
||||
|
||||
# Install ZFS userspace utilities (zpool, zfs) for pool/dataset monitoring
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
zfsutils-linux \
|
||||
&& apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Ensure data persistence across container recreations
|
||||
VOLUME ["/var/lib/beszel-agent"]
|
||||
|
||||
|
||||
@@ -65,6 +65,32 @@ RUN set -eux; \
|
||||
cp -v "$interp" "/out/rootfs$interp"; \
|
||||
fi
|
||||
|
||||
# --------------------------
|
||||
# ZFS utilities builder stage
|
||||
# --------------------------
|
||||
FROM --platform=$TARGETPLATFORM debian:bookworm-slim AS zfsutils-builder
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
zfsutils-linux \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Copy the zpool/zfs binaries and their required runtime libraries
|
||||
RUN set -eux; \
|
||||
mkdir -p /out/rootfs/lib /out/rootfs/lib64 /out/rootfs/usr/lib; \
|
||||
for bin in /usr/sbin/zpool /usr/sbin/zfs; do \
|
||||
mkdir -p "/out/rootfs$(dirname "$bin")"; \
|
||||
cp -v "$bin" "/out/rootfs$bin"; \
|
||||
ldd "$bin" \
|
||||
| awk '{print $3}' \
|
||||
| grep '^/' \
|
||||
| xargs -r -I '{}' sh -c 'mkdir -p "/out/rootfs$(dirname "{}")"; cp -v "{}" "/out/rootfs{}"'; \
|
||||
interp="$(ldd "$bin" | awk "/ld-linux/ {print \$1}")"; \
|
||||
if [ -n "$interp" ] && [ -e "$interp" ]; then \
|
||||
mkdir -p "/out/rootfs$(dirname "$interp")"; \
|
||||
cp -v "$interp" "/out/rootfs$interp"; \
|
||||
fi; \
|
||||
done
|
||||
|
||||
# --------------------------
|
||||
# Final image: lightweight multi-arch NVIDIA agent (slim)
|
||||
# --------------------------
|
||||
@@ -78,6 +104,9 @@ COPY --from=builder /app/agent/test-data/amdgpu.ids /usr/share/libdrm/amdgpu.ids
|
||||
COPY --from=smartmontools-builder /usr/sbin/smartctl /usr/sbin/smartctl
|
||||
COPY --from=smartmontools-builder /out/rootfs/ /
|
||||
|
||||
# Copy ZFS utilities (zpool, zfs) binaries and required runtime libraries
|
||||
COPY --from=zfsutils-builder /out/rootfs/ /
|
||||
|
||||
# nvidia-smi is intentionally not bundled.
|
||||
# Mount the host binary instead, for example:
|
||||
# - /usr/bin/nvidia-smi:/usr/bin/nvidia-smi:ro
|
||||
|
||||
@@ -186,11 +186,12 @@ type Stats struct {
|
||||
NetworkRecv float64 `json:"nr,omitzero" cbor:"4,keyasint,omitzero"` // deprecated 0.18.3 (MB) - keep field for old agents/records
|
||||
Bandwidth [2]uint64 `json:"b,omitzero" cbor:"9,keyasint,omitzero"` // [sent bytes, recv bytes]
|
||||
|
||||
Health DockerHealth `json:"-" cbor:"5,keyasint"`
|
||||
Status string `json:"-" cbor:"6,keyasint"`
|
||||
Id string `json:"-" cbor:"7,keyasint"`
|
||||
Image string `json:"-" cbor:"8,keyasint"`
|
||||
Ports string `json:"-" cbor:"10,keyasint"`
|
||||
Health DockerHealth `json:"-" cbor:"5,keyasint"`
|
||||
Status string `json:"-" cbor:"6,keyasint"`
|
||||
Id string `json:"-" cbor:"7,keyasint"`
|
||||
Image string `json:"-" cbor:"8,keyasint"`
|
||||
Ports string `json:"-" cbor:"10,keyasint"`
|
||||
UpdateAvailable bool `json:"u,omitzero" cbor:"11,keyasint,omitzero"`
|
||||
// PrevCpu [2]uint64 `json:"-"`
|
||||
CpuSystem uint64 `json:"-"`
|
||||
CpuContainer uint64 `json:"-"`
|
||||
|
||||
@@ -59,11 +59,15 @@ type Stats struct {
|
||||
|
||||
// ZfsPool holds per-pool ZFS metrics for a single collection interval.
|
||||
type ZfsPool struct {
|
||||
Total float64 `json:"d" cbor:"0,keyasint"` // total capacity in GiB
|
||||
Used float64 `json:"du" cbor:"1,keyasint"` // allocated in GiB
|
||||
ReadBytes uint64 `json:"rb,omitzero" cbor:"2,keyasint,omitzero"` // read throughput in bytes/s
|
||||
WriteBytes uint64 `json:"wb,omitzero" cbor:"3,keyasint,omitzero"` // write throughput in bytes/s
|
||||
Health string `json:"h,omitempty" cbor:"4,keyasint,omitempty"` // ONLINE, DEGRADED, FAULTED, ...
|
||||
DisplayName string `json:"n,omitempty" cbor:"8,keyasint,omitempty"`
|
||||
HideUsage bool `json:"hu,omitempty" cbor:"6,keyasint,omitempty"` // equivalent filesystem usage chart exists
|
||||
HideIO bool `json:"hi,omitempty" cbor:"7,keyasint,omitempty"` // equivalent filesystem I/O chart exists
|
||||
Raw bool `json:"raw,omitempty" cbor:"5,keyasint,omitempty"`
|
||||
Total float64 `json:"d" cbor:"0,keyasint"` // total capacity in GiB
|
||||
Used float64 `json:"du" cbor:"1,keyasint"` // allocated in GiB
|
||||
ReadBytes uint64 `json:"rb,omitzero" cbor:"2,keyasint,omitzero"` // read throughput in bytes/s
|
||||
WriteBytes uint64 `json:"wb,omitzero" cbor:"3,keyasint,omitzero"` // write throughput in bytes/s
|
||||
Health string `json:"h,omitempty" cbor:"4,keyasint,omitempty"` // ONLINE, DEGRADED, FAULTED, ...
|
||||
}
|
||||
|
||||
// Uint8Slice wraps []uint8 to customize JSON encoding while keeping CBOR efficient.
|
||||
|
||||
@@ -1,23 +1,47 @@
|
||||
// Package zfs defines the ZFS detail data exchanged between agent and hub.
|
||||
package zfs
|
||||
|
||||
import "strings"
|
||||
|
||||
// ZfsData is the detail payload returned by the agent for the GetZfsData action.
|
||||
type ZfsData struct {
|
||||
Pools []*PoolDetail `json:"pools,omitempty"`
|
||||
Complete bool `json:"complete,omitempty"`
|
||||
// Backends whose inventories are complete, even when another backend failed.
|
||||
CompleteBackends []string `json:"completeBackends,omitempty"`
|
||||
}
|
||||
|
||||
// CanRefreshPool also governs deletion: missing pools may only be removed
|
||||
// after a successful inventory of their backend. Complete supports old agents.
|
||||
func (data *ZfsData) CanRefreshPool(name string) bool {
|
||||
if data.Complete {
|
||||
return true
|
||||
}
|
||||
backend := "zfs"
|
||||
if strings.HasPrefix(name, "b:") {
|
||||
backend = "btrfs"
|
||||
}
|
||||
for _, complete := range data.CompleteBackends {
|
||||
if complete == backend {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// PoolDetail holds the verbose state of a single pool: capacity, health,
|
||||
// scrub, vdev, and dataset information.
|
||||
type PoolDetail struct {
|
||||
Name string `json:"name"`
|
||||
Health string `json:"health,omitempty"`
|
||||
Size uint64 `json:"size,omitempty"` // bytes
|
||||
Alloc uint64 `json:"alloc,omitempty"` // bytes
|
||||
Free uint64 `json:"free,omitempty"` // bytes
|
||||
Scrub *Scrub `json:"scrub,omitempty"`
|
||||
Vdevs []*Vdev `json:"vdevs,omitempty"`
|
||||
Datasets []*Dataset `json:"datasets,omitempty"`
|
||||
DisplayName string `json:"displayName,omitempty"`
|
||||
Raw bool `json:"raw,omitempty"`
|
||||
Name string `json:"name"`
|
||||
Health string `json:"health,omitempty"`
|
||||
Size uint64 `json:"size,omitempty"` // bytes
|
||||
Alloc uint64 `json:"alloc,omitempty"` // bytes
|
||||
Free uint64 `json:"free,omitempty"` // bytes
|
||||
Scrub *Scrub `json:"scrub,omitempty"`
|
||||
Vdevs []*Vdev `json:"vdevs,omitempty"`
|
||||
Datasets []*Dataset `json:"datasets,omitempty"`
|
||||
}
|
||||
|
||||
// Scrub holds the scrub (or resilver) status of a pool.
|
||||
|
||||
+1
-1
@@ -6,9 +6,9 @@ import (
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
"uuid"
|
||||
|
||||
"github.com/blang/semver"
|
||||
"github.com/google/uuid"
|
||||
"github.com/henrygd/beszel"
|
||||
"github.com/henrygd/beszel/internal/alerts"
|
||||
"github.com/henrygd/beszel/internal/ghupdate"
|
||||
|
||||
@@ -99,8 +99,8 @@ func setCollectionAuthSettings(app core.App) error {
|
||||
}
|
||||
|
||||
if err := applyCollectionRules(app, []string{"fingerprints"}, collectionRules{
|
||||
list: &systemScopedReadRule,
|
||||
view: &systemScopedReadRule,
|
||||
list: &systemScopedWriteRule,
|
||||
view: &systemScopedWriteRule,
|
||||
create: &systemScopedWriteRule,
|
||||
update: &systemScopedWriteRule,
|
||||
delete: &systemScopedWriteRule,
|
||||
|
||||
@@ -88,8 +88,8 @@ func TestCollectionRulesDefault(t *testing.T) {
|
||||
// fingerprints collection
|
||||
fingerprintsCollection, err := hub.FindCollectionByNameOrId("fingerprints")
|
||||
require.NoError(t, err, "Failed to find fingerprints collection")
|
||||
assert.Equal(t, isUserInSystemUsers, *fingerprintsCollection.ListRule)
|
||||
assert.Equal(t, isUserInSystemUsers, *fingerprintsCollection.ViewRule)
|
||||
assert.Equal(t, isUserInSystemUsersNotReadonly, *fingerprintsCollection.ListRule)
|
||||
assert.Equal(t, isUserInSystemUsersNotReadonly, *fingerprintsCollection.ViewRule)
|
||||
assert.Equal(t, isUserInSystemUsersNotReadonly, *fingerprintsCollection.CreateRule)
|
||||
assert.Equal(t, isUserInSystemUsersNotReadonly, *fingerprintsCollection.UpdateRule)
|
||||
assert.Equal(t, isUserInSystemUsersNotReadonly, *fingerprintsCollection.DeleteRule)
|
||||
@@ -216,8 +216,8 @@ func TestCollectionRulesShareAllSystems(t *testing.T) {
|
||||
// fingerprints collection
|
||||
fingerprintsCollection, err := hub.FindCollectionByNameOrId("fingerprints")
|
||||
require.NoError(t, err, "Failed to find fingerprints collection")
|
||||
assert.Equal(t, isUser, *fingerprintsCollection.ListRule)
|
||||
assert.Equal(t, isUser, *fingerprintsCollection.ViewRule)
|
||||
assert.Equal(t, isUserNotReadonly, *fingerprintsCollection.ListRule)
|
||||
assert.Equal(t, isUserNotReadonly, *fingerprintsCollection.ViewRule)
|
||||
assert.Equal(t, isUserNotReadonly, *fingerprintsCollection.CreateRule)
|
||||
assert.Equal(t, isUserNotReadonly, *fingerprintsCollection.UpdateRule)
|
||||
assert.Equal(t, isUserNotReadonly, *fingerprintsCollection.DeleteRule)
|
||||
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"uuid"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/henrygd/beszel/internal/entities/system"
|
||||
|
||||
"github.com/pocketbase/dbx"
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
//go:build testing
|
||||
|
||||
package systems
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/henrygd/beszel/internal/entities/container"
|
||||
"github.com/pocketbase/dbx"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCreateContainerRecordsPersistsImageUpdateAvailability(t *testing.T) {
|
||||
_, app := newTestSystemWithHub(t)
|
||||
|
||||
const (
|
||||
systemID = "system123"
|
||||
containerID = "abcdef123456"
|
||||
image = "nginx:latest"
|
||||
)
|
||||
|
||||
data := &container.Stats{
|
||||
Id: containerID,
|
||||
Name: "web",
|
||||
Image: image,
|
||||
UpdateAvailable: true,
|
||||
}
|
||||
require.NoError(t, createContainerRecords(app, []*container.Stats{data}, systemID))
|
||||
|
||||
var record struct {
|
||||
Image string `db:"image"`
|
||||
UpdateAvailable bool `db:"updatable"`
|
||||
}
|
||||
require.NoError(t, app.DB().Select("image", "updatable").From("containers").
|
||||
Where(dbx.HashExp{"id": containerID}).One(&record))
|
||||
assert.Equal(t, image, record.Image)
|
||||
assert.True(t, record.UpdateAvailable)
|
||||
|
||||
data.UpdateAvailable = false
|
||||
require.NoError(t, createContainerRecords(app, []*container.Stats{data}, systemID))
|
||||
require.NoError(t, app.DB().Select("image", "updatable").From("containers").
|
||||
Where(dbx.HashExp{"id": containerID}).One(&record))
|
||||
assert.Equal(t, image, record.Image)
|
||||
assert.False(t, record.UpdateAvailable)
|
||||
}
|
||||
@@ -272,7 +272,15 @@ func (sys *System) createRecords(data *system.CombinedData) (*core.Record, error
|
||||
|
||||
// update system record (do this last because it triggers alerts and we need above records to be inserted first)
|
||||
systemRecord.Set("status", up)
|
||||
systemRecord.Set("info", data.Info)
|
||||
// Distinguish an idle GPU from a system without GPU data (#2312)
|
||||
info := struct {
|
||||
system.Info
|
||||
GpuPct *float64 `json:"g,omitempty"`
|
||||
}{Info: data.Info}
|
||||
if len(data.Stats.GPUData) > 0 {
|
||||
info.GpuPct = &data.Info.GpuPct
|
||||
}
|
||||
systemRecord.Set("info", info)
|
||||
if err := txApp.SaveNoValidate(systemRecord); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -322,6 +330,11 @@ func createSystemdStatsRecords(app core.App, data []*systemd.Service, systemId s
|
||||
|
||||
valueStrings := make([]string, 0, len(data))
|
||||
for i, service := range data {
|
||||
// Agent payloads can contain null entries. Reject the snapshot before
|
||||
// executing any queries so existing service records remain intact.
|
||||
if service == nil {
|
||||
return fmt.Errorf("null systemd service at index %d", i)
|
||||
}
|
||||
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)
|
||||
@@ -363,7 +376,7 @@ func createContainerRecords(app core.App, data []*container.Stats, systemId stri
|
||||
valueStrings := make([]string, 0, len(data))
|
||||
for i, container := range data {
|
||||
suffix := fmt.Sprintf("%d", i)
|
||||
valueStrings = append(valueStrings, fmt.Sprintf("({:id%[1]s}, {:system}, {:name%[1]s}, {:image%[1]s}, {:ports%[1]s}, {:status%[1]s}, {:health%[1]s}, {:cpu%[1]s}, {:memory%[1]s}, {:net%[1]s}, {:updated})", suffix))
|
||||
valueStrings = append(valueStrings, fmt.Sprintf("({:id%[1]s}, {:system}, {:name%[1]s}, {:image%[1]s}, {:ports%[1]s}, {:status%[1]s}, {:health%[1]s}, {:cpu%[1]s}, {:memory%[1]s}, {:net%[1]s}, {:updateAvailable%[1]s}, {:updated})", suffix))
|
||||
params["id"+suffix] = container.Id
|
||||
params["name"+suffix] = container.Name
|
||||
params["image"+suffix] = container.Image
|
||||
@@ -377,9 +390,10 @@ func createContainerRecords(app core.App, data []*container.Stats, systemId stri
|
||||
netBytes = uint64((container.NetworkSent + container.NetworkRecv) * 1024 * 1024)
|
||||
}
|
||||
params["net"+suffix] = netBytes
|
||||
params["updateAvailable"+suffix] = container.UpdateAvailable
|
||||
}
|
||||
queryString := fmt.Sprintf(
|
||||
"INSERT INTO containers (id, system, name, image, ports, status, health, cpu, memory, net, updated) VALUES %s ON CONFLICT(id) DO UPDATE SET system = excluded.system, name = excluded.name, image = excluded.image, ports = excluded.ports, status = excluded.status, health = excluded.health, cpu = excluded.cpu, memory = excluded.memory, net = excluded.net, updated = excluded.updated",
|
||||
"INSERT INTO containers (id, system, name, image, ports, status, health, cpu, memory, net, updatable, updated) VALUES %s ON CONFLICT(id) DO UPDATE SET system = excluded.system, name = excluded.name, image = excluded.image, ports = excluded.ports, status = excluded.status, health = excluded.health, cpu = excluded.cpu, memory = excluded.memory, net = excluded.net, updatable = excluded.updatable, updated = excluded.updated",
|
||||
strings.Join(valueStrings, ","),
|
||||
)
|
||||
_, err := app.DB().NewQuery(queryString).Bind(params).Execute()
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
//go:build testing
|
||||
|
||||
package systems
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/henrygd/beszel/internal/entities/system"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCreateRecordsGPUUtilization(t *testing.T) {
|
||||
sys, app := newTestSystemWithHub(t)
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
gpu bool
|
||||
usage float64
|
||||
}{
|
||||
{"no GPU", false, 0},
|
||||
{"active GPU", true, 42.5},
|
||||
{"idle GPU", true, 0},
|
||||
{"GPU removed", false, 0},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
data := &system.CombinedData{Info: system.Info{GpuPct: tc.usage, Cpu: 12.5}}
|
||||
if tc.gpu {
|
||||
data.Stats.GPUData = map[string]system.GPUData{"0": {Name: "GPU", Usage: tc.usage}}
|
||||
}
|
||||
_, err := sys.createRecords(data)
|
||||
require.NoError(t, err)
|
||||
record, err := app.FindRecordById("systems", sys.Id)
|
||||
require.NoError(t, err)
|
||||
var info map[string]any
|
||||
require.NoError(t, record.UnmarshalJSONField("info", &info))
|
||||
assert.Equal(t, 12.5, info["cpu"])
|
||||
if tc.gpu {
|
||||
assert.Equal(t, tc.usage, info["g"])
|
||||
} else {
|
||||
assert.NotContains(t, info, "g")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/henrygd/beszel/internal/hub/ws"
|
||||
@@ -42,13 +43,17 @@ var errSystemExists = errors.New("system exists")
|
||||
// SystemManager manages a collection of monitored systems and their connections.
|
||||
// It handles system lifecycle, status updates, and maintains both SSH and WebSocket connections.
|
||||
type SystemManager struct {
|
||||
hub hubLike // Hub interface for database and alert operations
|
||||
systems *store.Store[string, *System] // Thread-safe store of active systems
|
||||
sshConfig *ssh.ClientConfig // SSH client configuration for system connections
|
||||
smartFetchMap *expirymap.ExpiryMap[smartFetchState] // Stores last SMART fetch time/result; TTL is only for cleanup
|
||||
zfsFetchMap *expirymap.ExpiryMap[zfsFetchState] // Stores last ZFS fetch time/result; TTL is only for cleanup
|
||||
ctx context.Context // Cancelled when the app terminates
|
||||
cancel context.CancelFunc // Cancels ctx and all child system contexts
|
||||
hub hubLike // Hub interface for database and alert operations
|
||||
systems *store.Store[string, *System] // Thread-safe store of active systems
|
||||
sshConfig *ssh.ClientConfig // SSH client configuration for system connections
|
||||
smartFetchMap *expirymap.ExpiryMap[smartFetchState] // Stores last SMART fetch time/result; TTL is only for cleanup
|
||||
zfsFetchMap *expirymap.ExpiryMap[zfsFetchState] // Stores last ZFS fetch time/result; TTL is only for cleanup
|
||||
realtimeMutex sync.Mutex // Protects all realtime worker and subscription state
|
||||
activeSubscriptions map[string]*subscriptionInfo // Realtime subscriptions keyed by system ID
|
||||
realtimeWorkerStop chan struct{} // Stops the current realtime worker generation
|
||||
realtimeWorkerRun bool // Whether a realtime worker has been started
|
||||
ctx context.Context // Cancelled when the app terminates
|
||||
cancel context.CancelFunc // Cancels ctx and all child system contexts
|
||||
}
|
||||
|
||||
// hubLike defines the interface requirements for the hub dependency.
|
||||
@@ -67,10 +72,11 @@ type hubLike interface {
|
||||
// The hub must implement the hubLike interface to provide database and alert functionality.
|
||||
func NewSystemManager(hub hubLike) *SystemManager {
|
||||
sm := &SystemManager{
|
||||
systems: store.New(map[string]*System{}),
|
||||
hub: hub,
|
||||
smartFetchMap: expirymap.New[smartFetchState](time.Hour),
|
||||
zfsFetchMap: expirymap.New[zfsFetchState](time.Hour),
|
||||
systems: store.New(map[string]*System{}),
|
||||
hub: hub,
|
||||
smartFetchMap: expirymap.New[smartFetchState](time.Hour),
|
||||
zfsFetchMap: expirymap.New[zfsFetchState](time.Hour),
|
||||
activeSubscriptions: make(map[string]*subscriptionInfo),
|
||||
}
|
||||
sm.ctx, sm.cancel = context.WithCancel(context.Background())
|
||||
return sm
|
||||
@@ -138,6 +144,7 @@ func (sm *SystemManager) bindEventHooks() {
|
||||
// onTerminate cancels SystemManager context on app shutdown
|
||||
func (sm *SystemManager) onTerminate(e *core.TerminateEvent) error {
|
||||
sm.cancel()
|
||||
sm.stopRealtimeWorker()
|
||||
return e.Next()
|
||||
}
|
||||
|
||||
|
||||
@@ -3,25 +3,27 @@ package systems
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/henrygd/beszel/internal/common"
|
||||
"github.com/henrygd/beszel/internal/hub/utils"
|
||||
"github.com/pocketbase/dbx"
|
||||
"github.com/pocketbase/pocketbase/apis"
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
"github.com/pocketbase/pocketbase/tools/subscriptions"
|
||||
)
|
||||
|
||||
type subscriptionInfo struct {
|
||||
subscription string
|
||||
connectedClients uint8
|
||||
connectedClients int
|
||||
fetching bool
|
||||
}
|
||||
|
||||
var (
|
||||
activeSubscriptions = make(map[string]*subscriptionInfo)
|
||||
workerRunning bool
|
||||
tickerStopChan chan struct{}
|
||||
realtimeMutex sync.Mutex
|
||||
)
|
||||
type realtimeFetch struct {
|
||||
systemID string
|
||||
subscription string
|
||||
info *subscriptionInfo
|
||||
}
|
||||
|
||||
// onRealtimeConnectRequest handles client connection events for realtime subscriptions.
|
||||
// It cleans up existing subscriptions when a client connects.
|
||||
@@ -38,6 +40,19 @@ func (sm *SystemManager) onRealtimeConnectRequest(e *core.RealtimeConnectRequest
|
||||
// onRealtimeSubscribeRequest handles client subscription events for realtime metrics.
|
||||
// It tracks new subscriptions and unsubscriptions to manage the realtime worker lifecycle.
|
||||
func (sm *SystemManager) onRealtimeSubscribeRequest(e *core.RealtimeSubscribeRequestEvent) error {
|
||||
// Parse with PocketBase's own subscription parser before changing the real
|
||||
// client. Reject the entire request if any metrics target is inaccessible.
|
||||
requested := subscriptions.NewDefaultClient()
|
||||
requested.Subscribe(e.Subscriptions...)
|
||||
for topic, options := range requested.Subscriptions() {
|
||||
if !strings.HasPrefix(topic, "rt_metrics") {
|
||||
continue
|
||||
}
|
||||
system, err := sm.GetSystem(options.Query["system"])
|
||||
if err != nil || !system.HasUser(e.App, e.Auth) {
|
||||
return e.NotFoundError("", nil)
|
||||
}
|
||||
}
|
||||
oldSubs := e.Client.Subscriptions()
|
||||
// after e.Next() is the result of the subscribe request
|
||||
err := e.Next()
|
||||
@@ -47,14 +62,7 @@ func (sm *SystemManager) onRealtimeSubscribeRequest(e *core.RealtimeSubscribeReq
|
||||
for k, options := range newSubs {
|
||||
if _, ok := oldSubs[k]; !ok {
|
||||
if strings.HasPrefix(k, "rt_metrics") {
|
||||
systemId := options.Query["system"]
|
||||
if _, ok := activeSubscriptions[systemId]; !ok {
|
||||
activeSubscriptions[systemId] = &subscriptionInfo{
|
||||
subscription: k,
|
||||
}
|
||||
}
|
||||
activeSubscriptions[systemId].connectedClients += 1
|
||||
sm.onRealtimeSubscriptionAdded()
|
||||
sm.addRealtimeSubscription(options.Query["system"], k)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -68,72 +76,76 @@ func (sm *SystemManager) onRealtimeSubscribeRequest(e *core.RealtimeSubscribeReq
|
||||
return err
|
||||
}
|
||||
|
||||
// onRealtimeSubscriptionAdded initializes or starts the realtime worker when the first subscription is added.
|
||||
// It ensures only one worker runs at a time.
|
||||
func (sm *SystemManager) onRealtimeSubscriptionAdded() {
|
||||
realtimeMutex.Lock()
|
||||
defer realtimeMutex.Unlock()
|
||||
// addRealtimeSubscription tracks a subscriber and starts a worker if necessary.
|
||||
func (sm *SystemManager) addRealtimeSubscription(systemID, subscription string) {
|
||||
sm.realtimeMutex.Lock()
|
||||
defer sm.realtimeMutex.Unlock()
|
||||
|
||||
// Start the worker if it's not already running
|
||||
if !workerRunning {
|
||||
workerRunning = true
|
||||
// Create a new stop channel for this worker instance
|
||||
tickerStopChan = make(chan struct{})
|
||||
go sm.startRealtimeWorker()
|
||||
if sm.activeSubscriptions == nil {
|
||||
sm.activeSubscriptions = make(map[string]*subscriptionInfo)
|
||||
}
|
||||
info, ok := sm.activeSubscriptions[systemID]
|
||||
if !ok {
|
||||
info = &subscriptionInfo{subscription: subscription}
|
||||
sm.activeSubscriptions[systemID] = info
|
||||
}
|
||||
info.connectedClients++
|
||||
|
||||
if !sm.realtimeWorkerRun {
|
||||
sm.realtimeWorkerRun = true
|
||||
stop := make(chan struct{})
|
||||
sm.realtimeWorkerStop = stop
|
||||
go sm.startRealtimeWorker(stop)
|
||||
}
|
||||
}
|
||||
|
||||
// checkSubscriptions stops the realtime worker when there are no active subscriptions.
|
||||
// This prevents unnecessary resource usage when no clients are listening for realtime data.
|
||||
func (sm *SystemManager) checkSubscriptions() {
|
||||
if !workerRunning || len(activeSubscriptions) > 0 {
|
||||
// stopRealtimeWorker stops the current worker generation, if any.
|
||||
func (sm *SystemManager) stopRealtimeWorker() {
|
||||
sm.realtimeMutex.Lock()
|
||||
defer sm.realtimeMutex.Unlock()
|
||||
sm.stopRealtimeWorkerLocked()
|
||||
}
|
||||
|
||||
func (sm *SystemManager) stopRealtimeWorkerLocked() {
|
||||
if !sm.realtimeWorkerRun {
|
||||
return
|
||||
}
|
||||
|
||||
realtimeMutex.Lock()
|
||||
defer realtimeMutex.Unlock()
|
||||
|
||||
// Signal the worker to stop
|
||||
if tickerStopChan != nil {
|
||||
select {
|
||||
case tickerStopChan <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
// Mark worker as stopped (will be reset when next subscription comes in)
|
||||
workerRunning = false
|
||||
close(sm.realtimeWorkerStop)
|
||||
sm.realtimeWorkerStop = nil
|
||||
sm.realtimeWorkerRun = false
|
||||
}
|
||||
|
||||
// removeRealtimeSubscription removes a realtime subscription and checks if the worker should be stopped.
|
||||
// It only processes subscriptions with the "rt_metrics" prefix and triggers cleanup when subscriptions are removed.
|
||||
func (sm *SystemManager) removeRealtimeSubscription(subscription string, options subscriptions.SubscriptionOptions) {
|
||||
if strings.HasPrefix(subscription, "rt_metrics") {
|
||||
systemId := options.Query["system"]
|
||||
if info, ok := activeSubscriptions[systemId]; ok {
|
||||
info.connectedClients -= 1
|
||||
systemID := options.Query["system"]
|
||||
sm.realtimeMutex.Lock()
|
||||
if info, ok := sm.activeSubscriptions[systemID]; ok {
|
||||
info.connectedClients--
|
||||
if info.connectedClients <= 0 {
|
||||
delete(activeSubscriptions, systemId)
|
||||
delete(sm.activeSubscriptions, systemID)
|
||||
}
|
||||
}
|
||||
sm.checkSubscriptions()
|
||||
if len(sm.activeSubscriptions) == 0 {
|
||||
sm.stopRealtimeWorkerLocked()
|
||||
}
|
||||
sm.realtimeMutex.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// startRealtimeWorker runs the main loop for fetching realtime data from agents.
|
||||
// It continuously fetches system data and broadcasts it to subscribed clients via WebSocket.
|
||||
func (sm *SystemManager) startRealtimeWorker() {
|
||||
func (sm *SystemManager) startRealtimeWorker(stop <-chan struct{}) {
|
||||
sm.fetchRealtimeDataAndNotify()
|
||||
tick := time.Tick(1 * time.Second)
|
||||
ticker := time.NewTicker(time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-tickerStopChan:
|
||||
case <-stop:
|
||||
return
|
||||
case <-tick:
|
||||
if len(activeSubscriptions) == 0 {
|
||||
return
|
||||
}
|
||||
case <-ticker.C:
|
||||
sm.fetchRealtimeDataAndNotify()
|
||||
}
|
||||
}
|
||||
@@ -141,27 +153,79 @@ func (sm *SystemManager) startRealtimeWorker() {
|
||||
|
||||
// fetchRealtimeDataAndNotify fetches realtime data for all active subscriptions and notifies the clients.
|
||||
func (sm *SystemManager) fetchRealtimeDataAndNotify() {
|
||||
for systemId, info := range activeSubscriptions {
|
||||
system, err := sm.GetSystem(systemId)
|
||||
for _, fetch := range sm.claimRealtimeFetches() {
|
||||
system, err := sm.GetSystem(fetch.systemID)
|
||||
if err != nil {
|
||||
sm.finishRealtimeFetch(fetch)
|
||||
continue
|
||||
}
|
||||
go func() {
|
||||
go func(fetch realtimeFetch) {
|
||||
defer sm.finishRealtimeFetch(fetch)
|
||||
data, err := system.fetchDataFromAgent(common.DataRequestOptions{CacheTimeMs: 1000})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
bytes, err := json.Marshal(data)
|
||||
if err == nil {
|
||||
notify(sm.hub, info.subscription, bytes)
|
||||
notify(sm.hub, system, fetch.subscription, bytes)
|
||||
}
|
||||
}()
|
||||
}(fetch)
|
||||
}
|
||||
}
|
||||
|
||||
// claimRealtimeFetches takes a stable snapshot and marks each selected system as
|
||||
// in flight. Slow agents are skipped on later ticks until their fetch completes.
|
||||
func (sm *SystemManager) claimRealtimeFetches() []realtimeFetch {
|
||||
sm.realtimeMutex.Lock()
|
||||
defer sm.realtimeMutex.Unlock()
|
||||
|
||||
fetches := make([]realtimeFetch, 0, len(sm.activeSubscriptions))
|
||||
for systemID, info := range sm.activeSubscriptions {
|
||||
if info.fetching {
|
||||
continue
|
||||
}
|
||||
info.fetching = true
|
||||
fetches = append(fetches, realtimeFetch{
|
||||
systemID: systemID,
|
||||
subscription: info.subscription,
|
||||
info: info,
|
||||
})
|
||||
}
|
||||
return fetches
|
||||
}
|
||||
|
||||
func (sm *SystemManager) finishRealtimeFetch(fetch realtimeFetch) {
|
||||
sm.realtimeMutex.Lock()
|
||||
defer sm.realtimeMutex.Unlock()
|
||||
// A subscription may have been removed and recreated while the old request
|
||||
// was running. Only release the exact entry claimed by this request.
|
||||
if info := sm.activeSubscriptions[fetch.systemID]; info == fetch.info {
|
||||
info.fetching = false
|
||||
}
|
||||
}
|
||||
|
||||
// notify broadcasts realtime data to all clients subscribed to a specific subscription.
|
||||
// It iterates through all connected clients and sends the data only to those with matching subscriptions.
|
||||
func notify(app core.App, subscription string, data []byte) error {
|
||||
// Custom topics bypass collection rules, so check current access for every
|
||||
// recipient, including clients whose authentication or membership was revoked.
|
||||
func notify(app core.App, system *System, subscription string, data []byte) error {
|
||||
shareAll, _ := utils.GetEnv("SHARE_ALL_SYSTEMS")
|
||||
members := make(map[string]struct{})
|
||||
if shareAll != "true" {
|
||||
// Refresh once per broadcast so membership changes take effect on the
|
||||
// next update without querying the database for every recipient.
|
||||
var recordData struct{ Users string }
|
||||
if err := app.DB().NewQuery("SELECT users FROM systems WHERE id={:id}").
|
||||
Bind(dbx.Params{"id": system.Id}).One(&recordData); err != nil {
|
||||
return err
|
||||
}
|
||||
var userIDs []string
|
||||
if err := json.Unmarshal([]byte(recordData.Users), &userIDs); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, id := range userIDs {
|
||||
members[id] = struct{}{}
|
||||
}
|
||||
}
|
||||
message := subscriptions.Message{
|
||||
Name: subscription,
|
||||
Data: data,
|
||||
@@ -170,6 +234,13 @@ func notify(app core.App, subscription string, data []byte) error {
|
||||
if !client.HasSubscription(subscription) {
|
||||
continue
|
||||
}
|
||||
auth, _ := client.Get(apis.RealtimeClientAuthKey).(*core.Record)
|
||||
if auth == nil {
|
||||
continue
|
||||
}
|
||||
if _, member := members[auth.Id]; shareAll != "true" && !member {
|
||||
continue
|
||||
}
|
||||
client.Send(message)
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -0,0 +1,229 @@
|
||||
package systems
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/pocketbase/pocketbase/apis"
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
pbtests "github.com/pocketbase/pocketbase/tests"
|
||||
"github.com/pocketbase/pocketbase/tools/hook"
|
||||
"github.com/pocketbase/pocketbase/tools/store"
|
||||
"github.com/pocketbase/pocketbase/tools/subscriptions"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestRealtimeAuthorization(t *testing.T) {
|
||||
t.Setenv("SHARE_ALL_SYSTEMS", "false")
|
||||
t.Setenv("BESZEL_HUB_SHARE_ALL_SYSTEMS", "")
|
||||
app, err := pbtests.NewTestApp(t.TempDir())
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(app.Cleanup)
|
||||
_, err = app.DB().NewQuery(`CREATE TABLE IF NOT EXISTS systems (id TEXT PRIMARY KEY, users TEXT)`).Execute()
|
||||
require.NoError(t, err)
|
||||
_, err = app.DB().NewQuery(`INSERT INTO systems (id, users) VALUES ('target', '["member"]')`).Execute()
|
||||
require.NoError(t, err)
|
||||
member := core.NewRecord(core.NewAuthCollection("users"))
|
||||
member.Id = "member"
|
||||
outsider := core.NewRecord(member.Collection())
|
||||
outsider.Id = "outsider"
|
||||
system := &System{Id: "target"}
|
||||
sm := newRealtimeTestManager()
|
||||
sm.systems.Set(system.Id, system)
|
||||
// Keep the lifecycle bookkeeping active without starting an agent worker.
|
||||
sm.realtimeWorkerRun = true
|
||||
sm.realtimeWorkerStop = make(chan struct{})
|
||||
t.Cleanup(sm.stopRealtimeWorker)
|
||||
topic := `rt_metrics?options={"query":{"system":"target"}}`
|
||||
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
auth *core.Record
|
||||
topic string
|
||||
share bool
|
||||
allowed bool
|
||||
}{
|
||||
{"guest", nil, topic, false, false},
|
||||
{"outsider", outsider, topic, false, false},
|
||||
{"member", member, topic, false, true},
|
||||
{"missing system", member, `rt_metrics`, false, false},
|
||||
{"unknown system", member, `rt_metrics?options={"query":{"system":"missing"}}`, false, false},
|
||||
{"malformed options", member, `rt_metrics?options=invalid`, false, false},
|
||||
{"prefix variant", outsider, `rt_metrics_extra?options={"query":{"system":"target"}}`, false, false},
|
||||
{"shared outsider", outsider, topic, true, true},
|
||||
{"shared guest", nil, topic, true, false},
|
||||
{"other topic", nil, "systems/*", false, true},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
if tc.share {
|
||||
t.Setenv("BESZEL_HUB_SHARE_ALL_SYSTEMS", "true")
|
||||
}
|
||||
client := subscriptions.NewDefaultClient()
|
||||
client.Subscribe("existing")
|
||||
e := &core.RealtimeSubscribeRequestEvent{
|
||||
RequestEvent: &core.RequestEvent{App: app, Auth: tc.auth},
|
||||
Client: client, Subscriptions: []string{tc.topic},
|
||||
}
|
||||
called := false
|
||||
h := &hook.Hook[*core.RealtimeSubscribeRequestEvent]{}
|
||||
h.BindFunc(sm.onRealtimeSubscribeRequest)
|
||||
err := h.Trigger(e, func(e *core.RealtimeSubscribeRequestEvent) error {
|
||||
called = true
|
||||
client.Unsubscribe()
|
||||
client.Subscribe(e.Subscriptions...)
|
||||
return nil
|
||||
})
|
||||
if tc.allowed {
|
||||
require.NoError(t, err)
|
||||
assert.True(t, called)
|
||||
} else {
|
||||
require.Error(t, err)
|
||||
assert.False(t, called)
|
||||
assert.True(t, client.HasSubscription("existing"))
|
||||
assert.False(t, client.HasSubscription(tc.topic))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("broadcast checks current access", func(t *testing.T) {
|
||||
client := subscriptions.NewDefaultClient()
|
||||
client.Subscribe(topic)
|
||||
app.SubscriptionsBroker().Register(client)
|
||||
defer app.SubscriptionsBroker().Unregister(client.Id())
|
||||
secondClient := subscriptions.NewDefaultClient()
|
||||
secondClient.Subscribe(topic)
|
||||
app.SubscriptionsBroker().Register(secondClient)
|
||||
defer app.SubscriptionsBroker().Unregister(secondClient.Id())
|
||||
check := func(auth *core.Record, allowed bool) {
|
||||
t.Helper()
|
||||
client.Set(apis.RealtimeClientAuthKey, auth)
|
||||
secondClient.Set(apis.RealtimeClientAuthKey, auth)
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
notify(app, system, topic, []byte(`{"cpu":42}`))
|
||||
close(done)
|
||||
}()
|
||||
// Even on failure, drain pending sends and join the broadcaster before
|
||||
// unregistering clients, which closes their channels.
|
||||
defer func() {
|
||||
for {
|
||||
select {
|
||||
case <-client.Channel():
|
||||
case <-secondClient.Channel():
|
||||
case <-done:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
var received [2]int
|
||||
timer := time.NewTimer(time.Second)
|
||||
defer timer.Stop()
|
||||
for {
|
||||
select {
|
||||
case msg := <-client.Channel():
|
||||
received[0]++
|
||||
assert.Equal(t, topic, msg.Name)
|
||||
case msg := <-secondClient.Channel():
|
||||
received[1]++
|
||||
assert.Equal(t, topic, msg.Name)
|
||||
case <-done:
|
||||
want := [2]int{}
|
||||
if allowed {
|
||||
want = [2]int{1, 1}
|
||||
}
|
||||
assert.Equal(t, want, received)
|
||||
return
|
||||
case <-timer.C:
|
||||
t.Fatal("broadcast did not finish")
|
||||
}
|
||||
}
|
||||
}
|
||||
check(nil, false)
|
||||
check(outsider, false)
|
||||
check(member, true)
|
||||
_, err := app.DB().NewQuery(`UPDATE systems SET users = '[]'`).Execute()
|
||||
require.NoError(t, err)
|
||||
check(member, false)
|
||||
t.Setenv("BESZEL_HUB_SHARE_ALL_SYSTEMS", "true")
|
||||
check(outsider, true)
|
||||
check(nil, false)
|
||||
})
|
||||
}
|
||||
|
||||
func newRealtimeTestManager() *SystemManager {
|
||||
return &SystemManager{
|
||||
systems: store.New(map[string]*System{}),
|
||||
activeSubscriptions: make(map[string]*subscriptionInfo),
|
||||
}
|
||||
}
|
||||
|
||||
func TestRealtimeFetchesDoNotOverlapPerSystem(t *testing.T) {
|
||||
sm := newRealtimeTestManager()
|
||||
sm.activeSubscriptions["one"] = &subscriptionInfo{subscription: "rt_metrics_one"}
|
||||
sm.activeSubscriptions["two"] = &subscriptionInfo{subscription: "rt_metrics_two"}
|
||||
|
||||
first := sm.claimRealtimeFetches()
|
||||
require.Len(t, first, 2)
|
||||
assert.Empty(t, sm.claimRealtimeFetches())
|
||||
|
||||
sm.finishRealtimeFetch(first[0])
|
||||
next := sm.claimRealtimeFetches()
|
||||
require.Len(t, next, 1)
|
||||
assert.Equal(t, first[0].systemID, next[0].systemID)
|
||||
|
||||
sm.finishRealtimeFetch(first[1])
|
||||
sm.finishRealtimeFetch(next[0])
|
||||
}
|
||||
|
||||
func TestFinishingOldRealtimeFetchDoesNotReleaseReplacement(t *testing.T) {
|
||||
sm := newRealtimeTestManager()
|
||||
oldInfo := &subscriptionInfo{subscription: "old"}
|
||||
sm.activeSubscriptions["system"] = oldInfo
|
||||
|
||||
fetch := sm.claimRealtimeFetches()[0]
|
||||
newInfo := &subscriptionInfo{subscription: "new", fetching: true}
|
||||
sm.activeSubscriptions["system"] = newInfo
|
||||
|
||||
sm.finishRealtimeFetch(fetch)
|
||||
assert.True(t, newInfo.fetching)
|
||||
}
|
||||
|
||||
func TestRealtimeSubscriptionLifecycle(t *testing.T) {
|
||||
sm := newRealtimeTestManager()
|
||||
options := subscriptions.SubscriptionOptions{Query: map[string]string{"system": "system"}}
|
||||
|
||||
sm.addRealtimeSubscription("system", "rt_metrics")
|
||||
sm.addRealtimeSubscription("system", "rt_metrics")
|
||||
|
||||
sm.realtimeMutex.Lock()
|
||||
firstStop := sm.realtimeWorkerStop
|
||||
assert.True(t, sm.realtimeWorkerRun)
|
||||
assert.Equal(t, 2, sm.activeSubscriptions["system"].connectedClients)
|
||||
sm.realtimeMutex.Unlock()
|
||||
|
||||
sm.removeRealtimeSubscription("rt_metrics", options)
|
||||
sm.realtimeMutex.Lock()
|
||||
assert.True(t, sm.realtimeWorkerRun)
|
||||
assert.Equal(t, 1, sm.activeSubscriptions["system"].connectedClients)
|
||||
sm.realtimeMutex.Unlock()
|
||||
|
||||
sm.removeRealtimeSubscription("rt_metrics", options)
|
||||
sm.realtimeMutex.Lock()
|
||||
assert.False(t, sm.realtimeWorkerRun)
|
||||
assert.Empty(t, sm.activeSubscriptions)
|
||||
sm.realtimeMutex.Unlock()
|
||||
select {
|
||||
case <-firstStop:
|
||||
default:
|
||||
t.Fatal("worker stop channel was not closed")
|
||||
}
|
||||
|
||||
// A later subscription must get a new stop channel owned by its worker.
|
||||
sm.addRealtimeSubscription("system", "rt_metrics")
|
||||
sm.realtimeMutex.Lock()
|
||||
secondStop := sm.realtimeWorkerStop
|
||||
assert.NotEqual(t, firstStop, secondStop)
|
||||
sm.realtimeMutex.Unlock()
|
||||
sm.stopRealtimeWorker()
|
||||
}
|
||||
@@ -32,13 +32,12 @@ func (sys *System) FetchAndSaveZfsPools(force bool) error {
|
||||
sys.recordZfsFetchResult(err, 0)
|
||||
return err
|
||||
}
|
||||
if zfsData == nil || !zfsData.Complete {
|
||||
err = errIncompleteZfsData
|
||||
sys.recordZfsFetchResult(err, 0)
|
||||
return err
|
||||
}
|
||||
err = sys.saveZfsPools(zfsData)
|
||||
sys.recordZfsFetchResult(err, len(zfsData.Pools))
|
||||
poolCount := 0
|
||||
if zfsData != nil {
|
||||
poolCount = len(zfsData.Pools)
|
||||
}
|
||||
sys.recordZfsFetchResult(err, poolCount)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -79,7 +78,7 @@ func (sys *System) zfsFetchInterval() time.Duration {
|
||||
// saveZfsPools saves ZFS pool detail data to the zfs_pools collection and
|
||||
// removes records for pools no longer reported by a complete agent inventory.
|
||||
func (sys *System) saveZfsPools(zfsData *zfs.ZfsData) error {
|
||||
if zfsData == nil || !zfsData.Complete {
|
||||
if zfsData == nil || (!zfsData.CanRefreshPool("zfs") && !zfsData.CanRefreshPool("b:")) {
|
||||
return errIncompleteZfsData
|
||||
}
|
||||
|
||||
@@ -89,10 +88,10 @@ func (sys *System) saveZfsPools(zfsData *zfs.ZfsData) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return hub.RunInTransaction(func(txApp core.App) error {
|
||||
err = hub.RunInTransaction(func(txApp core.App) error {
|
||||
alive := make(map[string]bool, len(zfsData.Pools))
|
||||
for _, pool := range zfsData.Pools {
|
||||
if pool == nil {
|
||||
if pool == nil || !zfsData.CanRefreshPool(pool.Name) {
|
||||
continue
|
||||
}
|
||||
alive[pool.Name] = true
|
||||
@@ -111,7 +110,7 @@ func (sys *System) saveZfsPools(zfsData *zfs.ZfsData) error {
|
||||
return err
|
||||
}
|
||||
for _, record := range existing {
|
||||
if !alive[record.GetString("name")] {
|
||||
if name := record.GetString("name"); zfsData.CanRefreshPool(name) && !alive[name] {
|
||||
if err := txApp.Delete(record); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -119,6 +118,14 @@ func (sys *System) saveZfsPools(zfsData *zfs.ZfsData) error {
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Report partial failure only after committing healthy backend updates.
|
||||
if !zfsData.Complete {
|
||||
return errIncompleteZfsData
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sys *System) upsertZfsPoolRecord(app core.App, collection *core.Collection, pool *zfs.PoolDetail) error {
|
||||
@@ -135,10 +142,12 @@ func (sys *System) upsertZfsPoolRecord(app core.App, collection *core.Collection
|
||||
|
||||
record.Set("system", sys.Id)
|
||||
record.Set("name", pool.Name)
|
||||
record.Set("display_name", pool.DisplayName)
|
||||
record.Set("health", pool.Health)
|
||||
record.Set("size", pool.Size)
|
||||
record.Set("alloc", pool.Alloc)
|
||||
record.Set("free", pool.Free)
|
||||
record.Set("raw", pool.Raw)
|
||||
record.Set("scrub", pool.Scrub)
|
||||
record.Set("vdevs", pool.Vdevs)
|
||||
record.Set("datasets", pool.Datasets)
|
||||
@@ -172,7 +181,9 @@ func (sys *System) syncZfsPoolHealth(app core.App, pools map[string]*system.ZfsP
|
||||
record.Set("id", recordID)
|
||||
record.Set("system", sys.Id)
|
||||
record.Set("name", name)
|
||||
record.Set("display_name", pool.DisplayName)
|
||||
record.Set("health", pool.Health)
|
||||
record.Set("raw", pool.Raw)
|
||||
record.Set("size", uint64(pool.Total*gib))
|
||||
record.Set("alloc", uint64(pool.Used*gib))
|
||||
record.Set("free", uint64(max(pool.Total-pool.Used, 0)*gib))
|
||||
@@ -181,10 +192,15 @@ func (sys *System) syncZfsPoolHealth(app core.App, pools map[string]*system.ZfsP
|
||||
}
|
||||
continue
|
||||
}
|
||||
if record.GetString("health") == pool.Health {
|
||||
if record.GetString("health") == pool.Health && record.GetBool("raw") == pool.Raw && record.GetString("display_name") == pool.DisplayName {
|
||||
continue
|
||||
}
|
||||
record.Set("display_name", pool.DisplayName)
|
||||
record.Set("health", pool.Health)
|
||||
record.Set("raw", pool.Raw)
|
||||
record.Set("size", uint64(pool.Total*gib))
|
||||
record.Set("alloc", uint64(pool.Used*gib))
|
||||
record.Set("free", uint64(max(pool.Total-pool.Used, 0)*gib))
|
||||
if err := app.SaveNoValidate(record); err != nil {
|
||||
return fmt.Errorf("updating ZFS pool health %q: %w", name, err)
|
||||
}
|
||||
|
||||
@@ -123,6 +123,44 @@ func TestSaveZfsPoolsIncompletePreservesRecords(t *testing.T) {
|
||||
assert.Len(t, records, 1)
|
||||
}
|
||||
|
||||
func TestSavePartialBackendInventory(t *testing.T) {
|
||||
for _, healthy := range []string{"zfs", "btrfs"} {
|
||||
t.Run(healthy, func(t *testing.T) {
|
||||
sys, app := newTestSystemWithHub(t)
|
||||
healthyKey, failedKey := "tank", "b:uuid"
|
||||
if healthy == "btrfs" {
|
||||
healthyKey, failedKey = failedKey, healthyKey
|
||||
}
|
||||
initial := &zfs.ZfsData{Complete: true, Pools: []*zfs.PoolDetail{
|
||||
{Name: healthyKey, Alloc: 10}, {Name: failedKey, Alloc: 10},
|
||||
}}
|
||||
require.NoError(t, sys.saveZfsPools(initial))
|
||||
failedID := makeStableHashId(sys.Id, failedKey)
|
||||
before, err := app.FindRecordById("zfs_pools", failedID)
|
||||
require.NoError(t, err)
|
||||
partial := &zfs.ZfsData{CompleteBackends: []string{healthy}, Pools: []*zfs.PoolDetail{
|
||||
{Name: healthyKey, Alloc: 20}, {Name: failedKey, Alloc: 99},
|
||||
}}
|
||||
assert.ErrorIs(t, sys.saveZfsPools(partial), errIncompleteZfsData)
|
||||
fresh, err := app.FindRecordById("zfs_pools", makeStableHashId(sys.Id, healthyKey))
|
||||
require.NoError(t, err)
|
||||
assert.EqualValues(t, 20, fresh.GetInt("alloc"))
|
||||
cached, err := app.FindRecordById("zfs_pools", failedID)
|
||||
require.NoError(t, err)
|
||||
assert.EqualValues(t, 10, cached.GetInt("alloc"))
|
||||
assert.Equal(t, before.GetDateTime("details_updated"), cached.GetDateTime("details_updated"))
|
||||
// An empty successful backend can prune, even while the other fails.
|
||||
partial.Pools = nil
|
||||
assert.ErrorIs(t, sys.saveZfsPools(partial), errIncompleteZfsData)
|
||||
records, err := app.FindRecordsByFilter("zfs_pools", "system={:system}", "", 0, 0, map[string]any{"system": sys.Id})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, records, 1)
|
||||
assert.Equal(t, failedKey, records[0].GetString("name"))
|
||||
require.NoError(t, sys.saveZfsPools(&zfs.ZfsData{Complete: true}))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSyncZfsPoolHealthWritesOnlyTransitions(t *testing.T) {
|
||||
sys, app := newTestSystemWithHub(t)
|
||||
collection, err := app.FindCachedCollectionByNameOrId("zfs_pools")
|
||||
@@ -151,3 +189,42 @@ func TestSyncZfsPoolHealthWritesOnlyTransitions(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "DEGRADED", record.GetString("health"))
|
||||
}
|
||||
|
||||
func TestZfsRawCapacityPersistence(t *testing.T) {
|
||||
sys, app := newTestSystemWithHub(t)
|
||||
require.NoError(t, sys.saveZfsPools(&zfs.ZfsData{Complete: true, Pools: []*zfs.PoolDetail{{Name: "btrfs", Size: 200, Alloc: 10, Raw: true}}}))
|
||||
record, err := app.FindRecordById("zfs_pools", makeStableHashId(sys.Id, "btrfs"))
|
||||
require.NoError(t, err)
|
||||
require.True(t, record.GetBool("raw"))
|
||||
require.NoError(t, sys.syncZfsPoolHealth(app, map[string]*system.ZfsPool{"btrfs": {Total: 1, Used: 0.25}}))
|
||||
record, err = app.FindRecordById("zfs_pools", record.Id)
|
||||
require.NoError(t, err)
|
||||
assert.False(t, record.GetBool("raw"))
|
||||
assert.EqualValues(t, 1024*1024*1024, record.GetInt("size"))
|
||||
}
|
||||
|
||||
func TestBtrfsDisplayNameKeepsRecordIdentity(t *testing.T) {
|
||||
sys, app := newTestSystemWithHub(t)
|
||||
key := "b:11111111-1111-4111-8111-111111111111"
|
||||
require.NoError(t, sys.syncZfsPoolHealth(app, map[string]*system.ZfsPool{
|
||||
key: {DisplayName: "tank", Health: "ONLINE"},
|
||||
"tank": {Health: "ONLINE"},
|
||||
}))
|
||||
id := makeStableHashId(sys.Id, key)
|
||||
record, err := app.FindRecordById("zfs_pools", id)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "tank", record.GetString("display_name"))
|
||||
require.NoError(t, sys.syncZfsPoolHealth(app, map[string]*system.ZfsPool{key: {DisplayName: "renamed", Health: "ONLINE"}}))
|
||||
record, err = app.FindRecordById("zfs_pools", id)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, key, record.GetString("name"))
|
||||
assert.Equal(t, "renamed", record.GetString("display_name"))
|
||||
require.NoError(t, sys.saveZfsPools(&zfs.ZfsData{Complete: true, Pools: []*zfs.PoolDetail{
|
||||
{Name: key, DisplayName: "detail name", Health: "ONLINE"}, {Name: "tank", Health: "ONLINE"},
|
||||
}}))
|
||||
record, err = app.FindRecordById("zfs_pools", id)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "detail name", record.GetString("display_name"))
|
||||
_, err = app.FindRecordById("zfs_pools", makeStableHashId(sys.Id, "tank"))
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
@@ -3,9 +3,11 @@
|
||||
package systems_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/fxamacker/cbor/v2"
|
||||
"github.com/henrygd/beszel/internal/entities/system"
|
||||
"github.com/henrygd/beszel/internal/entities/systemd"
|
||||
"github.com/henrygd/beszel/internal/hub/systems"
|
||||
@@ -15,6 +17,42 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCreateRecordsRejectsNullSystemdService(t *testing.T) {
|
||||
hub, user := tests.GetHubWithUser(t)
|
||||
defer hub.Cleanup()
|
||||
records, err := tests.CreateSystems(hub, 1, user.Id, "paused")
|
||||
require.NoError(t, err)
|
||||
sys, err := hub.GetSystemManager().GetSystem(records[0].Id)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, systems.CreateSystemdStatsRecords(hub, []*systemd.Service{
|
||||
{Name: "existing.service", State: systemd.StatusFailed},
|
||||
}, records[0].Id))
|
||||
|
||||
for _, services := range []string{`[null]`, `[{"name":"new.service"},null]`, `[null,{"name":"new.service"}]`} {
|
||||
for _, encoding := range []string{"json", "cbor"} {
|
||||
t.Run(encoding+"/"+services, func(t *testing.T) {
|
||||
var data system.CombinedData
|
||||
require.NoError(t, json.Unmarshal([]byte(`{"systemd":`+services+`}`), &data))
|
||||
if encoding == "cbor" {
|
||||
encoded, err := cbor.Marshal(data)
|
||||
require.NoError(t, err)
|
||||
data = system.CombinedData{}
|
||||
require.NoError(t, cbor.Unmarshal(encoded, &data))
|
||||
}
|
||||
_, err := sys.CreateRecords(&data)
|
||||
require.ErrorContains(t, err, "null systemd service")
|
||||
var names []string
|
||||
require.NoError(t, hub.DB().Select("name").From("systemd_services").
|
||||
Where(dbx.HashExp{"system": records[0].Id}).Column(&names))
|
||||
assert.Equal(t, []string{"existing.service"}, names)
|
||||
count, err := hub.CountRecords("system_stats", dbx.HashExp{"system": records[0].Id})
|
||||
require.NoError(t, err)
|
||||
assert.Zero(t, count, "invalid snapshot must roll back system stats")
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateRecordsHandlesSystemdAlertLifecycle(t *testing.T) {
|
||||
hub, user := tests.GetHubWithUser(t)
|
||||
defer hub.Cleanup()
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
m "github.com/pocketbase/pocketbase/migrations"
|
||||
)
|
||||
|
||||
func init() {
|
||||
m.Register(func(app core.App) error {
|
||||
c, err := app.FindCollectionByNameOrId("zfs_pools")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Fields.Add(&core.TextField{Name: "display_name"})
|
||||
c.Fields.Add(&core.BoolField{Name: "raw"})
|
||||
return app.Save(c)
|
||||
}, func(app core.App) error {
|
||||
c, err := app.FindCollectionByNameOrId("zfs_pools")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Fields.RemoveByName("display_name")
|
||||
c.Fields.RemoveByName("raw")
|
||||
|
||||
return app.Save(c)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
m "github.com/pocketbase/pocketbase/migrations"
|
||||
)
|
||||
|
||||
func init() {
|
||||
m.Register(func(app core.App) error {
|
||||
collection, err := app.FindCollectionByNameOrId("containers")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
collection.Fields.Add(&core.BoolField{Name: "updatable"})
|
||||
return app.Save(collection)
|
||||
}, func(app core.App) error {
|
||||
collection, err := app.FindCollectionByNameOrId("containers")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
collection.Fields.RemoveByName("updatable")
|
||||
return app.Save(collection)
|
||||
})
|
||||
}
|
||||
@@ -198,6 +198,7 @@ func AverageSystemStatsSlice(records []system.Stats) system.Stats {
|
||||
var fanSums map[string]uint64
|
||||
fanCount := uint64(0)
|
||||
zfsPoolCounts := make(map[string]uint64)
|
||||
zfsCapacityCounts := make(map[string]uint64)
|
||||
|
||||
// Accumulate totals
|
||||
for i := range records {
|
||||
@@ -350,9 +351,19 @@ func AverageSystemStatsSlice(records []system.Stats) system.Stats {
|
||||
}
|
||||
pool := sum.ZfsPools[name]
|
||||
if pool == nil {
|
||||
pool = &system.ZfsPool{}
|
||||
pool = &system.ZfsPool{HideUsage: value.HideUsage, HideIO: value.HideIO}
|
||||
sum.ZfsPools[name] = pool
|
||||
}
|
||||
// Never average physical and usable capacity into the same value.
|
||||
if pool.Raw != value.Raw {
|
||||
pool.Total, pool.Used = 0, 0
|
||||
zfsCapacityCounts[name] = 0
|
||||
}
|
||||
pool.HideUsage = pool.HideUsage && value.HideUsage
|
||||
pool.HideIO = pool.HideIO && value.HideIO
|
||||
pool.DisplayName = value.DisplayName
|
||||
pool.Raw = value.Raw
|
||||
zfsCapacityCounts[name]++
|
||||
pool.Total += value.Total
|
||||
pool.Used += value.Used
|
||||
pool.ReadBytes += value.ReadBytes
|
||||
@@ -476,8 +487,8 @@ func AverageSystemStatsSlice(records []system.Stats) system.Stats {
|
||||
// Average ZFS pool stats.
|
||||
for name, pool := range sum.ZfsPools {
|
||||
entryCount := zfsPoolCounts[name]
|
||||
pool.Total = twoDecimals(pool.Total / float64(entryCount))
|
||||
pool.Used = twoDecimals(pool.Used / float64(entryCount))
|
||||
pool.Total = twoDecimals(pool.Total / float64(zfsCapacityCounts[name]))
|
||||
pool.Used = twoDecimals(pool.Used / float64(zfsCapacityCounts[name]))
|
||||
pool.ReadBytes /= entryCount
|
||||
pool.WriteBytes /= entryCount
|
||||
}
|
||||
|
||||
@@ -889,3 +889,34 @@ func TestAverageContainerStatsSlice_ManyContainers(t *testing.T) {
|
||||
assert.Equal(t, 35.0, result[2].Cpu)
|
||||
assert.Equal(t, 45.0, result[3].Cpu)
|
||||
}
|
||||
|
||||
func TestAverageSystemStatsSlice_ZfsCapacityModes(t *testing.T) {
|
||||
for _, raw := range []bool{false, true} {
|
||||
result := records.AverageSystemStatsSlice([]system.Stats{
|
||||
{ZfsPools: map[string]*system.ZfsPool{"pool": {Total: 200, Used: 40, Raw: !raw, ReadBytes: 100}}},
|
||||
{ZfsPools: map[string]*system.ZfsPool{"pool": {Total: 100, Used: 10, Raw: raw, ReadBytes: 300}}},
|
||||
})
|
||||
assert.Equal(t, &system.ZfsPool{Total: 100, Used: 10, Raw: raw, ReadBytes: 200}, result.ZfsPools["pool"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestAverageSystemStatsSlice_ZfsDuplicateCharts(t *testing.T) {
|
||||
for _, hide := range []bool{false, true} {
|
||||
result := records.AverageSystemStatsSlice([]system.Stats{
|
||||
{ZfsPools: map[string]*system.ZfsPool{"pool": {HideUsage: true, HideIO: true}}},
|
||||
{ZfsPools: map[string]*system.ZfsPool{"pool": {HideUsage: hide, HideIO: hide}}},
|
||||
})
|
||||
assert.Equal(t, hide, result.ZfsPools["pool"].HideUsage)
|
||||
assert.Equal(t, hide, result.ZfsPools["pool"].HideIO)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAverageSystemStatsSlice_BtrfsDisplayName(t *testing.T) {
|
||||
result := records.AverageSystemStatsSlice([]system.Stats{
|
||||
{ZfsPools: map[string]*system.ZfsPool{"b:uuid": {DisplayName: "before", Used: 10}}},
|
||||
{ZfsPools: map[string]*system.ZfsPool{"b:uuid": {DisplayName: "after", Used: 20}}},
|
||||
})
|
||||
require.Len(t, result.ZfsPools, 1)
|
||||
assert.Equal(t, "after", result.ZfsPools["b:uuid"].DisplayName)
|
||||
assert.Equal(t, float64(15), result.ZfsPools["b:uuid"].Used)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { cn, decimalString, formatBytes, hourWithSeconds } from "@/lib/utils"
|
||||
import type { ContainerRecord } from "@/types"
|
||||
import { ContainerHealth, ContainerHealthLabels } from "@/lib/enums"
|
||||
import {
|
||||
CircleArrowUpIcon,
|
||||
ClockIcon,
|
||||
ContainerIcon,
|
||||
CpuIcon,
|
||||
@@ -177,11 +178,25 @@ export const containerChartCols: ColumnDef<ContainerRecord>[] = [
|
||||
header: ({ column }) => (
|
||||
<HeaderButton column={column} name={t({ message: "Image", context: "Docker image" })} Icon={LayersIcon} />
|
||||
),
|
||||
cell: ({ getValue }) => {
|
||||
cell: ({ getValue, row }) => {
|
||||
const val = getValue() as string
|
||||
return (
|
||||
<div className="ms-1 xl:w-40 truncate" title={val}>
|
||||
{val}
|
||||
<div className="ms-1 xl:w-40 flex items-center gap-2">
|
||||
<span className="truncate" title={val}>
|
||||
{val}
|
||||
</span>
|
||||
{row.original.updatable && (
|
||||
<Tooltip>
|
||||
<TooltipTrigger
|
||||
className="shrink-0 rounded-sm text-emerald-600 dark:text-emerald-400 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
||||
aria-label={t`Image update available`}
|
||||
onClick={(event) => event.stopPropagation()}
|
||||
>
|
||||
<CircleArrowUpIcon className="size-4" aria-hidden="true" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>{t`Image update available`}</TooltipContent>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
},
|
||||
|
||||
@@ -66,7 +66,7 @@ export default function ContainersTable({ systemId }: { systemId?: string }) {
|
||||
function fetchData(systemId?: string) {
|
||||
pb.collection<ContainerRecord>("containers")
|
||||
.getList(0, 2000, {
|
||||
fields: "id,name,image,ports,cpu,memory,net,health,status,system,updated",
|
||||
fields: "id,name,image,updatable,ports,cpu,memory,net,health,status,system,updated",
|
||||
filter: systemId ? pb.filter("system={:system}", { system: systemId }) : undefined,
|
||||
})
|
||||
.then(({ items }) => {
|
||||
|
||||
@@ -15,7 +15,7 @@ export default function () {
|
||||
const page = useStore($router)
|
||||
const [isFirstRun, setFirstRun] = useState(false)
|
||||
const [authMethods, setAuthMethods] = useState<AuthMethodsList>()
|
||||
const { theme } = useTheme()
|
||||
const { resolvedTheme } = useTheme()
|
||||
|
||||
useEffect(() => {
|
||||
document.title = t`Login` + " / Beszel"
|
||||
@@ -54,7 +54,7 @@ export default function () {
|
||||
<div
|
||||
className="grid gap-5 w-full px-4 mx-auto"
|
||||
// @ts-expect-error
|
||||
style={{ maxWidth: "21.5em", "--border": theme == "light" ? "hsl(30, 8%, 70%)" : "hsl(220, 3%, 25%)" }}
|
||||
style={{ maxWidth: "21.5em", "--border": resolvedTheme == "light" ? "hsl(30, 8%, 70%)" : "hsl(220, 3%, 25%)" }}
|
||||
>
|
||||
<div className="absolute top-3 right-3">
|
||||
<ModeToggle />
|
||||
|
||||
@@ -8,7 +8,7 @@ import { useSystemData } from "./system/use-system-data"
|
||||
import { CpuChart, ContainerCpuChart } from "./system/charts/cpu-charts"
|
||||
import { MemoryChart, ContainerMemoryChart, SwapChart } from "./system/charts/memory-charts"
|
||||
import { RootDiskCharts, ExtraFsCharts } from "./system/charts/disk-charts"
|
||||
import { ZfsCharts } from "./system/charts/zfs-charts"
|
||||
import { ZfsCharts } from "./system/charts/storage-pool-charts"
|
||||
import { BandwidthChart, ContainerNetworkChart } from "./system/charts/network-charts"
|
||||
import { TemperatureChart, FanChart, BatteryChart } from "./system/charts/sensor-charts"
|
||||
import { GpuPowerChart, GpuCharts } from "./system/charts/gpu-charts"
|
||||
|
||||
@@ -95,7 +95,7 @@ export function ChartCard({
|
||||
className,
|
||||
}: {
|
||||
title: string
|
||||
description: string
|
||||
description: React.ReactNode
|
||||
children: React.ReactNode
|
||||
grid?: boolean
|
||||
empty?: boolean
|
||||
|
||||
+20
-12
@@ -3,6 +3,7 @@ import AreaChartDefault from "@/components/charts/area-chart"
|
||||
import { decimalString, formatBytes, toFixedFloat } from "@/lib/utils"
|
||||
import type { SystemStatsRecord } from "@/types"
|
||||
import { ChartCard } from "../chart-card"
|
||||
import { RawCapacityLabel } from "../raw-capacity-label"
|
||||
import { Unit } from "@/lib/enums"
|
||||
import { useStore } from "@nanostores/react"
|
||||
import { $userSettings } from "@/lib/stores"
|
||||
@@ -10,9 +11,11 @@ import type { SystemData } from "../use-system-data"
|
||||
|
||||
// Accessors for ZFS metrics
|
||||
const poolUsage =
|
||||
(name: string) =>
|
||||
({ stats }: SystemStatsRecord) =>
|
||||
stats?.z?.[name]?.du ?? 0
|
||||
(name: string, raw: boolean) =>
|
||||
({ stats }: SystemStatsRecord) => {
|
||||
const pool = stats?.z?.[name]
|
||||
return pool && !!pool.raw === raw ? pool.du : null
|
||||
}
|
||||
const poolRead =
|
||||
(name: string) =>
|
||||
({ stats }: SystemStatsRecord) =>
|
||||
@@ -26,9 +29,10 @@ export function ZfsPoolUsageChart({ systemData, poolName }: { systemData: System
|
||||
const { chartData, grid, dataEmpty } = systemData
|
||||
const latest = chartData.systemStats.at(-1)?.stats
|
||||
const pool = latest?.z?.[poolName]
|
||||
if (!pool) {
|
||||
if (!pool || pool.hu) {
|
||||
return null
|
||||
}
|
||||
const displayName = pool.n || poolName
|
||||
let poolTotal = pool.d
|
||||
// round to nearest GB
|
||||
if (poolTotal >= 100) {
|
||||
@@ -39,8 +43,8 @@ export function ZfsPoolUsageChart({ systemData, poolName }: { systemData: System
|
||||
<ChartCard
|
||||
empty={dataEmpty}
|
||||
grid={grid}
|
||||
title={`${poolName} ${t`Usage`}`}
|
||||
description={t`Usage of ZFS pool ${poolName}`}
|
||||
title={`${displayName} ${t`Usage`}`}
|
||||
description={pool.raw ? <RawCapacityLabel label={t`Raw usage of storage pool ${displayName}`} /> : t`Usage of storage pool ${displayName}`}
|
||||
>
|
||||
<AreaChartDefault
|
||||
chartData={chartData}
|
||||
@@ -57,7 +61,7 @@ export function ZfsPoolUsageChart({ systemData, poolName }: { systemData: System
|
||||
dataPoints={[
|
||||
{
|
||||
label: t`Pool Usage`,
|
||||
dataKey: poolUsage(poolName),
|
||||
dataKey: poolUsage(poolName, !!pool.raw),
|
||||
color: 4,
|
||||
opacity: 0.4,
|
||||
},
|
||||
@@ -70,15 +74,16 @@ export function ZfsPoolUsageChart({ systemData, poolName }: { systemData: System
|
||||
export function ZfsPoolIOChart({ systemData, poolName }: { systemData: SystemData; poolName: string }) {
|
||||
const { chartData, grid, dataEmpty } = systemData
|
||||
const userSettings = useStore($userSettings)
|
||||
if (!chartData.systemStats?.length) {
|
||||
if (!chartData.systemStats?.length || chartData.systemStats.at(-1)?.stats.z?.[poolName]?.hi) {
|
||||
return null
|
||||
}
|
||||
const displayName = chartData.systemStats.at(-1)?.stats.z?.[poolName]?.n || poolName
|
||||
return (
|
||||
<ChartCard
|
||||
empty={dataEmpty}
|
||||
grid={grid}
|
||||
title={`${poolName} I/O`}
|
||||
description={t`Throughput of ZFS pool ${poolName}`}
|
||||
title={`${displayName} I/O`}
|
||||
description={t`Throughput of storage pool ${displayName}`}
|
||||
>
|
||||
<AreaChartDefault
|
||||
chartData={chartData}
|
||||
@@ -114,12 +119,15 @@ export function ZfsPoolIOChart({ systemData, poolName }: { systemData: SystemDat
|
||||
export function ZfsCharts({ systemData }: { systemData: SystemData }) {
|
||||
const latest = systemData.chartData.systemStats?.at(-1)?.stats
|
||||
const pools = latest?.z ?? {}
|
||||
if (Object.keys(pools).length === 0) {
|
||||
const visiblePools = Object.keys(pools)
|
||||
.filter((name) => !pools[name].hu || !pools[name].hi)
|
||||
.sort((a, b) => (pools[a].n || a).localeCompare(pools[b].n || b, undefined, { numeric: true }) || a.localeCompare(b))
|
||||
if (visiblePools.length === 0) {
|
||||
return null
|
||||
}
|
||||
return (
|
||||
<div className="grid xl:grid-cols-2 gap-4">
|
||||
{Object.keys(pools).map((poolName) => (
|
||||
{visiblePools.map((poolName) => (
|
||||
<div key={poolName} className="contents">
|
||||
<ZfsPoolUsageChart systemData={systemData} poolName={poolName} />
|
||||
<ZfsPoolIOChart systemData={systemData} poolName={poolName} />
|
||||
@@ -24,7 +24,7 @@ export function LazySmartTable({ systemId }: { systemId: string }) {
|
||||
)
|
||||
}
|
||||
|
||||
const ZfsTable = lazy(() => import("./zfs-table"))
|
||||
const ZfsTable = lazy(() => import("./storage-pools-table"))
|
||||
|
||||
export function LazyZfsTable({ systemId }: { systemId: string }) {
|
||||
const { isIntersecting, ref } = useIntersectionObserver({ rootMargin: "90px" })
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import { t } from "@lingui/core/macro"
|
||||
import { InfoIcon } from "lucide-react"
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"
|
||||
|
||||
export function RawCapacityLabel({ label = t`Raw` }: { label?: string }) {
|
||||
return (
|
||||
<span className="inline-flex items-center gap-1">
|
||||
{label}
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<button
|
||||
type="button"
|
||||
aria-label="About raw capacity"
|
||||
className="inline-flex rounded-sm text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
||||
>
|
||||
<InfoIcon className="size-3.5" aria-hidden="true" />
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent className="max-w-64">
|
||||
{t`Physical device space. True usable capacity is unknown. Pool disk usage alerts are disabled.`}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</span>
|
||||
)
|
||||
}
|
||||
+46
-16
@@ -26,6 +26,7 @@ import {
|
||||
CheckCircleIcon,
|
||||
CircleAlertIcon,
|
||||
ClockIcon,
|
||||
DatabaseIcon,
|
||||
HardDriveDownloadIcon,
|
||||
HardDriveIcon,
|
||||
HardDriveUploadIcon,
|
||||
@@ -35,10 +36,13 @@ import {
|
||||
RotateCwIcon,
|
||||
XCircleIcon,
|
||||
XIcon,
|
||||
FolderTreeIcon,
|
||||
} from "lucide-react"
|
||||
import { useCallback, useEffect, useMemo, useState } from "react"
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react"
|
||||
|
||||
const ZFS_POOL_FIELDS = "id,system,name,health,size,alloc,free,scrub,details_updated,updated"
|
||||
import { RawCapacityLabel } from "./raw-capacity-label"
|
||||
|
||||
const ZFS_POOL_FIELDS = "id,system,name,display_name,health,size,alloc,free,raw,scrub,details_updated,updated"
|
||||
|
||||
/** Maps a zpool health string to a Badge variant. */
|
||||
function healthVariant(health: string): "success" | "warning" | "danger" | "outline" {
|
||||
@@ -81,13 +85,30 @@ function HeaderButton<T>({ column, name, Icon }: { column: Column<T>; name: stri
|
||||
)
|
||||
}
|
||||
|
||||
function poolType(pool: ZfsPoolRecord): string {
|
||||
return pool.name.startsWith("b:") ? "Btrfs" : "ZFS"
|
||||
}
|
||||
|
||||
const columns: ColumnDef<ZfsPoolRecord>[] = [
|
||||
{
|
||||
accessorKey: "name",
|
||||
sortingFn: (a, b) => a.original.name.localeCompare(b.original.name),
|
||||
header: ({ column }) => <HeaderButton column={column} name={`Pool`} Icon={HardDriveIcon} />,
|
||||
id: "name",
|
||||
accessorFn: (pool) => pool.display_name || pool.name,
|
||||
header: ({ column }) => <HeaderButton column={column} name={`Pool`} Icon={DatabaseIcon} />,
|
||||
cell: ({ getValue }) => <span className="font-medium ms-1.5">{getValue() as string}</span>,
|
||||
},
|
||||
{
|
||||
id: "type",
|
||||
accessorFn: poolType,
|
||||
header: ({ column }) => <HeaderButton column={column} name={t`Type`} Icon={FolderTreeIcon} />,
|
||||
cell: ({ getValue }) => {
|
||||
const type = getValue() as string
|
||||
return (
|
||||
<Badge variant="outline" className={cn("border-transparent", type === "ZFS" ? "bg-blue-200 text-blue-800" : "bg-yellow-200 text-yellow-800")}>
|
||||
{type}
|
||||
</Badge>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "health",
|
||||
sortingFn: (a, b) => a.original.health.localeCompare(b.original.health),
|
||||
@@ -102,21 +123,21 @@ const columns: ColumnDef<ZfsPoolRecord>[] = [
|
||||
accessorFn: (record) => record.size,
|
||||
invertSorting: true,
|
||||
header: ({ column }) => <HeaderButton column={column} name={t`Capacity`} Icon={BinaryIcon} />,
|
||||
cell: ({ getValue }) => <span className="ms-1.5 tabular-nums">{formatCapacity(getValue() as number)}</span>,
|
||||
cell: ({ getValue, row }) => <span className="ms-1.5 tabular-nums">{formatCapacity(getValue() as number)}{row.original.raw ? ` (${t`Raw`})` : ""}</span>,
|
||||
},
|
||||
{
|
||||
id: "used",
|
||||
accessorFn: (record) => record.alloc,
|
||||
invertSorting: true,
|
||||
header: ({ column }) => <HeaderButton column={column} name={t`Used`} Icon={HardDriveDownloadIcon} />,
|
||||
cell: ({ getValue }) => <span className="ms-1.5 tabular-nums">{formatCapacity(getValue() as number)}</span>,
|
||||
cell: ({ getValue, row }) => <span className="ms-1.5 tabular-nums">{formatCapacity(getValue() as number)}{row.original.raw ? ` (${t`Raw`})` : ""}</span>,
|
||||
},
|
||||
{
|
||||
id: "free",
|
||||
accessorFn: (record) => record.free,
|
||||
invertSorting: true,
|
||||
header: ({ column }) => <HeaderButton column={column} name={t({ message: `Free`, context: "Free space" })} Icon={HardDriveUploadIcon} />,
|
||||
cell: ({ getValue }) => <span className="ms-1.5 tabular-nums">{formatCapacity(getValue() as number)}</span>,
|
||||
cell: ({ getValue, row }) => <span className="ms-1.5 tabular-nums">{row.original.raw ? "-" : formatCapacity(getValue() as number)}</span>,
|
||||
},
|
||||
{
|
||||
id: "scrub",
|
||||
@@ -201,7 +222,7 @@ const datasetColumns: ColumnDef<ZfsDataset>[] = [
|
||||
{
|
||||
accessorKey: "name",
|
||||
sortingFn: (a, b) => a.original.name.localeCompare(b.original.name),
|
||||
header: ({ column }) => <HeaderButton column={column} name={`Dataset`} Icon={HardDriveIcon} />,
|
||||
header: ({ column }) => <HeaderButton column={column} name={`Dataset`} Icon={DatabaseIcon} />,
|
||||
cell: ({ getValue }) => <span className="font-mono text-xs">{getValue() as string}</span>,
|
||||
},
|
||||
{
|
||||
@@ -310,6 +331,7 @@ function PoolSheet({
|
||||
onOpenChange: (open: boolean) => void
|
||||
}) {
|
||||
const [pool, setPool] = useState<ZfsPoolRecord | null>(null)
|
||||
const titleRef = useRef<HTMLHeadingElement>(null)
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
@@ -342,23 +364,30 @@ function PoolSheet({
|
||||
|
||||
return (
|
||||
<Sheet open={open} onOpenChange={onOpenChange}>
|
||||
<SheetContent className="w-full sm:max-w-220 gap-0 overflow-y-auto">
|
||||
<SheetContent
|
||||
className="w-full sm:max-w-220 gap-0 overflow-y-auto"
|
||||
onOpenAutoFocus={(event) => {
|
||||
event.preventDefault()
|
||||
titleRef.current?.focus()
|
||||
}}
|
||||
>
|
||||
<SheetHeader className="mb-0 border-b">
|
||||
<SheetTitle className="flex items-center gap-2">
|
||||
{pool ? pool.name : `ZFS Pool`}
|
||||
<SheetTitle ref={titleRef} tabIndex={-1} className="flex items-center gap-2 outline-none">
|
||||
{pool ? (pool.display_name || pool.name) : `Storage Pool`}
|
||||
{pool && <Badge variant={healthVariantValue}>{health}</Badge>}
|
||||
</SheetTitle>
|
||||
<SheetDescription className="flex flex-wrap items-center gap-x-2 gap-y-1">
|
||||
{pool?.size ? formatCapacity(pool.size) : null}
|
||||
{pool?.raw && <RawCapacityLabel />}
|
||||
{pool?.alloc ? (
|
||||
<>
|
||||
<Separator orientation="vertical" className="h-2.5 bg-muted-foreground opacity-70" />
|
||||
<span>
|
||||
<Trans>Used</Trans>: {formatCapacity(pool.alloc)}
|
||||
<Trans>Used</Trans>: {formatCapacity(pool.alloc)}{pool.raw ? ` (${t`Raw`})` : ""}
|
||||
</span>
|
||||
</>
|
||||
) : null}
|
||||
{pool?.free ? (
|
||||
{pool?.free && !pool.raw ? (
|
||||
<>
|
||||
<Separator orientation="vertical" className="h-2.5 bg-muted-foreground opacity-70" />
|
||||
<span>
|
||||
@@ -555,6 +584,7 @@ export default function ZfsTable({ systemId }: { systemId?: string }) {
|
||||
const table = useReactTable({
|
||||
data: zfsPools || ([] as ZfsPoolRecord[]),
|
||||
columns: tableColumns,
|
||||
initialState: { sorting: [{ id: "name", desc: false }] },
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
@@ -562,7 +592,7 @@ export default function ZfsTable({ systemId }: { systemId?: string }) {
|
||||
onGlobalFilterChange: setGlobalFilter,
|
||||
globalFilterFn: (row, _columnId, filterValue) => {
|
||||
const pool = row.original
|
||||
const searchString = `${pool.name} ${pool.health ?? ""}`.toLowerCase()
|
||||
const searchString = `${pool.display_name ?? ""} ${pool.name} ${poolType(pool)} ${pool.health ?? ""}`.toLowerCase()
|
||||
return (filterValue as string)
|
||||
.toLowerCase()
|
||||
.split(" ")
|
||||
@@ -587,7 +617,7 @@ export default function ZfsTable({ systemId }: { systemId?: string }) {
|
||||
<CardHeader className="p-0 mb-3 sm:mb-4">
|
||||
<div className="grid md:flex gap-x-5 gap-y-3 w-full items-end">
|
||||
<div className="px-2 sm:px-1">
|
||||
<CardTitle className="mb-2">ZFS</CardTitle>
|
||||
<CardTitle className="mb-2">Storage Pools</CardTitle>
|
||||
<CardDescription className="flex">
|
||||
<Trans>Click on a pool to view vdev and dataset details.</Trans>
|
||||
</CardDescription>
|
||||
@@ -171,6 +171,7 @@ export function useSystemData(id: string) {
|
||||
// get stats when system "changes." (Not just system to system,
|
||||
// also when new info comes in via systemManager realtime connection, indicating an update)
|
||||
useEffect(() => {
|
||||
const requestId = ++statsRequestId.current
|
||||
if (!system.id || !chartTime || chartTime === "1m") {
|
||||
return
|
||||
}
|
||||
@@ -179,7 +180,6 @@ export function useSystemData(id: string) {
|
||||
const { expectedInterval } = chartTimeData[chartTime]
|
||||
const ss_cache_key = `${systemId}_${chartTime}_system_stats`
|
||||
const cs_cache_key = `${systemId}_${chartTime}_container_stats`
|
||||
const requestId = ++statsRequestId.current
|
||||
|
||||
const cachedSystemStats = cache.get(ss_cache_key) as SystemStatsRecord[] | undefined
|
||||
const cachedContainerData = cache.get(cs_cache_key) as ChartData["containerData"] | undefined
|
||||
@@ -203,7 +203,7 @@ export function useSystemData(id: string) {
|
||||
getStats<SystemStatsRecord>("system_stats", systemId, chartTime),
|
||||
getStats<ContainerStatsRecord>("container_stats", systemId, chartTime),
|
||||
]).then(([systemStats, containerStats]) => {
|
||||
// If another request has been made since this one, ignore the results
|
||||
// Ignore responses for a previous system or chart time
|
||||
if (requestId !== statsRequestId.current) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -193,7 +193,7 @@ export function SystemsTableColumns(viewMode: "table" | "grid"): ColumnDef<Syste
|
||||
header: sortableHeader,
|
||||
},
|
||||
{
|
||||
accessorFn: ({ info }) => info.g || undefined,
|
||||
accessorFn: ({ info }) => info.g,
|
||||
id: "gpu",
|
||||
name: () => "GPU",
|
||||
cell: (info) => {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { createContext, useContext, useEffect, useState } from "react"
|
||||
|
||||
type Theme = "dark" | "light" | "system"
|
||||
type ResolvedTheme = "dark" | "light"
|
||||
|
||||
type ThemeProviderProps = {
|
||||
children: React.ReactNode
|
||||
@@ -10,11 +11,13 @@ type ThemeProviderProps = {
|
||||
|
||||
type ThemeProviderState = {
|
||||
theme: Theme
|
||||
resolvedTheme: ResolvedTheme
|
||||
setTheme: (theme: Theme) => void
|
||||
}
|
||||
|
||||
const initialState: ThemeProviderState = {
|
||||
theme: "system",
|
||||
resolvedTheme: "light",
|
||||
setTheme: () => null,
|
||||
}
|
||||
|
||||
@@ -27,24 +30,28 @@ export function ThemeProvider({
|
||||
...props
|
||||
}: ThemeProviderProps) {
|
||||
const [theme, setTheme] = useState<Theme>(() => (localStorage.getItem(storageKey) as Theme) || defaultTheme)
|
||||
const [systemDark, setSystemDark] = useState(() => window.matchMedia("(prefers-color-scheme: dark)").matches)
|
||||
|
||||
useEffect(() => {
|
||||
const media = window.matchMedia("(prefers-color-scheme: dark)")
|
||||
const onChange = (event: MediaQueryListEvent) => setSystemDark(event.matches)
|
||||
|
||||
media.addEventListener("change", onChange)
|
||||
return () => media.removeEventListener("change", onChange)
|
||||
}, [])
|
||||
|
||||
const resolvedTheme = theme === "system" ? (systemDark ? "dark" : "light") : theme
|
||||
|
||||
useEffect(() => {
|
||||
const root = window.document.documentElement
|
||||
|
||||
root.classList.remove("light", "dark")
|
||||
|
||||
if (theme === "system") {
|
||||
const systemTheme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"
|
||||
|
||||
root.classList.add(systemTheme)
|
||||
return
|
||||
}
|
||||
|
||||
root.classList.add(theme)
|
||||
}, [theme])
|
||||
root.classList.add(resolvedTheme)
|
||||
}, [resolvedTheme])
|
||||
|
||||
const value = {
|
||||
theme,
|
||||
resolvedTheme,
|
||||
setTheme: (theme: Theme) => {
|
||||
localStorage.setItem(storageKey, theme)
|
||||
setTheme(theme)
|
||||
|
||||
@@ -4,7 +4,7 @@ import { basePath } from "@/components/router"
|
||||
import { toast } from "@/components/ui/use-toast"
|
||||
import type { ChartTimes, UserSettings } from "@/types"
|
||||
import { $alerts, $allSystemsById, $allSystemsByName, $userSettings } from "./stores"
|
||||
import { chartTimeData } from "./utils"
|
||||
import { chartTimeData, debounce } from "./utils"
|
||||
|
||||
/** PocketBase JS Client */
|
||||
export const pb = new PocketBase(basePath)
|
||||
@@ -12,7 +12,7 @@ export const pb = new PocketBase(basePath)
|
||||
export const isAdmin = () => pb.authStore.record?.role === "admin"
|
||||
export const isReadOnlyUser = () => pb.authStore.record?.role === "readonly"
|
||||
|
||||
export const verifyAuth = () => {
|
||||
const verifyAuth = () => {
|
||||
pb.collection("users")
|
||||
.authRefresh()
|
||||
.catch(() => {
|
||||
@@ -25,6 +25,22 @@ export const verifyAuth = () => {
|
||||
})
|
||||
}
|
||||
|
||||
const verifyAuthDebounced = debounce(verifyAuth, 100)
|
||||
|
||||
// verify the session whenever any API request returns a 4xx response (e.g. an
|
||||
// expired JWT). The auth-refresh endpoint is excluded to avoid a loop, since
|
||||
// it returns 401 itself when the token is no longer valid.
|
||||
pb.afterSend = (response, data) => {
|
||||
if (
|
||||
(response.status === 401 || response.status === 403) &&
|
||||
pb.authStore.token &&
|
||||
!response.url.includes("auth-refresh")
|
||||
) {
|
||||
verifyAuthDebounced()
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
/** Logs the user out by clearing the auth store and unsubscribing from realtime updates. */
|
||||
export function logOut() {
|
||||
$allSystemsByName.set({})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/** biome-ignore-all lint/suspicious/noAssignInExpressions: it's fine :) */
|
||||
import type { PreinitializedMapStore } from "nanostores"
|
||||
import { pb, verifyAuth } from "@/lib/api"
|
||||
import { pb } from "@/lib/api"
|
||||
import {
|
||||
$allSystemsById,
|
||||
$allSystemsByName,
|
||||
@@ -167,11 +167,6 @@ export async function subscribe() {
|
||||
export async function refresh() {
|
||||
try {
|
||||
const records = await fetchSystems()
|
||||
if (!records.length) {
|
||||
// No systems found, verify authentication
|
||||
verifyAuth()
|
||||
return
|
||||
}
|
||||
for (const record of records) {
|
||||
add(record)
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: ar\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-09-02 19:32\n"
|
||||
"PO-Revision-Date: 2026-09-10 00:33\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"
|
||||
@@ -97,7 +97,7 @@ msgstr "5 دقائق"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Actions"
|
||||
msgstr "إجراءات"
|
||||
@@ -196,7 +196,7 @@ msgstr "هل أنت متأكد؟"
|
||||
msgid "Automatic copy requires a secure context."
|
||||
msgstr "النسخ التلقائي يتطلب سياقًا آمنًا."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Disk space available"
|
||||
msgid "Available"
|
||||
msgstr "المتاح"
|
||||
@@ -338,7 +338,7 @@ msgid "Capabilities"
|
||||
msgstr "القدرات"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Capacity"
|
||||
msgstr "السعة"
|
||||
|
||||
@@ -391,14 +391,14 @@ msgstr "تحقق من خدمة المراقبة الخاصة بك"
|
||||
msgid "Check your notification service"
|
||||
msgstr "تحقق من خدمة الإشعارات الخاصة بك"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Checksum errors"
|
||||
msgstr "أخطاء المجموع الاختباري"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Clear"
|
||||
msgstr "مسح"
|
||||
@@ -411,7 +411,7 @@ msgstr "انقر على حاوية لعرض مزيد من المعلومات."
|
||||
msgid "Click on a device to view more information."
|
||||
msgstr "انقر على جهاز لعرض مزيد من المعلومات."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Click on a pool to view vdev and dataset details."
|
||||
msgstr "انقر على مجموعة تخزين لعرض تفاصيل vdev ومجموعة البيانات."
|
||||
|
||||
@@ -503,7 +503,7 @@ msgstr "نسخ الاسم"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Copy public key"
|
||||
msgstr ""
|
||||
msgstr "نسخ المفتاح العام"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
@@ -869,14 +869,14 @@ msgstr "فشل: {0}"
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
msgid "Fans"
|
||||
msgstr ""
|
||||
msgstr "المراوح"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system/chart-card.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Filter..."
|
||||
@@ -898,8 +898,8 @@ msgstr "لمدة <0>{min}</0> {min, plural, one {دقيقة} other {دقائق}}
|
||||
msgid "Forgot password?"
|
||||
msgstr "هل نسيت كلمة المرور؟"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Free space"
|
||||
msgid "Free"
|
||||
msgstr "المساحة الحرة"
|
||||
@@ -948,7 +948,7 @@ msgid "Grid"
|
||||
msgstr "شبكة"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Health"
|
||||
msgstr "الصحة"
|
||||
|
||||
@@ -1146,7 +1146,7 @@ msgstr "استخدام الذاكرة للحاويات"
|
||||
msgid "Model"
|
||||
msgstr "الموديل"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Mountpoint"
|
||||
msgstr "نقطة الربط"
|
||||
|
||||
@@ -1185,7 +1185,7 @@ msgstr "وحدة الشبكة"
|
||||
msgid "No"
|
||||
msgstr "لا"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "No detail data for this pool."
|
||||
msgstr "لا تتوفر بيانات تفصيلية لمجموعة التخزين هذه."
|
||||
|
||||
@@ -1212,7 +1212,7 @@ msgstr "لا توجد سمات S.M.A.R.T. متاحة لهذا الجهاز."
|
||||
msgid "No systems found."
|
||||
msgstr "لم يتم العثور على أنظمة."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "None"
|
||||
msgstr "لا شيء"
|
||||
|
||||
@@ -1255,7 +1255,7 @@ msgstr "كلمة مرور لمرة واحدة"
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Open menu"
|
||||
msgstr "فتح القائمة"
|
||||
@@ -1346,6 +1346,10 @@ msgstr "دائم"
|
||||
msgid "Persistence"
|
||||
msgstr "الاستمرارية"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
msgid "Physical device space. True usable capacity is unknown. Pool disk usage alerts are disabled."
|
||||
msgstr "مساحة الجهاز الفعلية. السعة الفعلية القابلة للاستخدام غير معروفة. تم تعطيل تنبيهات استخدام أقراص المجموعة."
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
msgstr "يرجى <0>تكوين خادم SMTP</0> لضمان تسليم التنبيهات."
|
||||
@@ -1379,11 +1383,11 @@ msgstr "يرجى الاطلاع على <0>التوثيق</0> للحصول على
|
||||
msgid "Please sign in to your account"
|
||||
msgstr "يرجى تسجيل الدخول إلى حسابك"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Pool Health"
|
||||
msgstr "صحة مجموعة التخزين"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Pool Usage"
|
||||
msgstr "استخدام مجموعة التخزين"
|
||||
|
||||
@@ -1416,7 +1420,7 @@ msgstr "تم بدء العملية"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Public key"
|
||||
msgstr ""
|
||||
msgstr "المفتاح العام"
|
||||
|
||||
#. Use 'Key' if your language requires many more characters
|
||||
#: src/components/add-system.tsx
|
||||
@@ -1432,10 +1436,21 @@ msgstr "عمق الدور"
|
||||
msgid "Quiet Hours"
|
||||
msgstr "ساعات الهدوء"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Raw"
|
||||
msgstr "الخام"
|
||||
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Raw usage of storage pool {displayName}"
|
||||
msgstr "الاستخدام الخام لمجموعة التخزين {displayName}"
|
||||
|
||||
#. Disk read
|
||||
#. Disk read
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -1443,7 +1458,7 @@ msgstr "ساعات الهدوء"
|
||||
msgid "Read"
|
||||
msgstr "قراءة"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Read errors"
|
||||
msgstr "أخطاء القراءة"
|
||||
|
||||
@@ -1454,7 +1469,7 @@ msgstr "تم الاستلام"
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Refresh"
|
||||
msgstr "تحديث"
|
||||
|
||||
@@ -1643,7 +1658,7 @@ msgstr "وقت البدء"
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "الحالة"
|
||||
@@ -1671,7 +1686,7 @@ msgstr "استخدام التبديل"
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Switch theme"
|
||||
msgstr ""
|
||||
msgstr "تبديل السمة"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
@@ -1690,7 +1705,7 @@ msgstr "النظام"
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
msgid "System fan speeds (RPM)"
|
||||
msgstr ""
|
||||
msgstr "سرعات مراوح النظام (RPM)"
|
||||
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "System load averages over time"
|
||||
@@ -1772,9 +1787,9 @@ msgstr "سيؤدي هذا إلى حذف جميع السجلات المحددة
|
||||
msgid "Throughput of {extraFsName}"
|
||||
msgstr "معدل نقل {extraFsName}"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Throughput of ZFS pool {poolName}"
|
||||
msgstr "معدل نقل البيانات لمجموعة ZFS {poolName}"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Throughput of storage pool {displayName}"
|
||||
msgstr "معدل نقل مجموعة التخزين {displayName}"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Time format"
|
||||
@@ -1897,6 +1912,7 @@ msgstr "يتم التفعيل عندما يتجاوز استخدام أي قرص
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Type"
|
||||
msgstr "النوع"
|
||||
|
||||
@@ -1919,7 +1935,7 @@ msgid "Universal token"
|
||||
msgstr "رمز مميز عالمي"
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Unknown"
|
||||
msgstr "غير معروفة"
|
||||
@@ -1945,7 +1961,7 @@ msgstr "تحديث"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Updated"
|
||||
msgstr "تم التحديث"
|
||||
@@ -1968,20 +1984,20 @@ msgstr "مدة التشغيل"
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Usage"
|
||||
msgstr "الاستخدام"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Usage of ZFS pool {poolName}"
|
||||
msgstr "استخدام مجموعة ZFS {poolName}"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Usage of storage pool {displayName}"
|
||||
msgstr "استخدام مجموعة التخزين {displayName}"
|
||||
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Used"
|
||||
msgstr "مستخدم"
|
||||
|
||||
@@ -2058,7 +2074,7 @@ msgstr "أمر ويندوز"
|
||||
#. Disk write
|
||||
#. Disk write
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -2066,7 +2082,7 @@ msgstr "أمر ويندوز"
|
||||
msgid "Write"
|
||||
msgstr "كتابة"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Write errors"
|
||||
msgstr "أخطاء الكتابة"
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: bg\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-09-02 21:43\n"
|
||||
"PO-Revision-Date: 2026-09-10 00:33\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Bulgarian\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -97,7 +97,7 @@ msgstr "5 минути"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Actions"
|
||||
msgstr "Действия"
|
||||
@@ -196,7 +196,7 @@ msgstr "Сигурни ли сте?"
|
||||
msgid "Automatic copy requires a secure context."
|
||||
msgstr "Автоматичното копиране изисква защитен контескт."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Disk space available"
|
||||
msgid "Available"
|
||||
msgstr "Налично"
|
||||
@@ -338,7 +338,7 @@ msgid "Capabilities"
|
||||
msgstr "Възможности"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Capacity"
|
||||
msgstr "Капацитет"
|
||||
|
||||
@@ -391,14 +391,14 @@ msgstr "Проверете мониторинг услугата си"
|
||||
msgid "Check your notification service"
|
||||
msgstr "Провери услугата си за удостоверяване"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Checksum errors"
|
||||
msgstr "Грешки в контролната сума"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Clear"
|
||||
msgstr "Изчисти"
|
||||
@@ -411,7 +411,7 @@ msgstr "Кликнете върху контейнер, за да видите
|
||||
msgid "Click on a device to view more information."
|
||||
msgstr "Кликнете върху устройство, за да видите повече информация."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Click on a pool to view vdev and dataset details."
|
||||
msgstr "Щракнете върху пул, за да видите подробности за vdev и наборите от данни."
|
||||
|
||||
@@ -503,7 +503,7 @@ msgstr "Копирай име"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Copy public key"
|
||||
msgstr ""
|
||||
msgstr "Копирай публичния ключ"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
@@ -869,14 +869,14 @@ msgstr "Неуспешни: {0}"
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
msgid "Fans"
|
||||
msgstr ""
|
||||
msgstr "Вентилатори"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system/chart-card.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Filter..."
|
||||
@@ -898,8 +898,8 @@ msgstr "За <0>{min}</0> {min, plural, one {минута} other {минути}}
|
||||
msgid "Forgot password?"
|
||||
msgstr "Забравена парола?"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Free space"
|
||||
msgid "Free"
|
||||
msgstr "Свободно"
|
||||
@@ -928,7 +928,7 @@ msgstr "Глобален"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "GPU"
|
||||
msgstr "GPU"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
msgid "GPU Engines"
|
||||
@@ -948,13 +948,13 @@ msgid "Grid"
|
||||
msgstr "Мрежово"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Health"
|
||||
msgstr "Здраве"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Heartbeat"
|
||||
msgstr "Heartbeat"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat Monitoring"
|
||||
@@ -1146,7 +1146,7 @@ msgstr "Използване на паметта от контейнерите"
|
||||
msgid "Model"
|
||||
msgstr "Модел"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Mountpoint"
|
||||
msgstr "Точка на монтиране"
|
||||
|
||||
@@ -1185,7 +1185,7 @@ msgstr "Единица за измерване на скорост"
|
||||
msgid "No"
|
||||
msgstr "Не"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "No detail data for this pool."
|
||||
msgstr "Няма подробни данни за този пул."
|
||||
|
||||
@@ -1212,7 +1212,7 @@ msgstr "Няма налични S.M.A.R.T. атрибути за това уст
|
||||
msgid "No systems found."
|
||||
msgstr "Няма намерени системи."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "None"
|
||||
msgstr "Няма"
|
||||
|
||||
@@ -1255,7 +1255,7 @@ msgstr "Еднократна парола"
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Open menu"
|
||||
msgstr "Отвори менюто"
|
||||
@@ -1346,6 +1346,10 @@ msgstr "Постоянен"
|
||||
msgid "Persistence"
|
||||
msgstr "Устойчивост"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
msgid "Physical device space. True usable capacity is unknown. Pool disk usage alerts are disabled."
|
||||
msgstr "Физическо пространство на устройството. Действителният използваем капацитет е неизвестен. Сигналите за използване на диска на пула са изключени."
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
msgstr "Моля <0>конфигурурай SMTP сървър</0> за да се подсигуриш, че тревогите са доставени."
|
||||
@@ -1379,11 +1383,11 @@ msgstr "Моля виж <0>документацията</0> за инструк
|
||||
msgid "Please sign in to your account"
|
||||
msgstr "Моля влез в акаунта ти"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Pool Health"
|
||||
msgstr "Състояние на пула"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Pool Usage"
|
||||
msgstr "Използване на пула"
|
||||
|
||||
@@ -1416,7 +1420,7 @@ msgstr "Процесът стартира"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Public key"
|
||||
msgstr ""
|
||||
msgstr "Публичен ключ"
|
||||
|
||||
#. Use 'Key' if your language requires many more characters
|
||||
#: src/components/add-system.tsx
|
||||
@@ -1432,10 +1436,21 @@ msgstr "Дълбочина на опашката"
|
||||
msgid "Quiet Hours"
|
||||
msgstr "Тихи часове"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Raw"
|
||||
msgstr "Сурово"
|
||||
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Raw usage of storage pool {displayName}"
|
||||
msgstr "Сурово използване на пула {displayName}"
|
||||
|
||||
#. Disk read
|
||||
#. Disk read
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -1443,7 +1458,7 @@ msgstr "Тихи часове"
|
||||
msgid "Read"
|
||||
msgstr "Прочети"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Read errors"
|
||||
msgstr "Грешки при четене"
|
||||
|
||||
@@ -1454,7 +1469,7 @@ msgstr "Получени"
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Refresh"
|
||||
msgstr "Опресни"
|
||||
|
||||
@@ -1643,7 +1658,7 @@ msgstr "Начален час"
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "Състояние"
|
||||
@@ -1671,7 +1686,7 @@ msgstr "Използване на swap"
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Switch theme"
|
||||
msgstr ""
|
||||
msgstr "Смени темата"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
@@ -1690,7 +1705,7 @@ msgstr "Система"
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
msgid "System fan speeds (RPM)"
|
||||
msgstr ""
|
||||
msgstr "Скорости на вентилаторите на системата (RPM)"
|
||||
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "System load averages over time"
|
||||
@@ -1772,9 +1787,9 @@ msgstr "Това ще доведе до трайно изтриване на в
|
||||
msgid "Throughput of {extraFsName}"
|
||||
msgstr "Пропускателна способност на {extraFsName}"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Throughput of ZFS pool {poolName}"
|
||||
msgstr "Пропускателна способност на ZFS пул {poolName}"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Throughput of storage pool {displayName}"
|
||||
msgstr "Пропускателна способност на пула {displayName}"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Time format"
|
||||
@@ -1819,7 +1834,7 @@ msgstr "Общо изпратени данни за всеки интерфей
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
msgctxt "Disk I/O"
|
||||
msgid "Total time spent on read/write (can exceed 100%)"
|
||||
msgstr ""
|
||||
msgstr "Общо време, прекарано в четене/запис (може да надвиши 100%)"
|
||||
|
||||
#. placeholder {0}: data.length
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
@@ -1897,6 +1912,7 @@ msgstr "Задейства се, когато употребата на няко
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Type"
|
||||
msgstr "Тип"
|
||||
|
||||
@@ -1919,7 +1935,7 @@ msgid "Universal token"
|
||||
msgstr "Универсален тоукън"
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Unknown"
|
||||
msgstr "Неизвестна"
|
||||
@@ -1945,7 +1961,7 @@ msgstr "Актуализирай"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Updated"
|
||||
msgstr "Актуализирано"
|
||||
@@ -1968,20 +1984,20 @@ msgstr "Време на работа"
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Usage"
|
||||
msgstr "Употреба"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Usage of ZFS pool {poolName}"
|
||||
msgstr "Използване на ZFS пул {poolName}"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Usage of storage pool {displayName}"
|
||||
msgstr "Използване на пула {displayName}"
|
||||
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Used"
|
||||
msgstr "Използвани"
|
||||
|
||||
@@ -2058,7 +2074,7 @@ msgstr "Команда Windows"
|
||||
#. Disk write
|
||||
#. Disk write
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -2066,7 +2082,7 @@ msgstr "Команда Windows"
|
||||
msgid "Write"
|
||||
msgstr "Запиши"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Write errors"
|
||||
msgstr "Грешки при запис"
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: cs\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-09-02 21:43\n"
|
||||
"PO-Revision-Date: 2026-09-10 00:33\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Czech\n"
|
||||
"Plural-Forms: nplurals=4; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 3;\n"
|
||||
@@ -61,7 +61,7 @@ msgstr "1 hodina"
|
||||
#. Load average
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "1 min"
|
||||
msgstr "1 min"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 minute"
|
||||
@@ -78,7 +78,7 @@ msgstr "12 hodin"
|
||||
#. Load average
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "15 min"
|
||||
msgstr "15 min"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "24 hours"
|
||||
@@ -91,13 +91,13 @@ msgstr "30 dní"
|
||||
#. Load average
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "5 min"
|
||||
msgstr "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/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Actions"
|
||||
msgstr "Akce"
|
||||
@@ -154,7 +154,7 @@ msgstr "Po nastavení proměnných prostředí restartujte hub Beszel, aby se zm
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Agent"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
@@ -196,7 +196,7 @@ msgstr "Jste si jistý?"
|
||||
msgid "Automatic copy requires a secure context."
|
||||
msgstr "Automatická kopie vyžaduje zabezpečený kontext."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Disk space available"
|
||||
msgid "Available"
|
||||
msgstr "Dostupné"
|
||||
@@ -338,7 +338,7 @@ msgid "Capabilities"
|
||||
msgstr "Schopnosti"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Capacity"
|
||||
msgstr "Kapacita"
|
||||
|
||||
@@ -391,14 +391,14 @@ msgstr "Zkontrolujte svou monitorovací službu"
|
||||
msgid "Check your notification service"
|
||||
msgstr "Zkontrolujte službu upozornění"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Checksum errors"
|
||||
msgstr "Chyby kontrolního součtu"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Clear"
|
||||
msgstr "Vymazat"
|
||||
@@ -411,7 +411,7 @@ msgstr "Klikněte na kontejner pro zobrazení dalších informací."
|
||||
msgid "Click on a device to view more information."
|
||||
msgstr "Klikněte na zařízení pro zobrazení dalších informací."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Click on a pool to view vdev and dataset details."
|
||||
msgstr "Kliknutím na fond zobrazíte podrobnosti o vdev a datových sadách."
|
||||
|
||||
@@ -503,7 +503,7 @@ msgstr "Kopírovat název"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Copy public key"
|
||||
msgstr ""
|
||||
msgstr "Zkopírovat veřejný klíč"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
@@ -647,7 +647,7 @@ msgstr "Popis"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
msgid "Detail"
|
||||
msgstr "Detail"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Device"
|
||||
@@ -869,14 +869,14 @@ msgstr "Neúspěšné: {0}"
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
msgid "Fans"
|
||||
msgstr ""
|
||||
msgstr "Ventilátory"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system/chart-card.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Filter..."
|
||||
@@ -898,8 +898,8 @@ msgstr "Za <0>{min}</0> {min, plural, one {minutu} few {minuty} other {minut}}"
|
||||
msgid "Forgot password?"
|
||||
msgstr "Zapomněli jste heslo?"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Free space"
|
||||
msgid "Free"
|
||||
msgstr "Volné"
|
||||
@@ -948,7 +948,7 @@ msgid "Grid"
|
||||
msgstr "Mřížka"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Health"
|
||||
msgstr "Zdraví"
|
||||
|
||||
@@ -1146,7 +1146,7 @@ msgstr "Využití paměti kontejnery"
|
||||
msgid "Model"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Mountpoint"
|
||||
msgstr "Bod připojení"
|
||||
|
||||
@@ -1185,7 +1185,7 @@ msgstr "Síťová jednotka"
|
||||
msgid "No"
|
||||
msgstr "Ne"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "No detail data for this pool."
|
||||
msgstr "Pro tento fond nejsou k dispozici podrobné údaje."
|
||||
|
||||
@@ -1212,7 +1212,7 @@ msgstr "Pro toto zařízení nejsou k dispozici žádné atributy S.M.A.R.T."
|
||||
msgid "No systems found."
|
||||
msgstr "Nenalezeny žádné systémy."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "None"
|
||||
msgstr "Žádné"
|
||||
|
||||
@@ -1255,7 +1255,7 @@ msgstr "Jednorázové heslo"
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Open menu"
|
||||
msgstr "Otevřít menu"
|
||||
@@ -1346,6 +1346,10 @@ msgstr "Trvalý"
|
||||
msgid "Persistence"
|
||||
msgstr "Trvalost"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
msgid "Physical device space. True usable capacity is unknown. Pool disk usage alerts are disabled."
|
||||
msgstr "Fyzické místo na zařízení. Skutečná využitelná kapacita není známa. Upozornění na využití disků fondu jsou vypnutá."
|
||||
|
||||
#: 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."
|
||||
@@ -1379,11 +1383,11 @@ msgstr "Instrukce naleznete v <0>dokumentaci</0>."
|
||||
msgid "Please sign in to your account"
|
||||
msgstr "Přihlaste se prosím k vašemu účtu"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Pool Health"
|
||||
msgstr "Stav fondu"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Pool Usage"
|
||||
msgstr "Využití fondu"
|
||||
|
||||
@@ -1416,7 +1420,7 @@ msgstr "Proces spuštěn"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Public key"
|
||||
msgstr ""
|
||||
msgstr "Veřejný klíč"
|
||||
|
||||
#. Use 'Key' if your language requires many more characters
|
||||
#: src/components/add-system.tsx
|
||||
@@ -1432,10 +1436,21 @@ msgstr "Hloubka fronty"
|
||||
msgid "Quiet Hours"
|
||||
msgstr "Tiché hodiny"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Raw"
|
||||
msgstr "Hrubé"
|
||||
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Raw usage of storage pool {displayName}"
|
||||
msgstr "Hrubé využití fondu {displayName}"
|
||||
|
||||
#. Disk read
|
||||
#. Disk read
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -1443,7 +1458,7 @@ msgstr "Tiché hodiny"
|
||||
msgid "Read"
|
||||
msgstr "Číst"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Read errors"
|
||||
msgstr "Chyby čtení"
|
||||
|
||||
@@ -1454,7 +1469,7 @@ msgstr "Přijato"
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Refresh"
|
||||
msgstr "Aktualizovat"
|
||||
|
||||
@@ -1643,7 +1658,7 @@ 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/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "Stav"
|
||||
@@ -1671,7 +1686,7 @@ msgstr "Swap využití"
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Switch theme"
|
||||
msgstr ""
|
||||
msgstr "Přepnout motiv"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
@@ -1690,7 +1705,7 @@ msgstr "Systém"
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
msgid "System fan speeds (RPM)"
|
||||
msgstr ""
|
||||
msgstr "Rychlosti ventilátorů systému (RPM)"
|
||||
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "System load averages over time"
|
||||
@@ -1772,9 +1787,9 @@ msgstr "Tímto trvale odstraníte všechny vybrané záznamy z databáze."
|
||||
msgid "Throughput of {extraFsName}"
|
||||
msgstr "Propustnost {extraFsName}"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Throughput of ZFS pool {poolName}"
|
||||
msgstr "Propustnost fondu ZFS {poolName}"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Throughput of storage pool {displayName}"
|
||||
msgstr "Propustnost fondu {displayName}"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Time format"
|
||||
@@ -1819,7 +1834,7 @@ msgstr "Celkový odeslaný objem dat pro každé rozhraní"
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
msgctxt "Disk I/O"
|
||||
msgid "Total time spent on read/write (can exceed 100%)"
|
||||
msgstr ""
|
||||
msgstr "Celkový čas strávený čtením/zápisem (může přesáhnout 100 %)"
|
||||
|
||||
#. placeholder {0}: data.length
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
@@ -1897,6 +1912,7 @@ 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
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Type"
|
||||
msgstr "Typ"
|
||||
|
||||
@@ -1919,7 +1935,7 @@ msgid "Universal token"
|
||||
msgstr "Univerzální token"
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Unknown"
|
||||
msgstr "Neznámá"
|
||||
@@ -1945,7 +1961,7 @@ msgstr "Aktualizovat"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Updated"
|
||||
msgstr "Aktualizováno"
|
||||
@@ -1968,20 +1984,20 @@ msgstr "Doba provozu"
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Usage"
|
||||
msgstr "Využití"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Usage of ZFS pool {poolName}"
|
||||
msgstr "Využití fondu ZFS {poolName}"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Usage of storage pool {displayName}"
|
||||
msgstr "Využití fondu {displayName}"
|
||||
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Used"
|
||||
msgstr "Využito"
|
||||
|
||||
@@ -2058,7 +2074,7 @@ msgstr "Windows příkaz"
|
||||
#. Disk write
|
||||
#. Disk write
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -2066,7 +2082,7 @@ msgstr "Windows příkaz"
|
||||
msgid "Write"
|
||||
msgstr "Psát"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Write errors"
|
||||
msgstr "Chyby zápisu"
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: da\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-09-02 21:43\n"
|
||||
"PO-Revision-Date: 2026-09-10 00:33\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Danish\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -48,7 +48,7 @@ msgstr "{count, plural, one {{countString} minut} other {{countString} minutter}
|
||||
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
msgid "{diskName} I/O"
|
||||
msgstr "{diskName} I/O"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "{threads, plural, one {# thread} other {# threads}}"
|
||||
@@ -97,7 +97,7 @@ msgstr "5 minutter"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Actions"
|
||||
msgstr "Handlinger"
|
||||
@@ -154,7 +154,7 @@ msgstr "Efter indstilling af miljøvariablerne skal du genstarte din Beszel-hub
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Agent"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
@@ -196,7 +196,7 @@ msgstr "Er du sikker?"
|
||||
msgid "Automatic copy requires a secure context."
|
||||
msgstr "Automatisk kopiering kræver en sikker kontekst."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Disk space available"
|
||||
msgid "Available"
|
||||
msgstr "Tilgængelig"
|
||||
@@ -300,7 +300,7 @@ msgstr "Binær"
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bits (Kbps, Mbps, Gbps)"
|
||||
msgstr "Bits (Kbps, Mbps, Gbps)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Boot state"
|
||||
@@ -309,7 +309,7 @@ msgstr "Opstartstilstand"
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bytes (KB/s, MB/s, GB/s)"
|
||||
msgstr "Bytes (KB/s, MB/s, GB/s)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
msgid "Cache / Buffers"
|
||||
@@ -338,7 +338,7 @@ msgid "Capabilities"
|
||||
msgstr "Funktioner"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Capacity"
|
||||
msgstr "Kapacitet"
|
||||
|
||||
@@ -348,7 +348,7 @@ msgstr "Forsigtig - muligt tab af data"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Celsius (°C)"
|
||||
msgstr "Celsius (°C)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change display units for metrics."
|
||||
@@ -391,14 +391,14 @@ msgstr "Tjek din overvågningstjeneste"
|
||||
msgid "Check your notification service"
|
||||
msgstr "Tjek din notifikationstjeneste"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Checksum errors"
|
||||
msgstr "Kontrolsumfejl"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Clear"
|
||||
msgstr "Ryd"
|
||||
@@ -411,7 +411,7 @@ msgstr "Klik på en container for at se mere information."
|
||||
msgid "Click on a device to view more information."
|
||||
msgstr "Klik på en enhed for at se flere oplysninger."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Click on a pool to view vdev and dataset details."
|
||||
msgstr "Klik på en pool for at se detaljer om vdev og datasæt."
|
||||
|
||||
@@ -447,7 +447,7 @@ msgstr "Forbindelsen er nede"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Container"
|
||||
msgstr "Container"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Container Health"
|
||||
@@ -503,7 +503,7 @@ msgstr "Kopier navn"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Copy public key"
|
||||
msgstr ""
|
||||
msgstr "Kopiér offentlig nøgle"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
@@ -530,7 +530,7 @@ msgstr "Kerne"
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "CPU"
|
||||
msgstr "CPU"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "CPU Cores"
|
||||
@@ -542,7 +542,7 @@ msgstr "CPU-I/O-ventetid (IOWait)"
|
||||
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "CPU Peak"
|
||||
msgstr "CPU Peak"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "CPU Steal Time"
|
||||
@@ -661,7 +661,7 @@ msgstr "Aflader"
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Disk"
|
||||
msgstr "Disk"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Disk unit"
|
||||
@@ -732,7 +732,7 @@ msgstr "Rediger {foo}"
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
#: src/components/login/otp-forms.tsx
|
||||
msgid "Email"
|
||||
msgstr "Email"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Email notifications"
|
||||
@@ -826,7 +826,7 @@ msgstr "Eksporter din nuværende systemkonfiguration."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Fahrenheit (°F)"
|
||||
msgstr "Fahrenheit (°F)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Failed"
|
||||
@@ -869,18 +869,18 @@ msgstr "Mislykkedes: {0}"
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
msgid "Fans"
|
||||
msgstr ""
|
||||
msgstr "Blæsere"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system/chart-card.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Filter..."
|
||||
msgstr "Filter..."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Fingerprint"
|
||||
@@ -888,7 +888,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}}"
|
||||
@@ -898,8 +898,8 @@ msgstr "For <0>{min}</0> {min, plural, one {minut} other {minutter}}"
|
||||
msgid "Forgot password?"
|
||||
msgstr "Glemt adgangskode?"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Free space"
|
||||
msgid "Free"
|
||||
msgstr "Ledig"
|
||||
@@ -924,7 +924,7 @@ msgstr "Generelt"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Global"
|
||||
msgstr "Global"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "GPU"
|
||||
@@ -948,7 +948,7 @@ msgid "Grid"
|
||||
msgstr "Gitter"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Health"
|
||||
msgstr "Sundhed"
|
||||
|
||||
@@ -1091,7 +1091,7 @@ msgstr "Loginforsøg mislykkedes"
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Logs"
|
||||
msgstr "Logs"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Looking instead for where to create alerts? Click the bell <0/> icons in the systems table."
|
||||
@@ -1144,9 +1144,9 @@ msgstr "Containeres hukommelsesforbrug"
|
||||
#. Device model
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Model"
|
||||
msgstr "Model"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Mountpoint"
|
||||
msgstr "Monteringspunkt"
|
||||
|
||||
@@ -1161,7 +1161,7 @@ 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/charts/network-charts.tsx
|
||||
msgid "Network traffic of containers"
|
||||
@@ -1185,7 +1185,7 @@ msgstr "Netværksenhed"
|
||||
msgid "No"
|
||||
msgstr "Nej"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "No detail data for this pool."
|
||||
msgstr "Ingen detaljerede data for denne pool."
|
||||
|
||||
@@ -1212,7 +1212,7 @@ msgstr "Ingen S.M.A.R.T.-attributter tilgængelige for denne enhed."
|
||||
msgid "No systems found."
|
||||
msgstr "Ingen systemer fundet."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "None"
|
||||
msgstr "Ingen"
|
||||
|
||||
@@ -1255,7 +1255,7 @@ msgstr "Engangsadgangskode"
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Open menu"
|
||||
msgstr "Åbn menu"
|
||||
@@ -1311,7 +1311,7 @@ msgstr "Tidligere"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Pause"
|
||||
msgstr "Pause"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Paused"
|
||||
@@ -1346,6 +1346,10 @@ msgstr ""
|
||||
msgid "Persistence"
|
||||
msgstr "Vedholdenhed"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
msgid "Physical device space. True usable capacity is unknown. Pool disk usage alerts are disabled."
|
||||
msgstr "Fysisk enhedsplads. Den reelle brugbare kapacitet er ukendt. Advarsler om diskforbrug for poolen er deaktiveret."
|
||||
|
||||
#: 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."
|
||||
@@ -1379,17 +1383,17 @@ msgstr "Se <0>dokumentationen</0> for instruktioner."
|
||||
msgid "Please sign in to your account"
|
||||
msgstr "Log venligst ind på din konto"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Pool Health"
|
||||
msgstr "Poolstatus"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Pool Usage"
|
||||
msgstr "Poolforbrug"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Port"
|
||||
msgstr "Port"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
msgctxt "Container ports"
|
||||
@@ -1416,7 +1420,7 @@ msgstr "Proces startet"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Public key"
|
||||
msgstr ""
|
||||
msgstr "Offentlig nøgle"
|
||||
|
||||
#. Use 'Key' if your language requires many more characters
|
||||
#: src/components/add-system.tsx
|
||||
@@ -1432,10 +1436,21 @@ msgstr "Kødybde"
|
||||
msgid "Quiet Hours"
|
||||
msgstr "Stille timer"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Raw"
|
||||
msgstr "Rå"
|
||||
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Raw usage of storage pool {displayName}"
|
||||
msgstr "Råt forbrug af lagerpool {displayName}"
|
||||
|
||||
#. Disk read
|
||||
#. Disk read
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -1443,7 +1458,7 @@ msgstr "Stille timer"
|
||||
msgid "Read"
|
||||
msgstr "Læs"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Read errors"
|
||||
msgstr "Læsefejl"
|
||||
|
||||
@@ -1454,7 +1469,7 @@ msgstr "Modtaget"
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Refresh"
|
||||
msgstr "Opdater"
|
||||
|
||||
@@ -1501,7 +1516,7 @@ msgstr "Genoptag"
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgctxt "Root disk label"
|
||||
msgid "Root"
|
||||
msgstr "Root"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Rotate token"
|
||||
@@ -1643,7 +1658,7 @@ msgstr "Starttid"
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "Tilstand"
|
||||
@@ -1654,7 +1669,7 @@ msgstr "Tilstand"
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Status"
|
||||
msgstr "Status"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Sub State"
|
||||
@@ -1671,7 +1686,7 @@ msgstr "Swap forbrug"
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Switch theme"
|
||||
msgstr ""
|
||||
msgstr "Skift tema"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
@@ -1686,11 +1701,11 @@ msgstr ""
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "System"
|
||||
msgstr "System"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
msgid "System fan speeds (RPM)"
|
||||
msgstr ""
|
||||
msgstr "Systemblæserhastigheder (RPM)"
|
||||
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "System load averages over time"
|
||||
@@ -1698,7 +1713,7 @@ msgstr "Gennemsnitlig system belastning over tid"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Systemd Services"
|
||||
msgstr "Systemd Services"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Systems"
|
||||
@@ -1742,7 +1757,7 @@ msgstr "Temperaturer i systemsensorer"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Test <0>URL</0>"
|
||||
msgstr "Test <0>URL</0>"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Test heartbeat"
|
||||
@@ -1772,9 +1787,9 @@ msgstr "Dette vil permanent slette alle poster fra databasen."
|
||||
msgid "Throughput of {extraFsName}"
|
||||
msgstr "Gennemløb af {extraFsName}"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Throughput of ZFS pool {poolName}"
|
||||
msgstr "Gennemløb for ZFS-pool {poolName}"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Throughput of storage pool {displayName}"
|
||||
msgstr "Gennemløb af lagerpool {displayName}"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Time format"
|
||||
@@ -1819,7 +1834,7 @@ msgstr "Samlet sendt data for hver interface"
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
msgctxt "Disk I/O"
|
||||
msgid "Total time spent on read/write (can exceed 100%)"
|
||||
msgstr ""
|
||||
msgstr "Samlet tid brugt på læsning/skrivning (kan overstige 100 %)"
|
||||
|
||||
#. placeholder {0}: data.length
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
@@ -1897,8 +1912,9 @@ 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
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Type"
|
||||
msgstr "Type"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Unhealthy"
|
||||
@@ -1919,7 +1935,7 @@ msgid "Universal token"
|
||||
msgstr "Universalnøgle"
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Unknown"
|
||||
msgstr "Ukendt"
|
||||
@@ -1945,7 +1961,7 @@ msgstr "Opdater"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Updated"
|
||||
msgstr "Opdateret"
|
||||
@@ -1968,20 +1984,20 @@ msgstr "Oppetid"
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Usage"
|
||||
msgstr "Forbrug"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Usage of ZFS pool {poolName}"
|
||||
msgstr "Forbrug af ZFS-pool {poolName}"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Usage of storage pool {displayName}"
|
||||
msgstr "Forbrug af lagerpool {displayName}"
|
||||
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Used"
|
||||
msgstr "Brugt"
|
||||
|
||||
@@ -2058,7 +2074,7 @@ msgstr "Windows-kommando"
|
||||
#. Disk write
|
||||
#. Disk write
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -2066,7 +2082,7 @@ msgstr "Windows-kommando"
|
||||
msgid "Write"
|
||||
msgstr "Skriv"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Write errors"
|
||||
msgstr "Skrivefejl"
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: de\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-09-02 20:36\n"
|
||||
"PO-Revision-Date: 2026-09-10 00:33\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: German\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -97,7 +97,7 @@ msgstr "5 Min"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Actions"
|
||||
msgstr "Aktionen"
|
||||
@@ -142,7 +142,7 @@ msgstr "Breite des Hauptlayouts anpassen"
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Admin"
|
||||
msgstr "Admin"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "After"
|
||||
@@ -154,7 +154,7 @@ msgstr "Starten Sie nach dem Festlegen der Umgebungsvariablen Ihren Beszel-Hub n
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Agent"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
@@ -196,7 +196,7 @@ msgstr "Bist du sicher?"
|
||||
msgid "Automatic copy requires a secure context."
|
||||
msgstr "Automatisches Kopieren erfordert einen sicheren Kontext."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Disk space available"
|
||||
msgid "Available"
|
||||
msgstr "Verfügbar"
|
||||
@@ -248,7 +248,7 @@ msgstr "Durchschnittliche Auslastung der GPU-Engines"
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Backups"
|
||||
msgstr "Backups"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/charts/network-charts.tsx
|
||||
#: src/lib/alerts.ts
|
||||
@@ -300,7 +300,7 @@ msgstr "Binär"
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bits (Kbps, Mbps, Gbps)"
|
||||
msgstr "Bits (Kbps, Mbps, Gbps)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Boot state"
|
||||
@@ -309,7 +309,7 @@ msgstr "Boot-Zustand"
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bytes (KB/s, MB/s, GB/s)"
|
||||
msgstr "Bytes (KB/s, MB/s, GB/s)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
msgid "Cache / Buffers"
|
||||
@@ -338,7 +338,7 @@ msgid "Capabilities"
|
||||
msgstr "Fähigkeiten"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Capacity"
|
||||
msgstr "Kapazität"
|
||||
|
||||
@@ -348,7 +348,7 @@ msgstr "Vorsicht - potenzieller Datenverlust"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Celsius (°C)"
|
||||
msgstr "Celsius (°C)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change display units for metrics."
|
||||
@@ -391,14 +391,14 @@ msgstr "Überprüfen Sie Ihren Überwachungsdienst"
|
||||
msgid "Check your notification service"
|
||||
msgstr "Überprüfe deinen Benachrichtigungsdienst"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Checksum errors"
|
||||
msgstr "Prüfsummenfehler"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Clear"
|
||||
msgstr "Löschen"
|
||||
@@ -411,7 +411,7 @@ msgstr "Klicke auf einen Container, um weitere Informationen zu sehen."
|
||||
msgid "Click on a device to view more information."
|
||||
msgstr "Klicke auf ein Gerät, um weitere Informationen zu sehen."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Click on a pool to view vdev and dataset details."
|
||||
msgstr "Klicke auf einen Pool, um Details zu vdevs und Datensätzen anzuzeigen."
|
||||
|
||||
@@ -447,7 +447,7 @@ msgstr "Verbindung unterbrochen"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Container"
|
||||
msgstr "Container"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Container Health"
|
||||
@@ -530,7 +530,7 @@ msgstr "Kern"
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "CPU"
|
||||
msgstr "CPU"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "CPU Cores"
|
||||
@@ -826,7 +826,7 @@ msgstr "Exportiere die aktuelle Systemkonfiguration."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Fahrenheit (°F)"
|
||||
msgstr "Fahrenheit (°F)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Failed"
|
||||
@@ -875,12 +875,12 @@ msgstr "Lüfter"
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system/chart-card.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Filter..."
|
||||
msgstr "Filter..."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Fingerprint"
|
||||
@@ -898,8 +898,8 @@ msgstr "Für <0>{min}</0> {min, plural, one {Minute} other {Minuten}}"
|
||||
msgid "Forgot password?"
|
||||
msgstr "Passwort vergessen?"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Free space"
|
||||
msgid "Free"
|
||||
msgstr "Frei"
|
||||
@@ -924,7 +924,7 @@ msgstr "Allgemein"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Global"
|
||||
msgstr "Global"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "GPU"
|
||||
@@ -948,13 +948,13 @@ msgid "Grid"
|
||||
msgstr "Raster"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Health"
|
||||
msgstr "Gesundheit"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Heartbeat"
|
||||
msgstr "Heartbeat"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat Monitoring"
|
||||
@@ -972,7 +972,7 @@ msgstr "Homebrew-Befehl"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Host / IP"
|
||||
msgstr "Host / IP"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP Method"
|
||||
@@ -1009,7 +1009,7 @@ msgstr "Wenn du das Passwort für dein Administratorkonto verloren hast, kannst
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
msgctxt "Docker image"
|
||||
msgid "Image"
|
||||
msgstr "Image"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Inactive"
|
||||
@@ -1146,7 +1146,7 @@ msgstr "Speichernutzung der Container"
|
||||
msgid "Model"
|
||||
msgstr "Modell"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Mountpoint"
|
||||
msgstr "Einhängepunkt"
|
||||
|
||||
@@ -1156,7 +1156,7 @@ msgstr "Einhängepunkt"
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Name"
|
||||
msgstr "Name"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
@@ -1185,7 +1185,7 @@ msgstr "Netzwerkeinheit"
|
||||
msgid "No"
|
||||
msgstr "Nein"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "No detail data for this pool."
|
||||
msgstr "Für diesen Pool sind keine Detaildaten verfügbar."
|
||||
|
||||
@@ -1212,7 +1212,7 @@ msgstr "Für dieses Gerät sind keine S.M.A.R.T.-Attribute verfügbar."
|
||||
msgid "No systems found."
|
||||
msgstr "Keine Systeme gefunden."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "None"
|
||||
msgstr "Keine"
|
||||
|
||||
@@ -1255,7 +1255,7 @@ msgstr "Einmalpasswort"
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Open menu"
|
||||
msgstr "Menü öffnen"
|
||||
@@ -1311,7 +1311,7 @@ msgstr "Vergangen"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Pause"
|
||||
msgstr "Pause"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Paused"
|
||||
@@ -1346,6 +1346,10 @@ msgstr "Dauerhaft"
|
||||
msgid "Persistence"
|
||||
msgstr "Persistenz"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
msgid "Physical device space. True usable capacity is unknown. Pool disk usage alerts are disabled."
|
||||
msgstr "Physischer Gerätespeicherplatz. Die tatsächlich nutzbare Kapazität ist unbekannt. Warnungen zur Pool-Festplattennutzung sind deaktiviert."
|
||||
|
||||
#: 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."
|
||||
@@ -1379,22 +1383,22 @@ msgstr "In der <0>Dokumentation</0> findest du weitere Anweisungen."
|
||||
msgid "Please sign in to your account"
|
||||
msgstr "Bitte melde dich bei deinem Konto an"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Pool Health"
|
||||
msgstr "Pool-Zustand"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Pool Usage"
|
||||
msgstr "Pool-Auslastung"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Port"
|
||||
msgstr "Port"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
msgctxt "Container ports"
|
||||
msgid "Ports"
|
||||
msgstr "Ports"
|
||||
msgstr ""
|
||||
|
||||
#. Power On Time
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
@@ -1432,10 +1436,21 @@ msgstr "Warteschlangentiefe"
|
||||
msgid "Quiet Hours"
|
||||
msgstr "Ruhezeiten"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Raw"
|
||||
msgstr "Roh"
|
||||
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Raw usage of storage pool {displayName}"
|
||||
msgstr "Rohauslastung des Pools {displayName}"
|
||||
|
||||
#. Disk read
|
||||
#. Disk read
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -1443,7 +1458,7 @@ msgstr "Ruhezeiten"
|
||||
msgid "Read"
|
||||
msgstr "Lesen"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Read errors"
|
||||
msgstr "Lesefehler"
|
||||
|
||||
@@ -1454,7 +1469,7 @@ msgstr "Empfangen"
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Refresh"
|
||||
msgstr "Aktualisieren"
|
||||
|
||||
@@ -1501,7 +1516,7 @@ msgstr "Fortsetzen"
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgctxt "Root disk label"
|
||||
msgid "Root"
|
||||
msgstr "Root"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Rotate token"
|
||||
@@ -1643,7 +1658,7 @@ msgstr "Startzeit"
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "Status"
|
||||
@@ -1654,7 +1669,7 @@ msgstr "Status"
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Status"
|
||||
msgstr "Status"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Sub State"
|
||||
@@ -1686,11 +1701,11 @@ msgstr "Design wechseln"
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "System"
|
||||
msgstr "System"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
msgid "System fan speeds (RPM)"
|
||||
msgstr ""
|
||||
msgstr "Systemlüftergeschwindigkeiten (RPM)"
|
||||
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "System load averages over time"
|
||||
@@ -1715,7 +1730,7 @@ msgstr "Tabelle"
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgctxt "Tabs system layout option"
|
||||
msgid "Tabs"
|
||||
msgstr "Tabs"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Tasks"
|
||||
@@ -1742,7 +1757,7 @@ msgstr "Temperaturen der Systemsensoren"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Test <0>URL</0>"
|
||||
msgstr "Test <0>URL</0>"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Test heartbeat"
|
||||
@@ -1772,9 +1787,9 @@ msgstr "Dadurch werden alle ausgewählten Datensätze dauerhaft aus der Datenban
|
||||
msgid "Throughput of {extraFsName}"
|
||||
msgstr "Durchsatz von {extraFsName}"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Throughput of ZFS pool {poolName}"
|
||||
msgstr "Durchsatz des ZFS-Pools {poolName}"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Throughput of storage pool {displayName}"
|
||||
msgstr "Durchsatz des Pools {displayName}"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Time format"
|
||||
@@ -1787,7 +1802,7 @@ msgstr "An E-Mail(s)"
|
||||
#: 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
|
||||
@@ -1897,6 +1912,7 @@ msgstr "Löst aus, wenn die Nutzung einer Festplatte einen Schwellenwert übersc
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Type"
|
||||
msgstr "Typ"
|
||||
|
||||
@@ -1919,7 +1935,7 @@ msgid "Universal token"
|
||||
msgstr "Universeller Token"
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Unknown"
|
||||
msgstr "Unbekannt"
|
||||
@@ -1945,7 +1961,7 @@ msgstr "Aktualisieren"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Updated"
|
||||
msgstr "Aktualisiert"
|
||||
@@ -1968,20 +1984,20 @@ msgstr "Betriebszeit"
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Usage"
|
||||
msgstr "Nutzung"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Usage of ZFS pool {poolName}"
|
||||
msgstr "Auslastung des ZFS-Pools {poolName}"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Usage of storage pool {displayName}"
|
||||
msgstr "Auslastung des Pools {displayName}"
|
||||
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Used"
|
||||
msgstr "Verwendet"
|
||||
|
||||
@@ -2058,7 +2074,7 @@ msgstr "Windows-Befehl"
|
||||
#. Disk write
|
||||
#. Disk write
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -2066,7 +2082,7 @@ msgstr "Windows-Befehl"
|
||||
msgid "Write"
|
||||
msgstr "Schreiben"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Write errors"
|
||||
msgstr "Schreibfehler"
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: el\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-09-02 21:43\n"
|
||||
"PO-Revision-Date: 2026-09-10 00:33\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Greek\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -52,7 +52,7 @@ msgstr "I/O {diskName}"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "{threads, plural, one {# thread} other {# threads}}"
|
||||
msgstr "{threads, plural, one {# thread} other {# threads}}"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 hour"
|
||||
@@ -97,7 +97,7 @@ msgstr "5 λ"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Actions"
|
||||
msgstr "Ενέργειες"
|
||||
@@ -196,7 +196,7 @@ msgstr "Είστε βέβαιοι;"
|
||||
msgid "Automatic copy requires a secure context."
|
||||
msgstr "Η αυτόματη αντιγραφή απαιτεί ασφαλές περιβάλλον."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Disk space available"
|
||||
msgid "Available"
|
||||
msgstr "Διαθέσιμο"
|
||||
@@ -338,7 +338,7 @@ msgid "Capabilities"
|
||||
msgstr "Δυνατότητες"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Capacity"
|
||||
msgstr "Χωρητικότητα"
|
||||
|
||||
@@ -391,14 +391,14 @@ msgstr "Ελέγξτε την υπηρεσία παρακολούθησής σα
|
||||
msgid "Check your notification service"
|
||||
msgstr "Ελέγξτε την υπηρεσία ειδοποιήσεών σας"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Checksum errors"
|
||||
msgstr "Σφάλματα αθροίσματος ελέγχου"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Clear"
|
||||
msgstr "Εκκαθάριση"
|
||||
@@ -411,7 +411,7 @@ msgstr "Κάντε κλικ σε ένα κοντέινερ για να δείτ
|
||||
msgid "Click on a device to view more information."
|
||||
msgstr "Κάντε κλικ σε μια συσκευή για να δείτε περισσότερες πληροφορίες."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Click on a pool to view vdev and dataset details."
|
||||
msgstr "Κάντε κλικ σε ένα pool για να δείτε λεπτομέρειες για τα vdev και τα σύνολα δεδομένων."
|
||||
|
||||
@@ -447,11 +447,11 @@ msgstr "Η σύνδεση είναι εκτός λειτουργίας"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Container"
|
||||
msgstr ""
|
||||
msgstr "Κοντέινερ"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Container Health"
|
||||
msgstr ""
|
||||
msgstr "Υγεία κοντέινερ"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Containers"
|
||||
@@ -530,7 +530,7 @@ msgstr "Βασικά"
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "CPU"
|
||||
msgstr "CPU"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "CPU Cores"
|
||||
@@ -732,7 +732,7 @@ msgstr "Επεξεργασία {foo}"
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
#: src/components/login/otp-forms.tsx
|
||||
msgid "Email"
|
||||
msgstr "Email"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Email notifications"
|
||||
@@ -875,8 +875,8 @@ msgstr "Ανεμιστήρες"
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system/chart-card.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Filter..."
|
||||
@@ -898,8 +898,8 @@ msgstr "Για <0>{min}</0> {min, plural, one {λεπτό} other {λεπτά}}"
|
||||
msgid "Forgot password?"
|
||||
msgstr "Ξεχάσατε τον κωδικό πρόσβασης;"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Free space"
|
||||
msgid "Free"
|
||||
msgstr "Ελεύθερο"
|
||||
@@ -928,7 +928,7 @@ msgstr "Καθολικό"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "GPU"
|
||||
msgstr "GPU"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
msgid "GPU Engines"
|
||||
@@ -948,13 +948,13 @@ msgid "Grid"
|
||||
msgstr "Πλέγμα"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Health"
|
||||
msgstr "Υγεία"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Heartbeat"
|
||||
msgstr "Heartbeat"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat Monitoring"
|
||||
@@ -1146,7 +1146,7 @@ msgstr "Χρήση μνήμης των κοντέινερ"
|
||||
msgid "Model"
|
||||
msgstr "Μοντέλο"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Mountpoint"
|
||||
msgstr "Σημείο προσάρτησης"
|
||||
|
||||
@@ -1185,7 +1185,7 @@ msgstr "Μονάδα δικτύου"
|
||||
msgid "No"
|
||||
msgstr "Όχι"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "No detail data for this pool."
|
||||
msgstr "Δεν υπάρχουν λεπτομερή δεδομένα για αυτό το pool."
|
||||
|
||||
@@ -1212,7 +1212,7 @@ msgstr "Δεν υπάρχουν διαθέσιμα χαρακτηριστικά
|
||||
msgid "No systems found."
|
||||
msgstr "Δεν βρέθηκαν συστήματα."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "None"
|
||||
msgstr "Κανένα"
|
||||
|
||||
@@ -1255,7 +1255,7 @@ msgstr "Κωδικός μίας χρήσης"
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Open menu"
|
||||
msgstr "Άνοιγμα μενού"
|
||||
@@ -1346,6 +1346,10 @@ msgstr "Μόνιμο"
|
||||
msgid "Persistence"
|
||||
msgstr "Διατήρηση"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
msgid "Physical device space. True usable capacity is unknown. Pool disk usage alerts are disabled."
|
||||
msgstr "Φυσικός χώρος συσκευής. Η πραγματική ωφέλιμη χωρητικότητα είναι άγνωστη. Οι ειδοποιήσεις χρήσης δίσκων του pool είναι απενεργοποιημένες."
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
msgstr "Παρακαλώ <0>ρυθμίστε έναν διακομιστή SMTP</0> για να διασφαλίσετε την παράδοση των ειδοποιήσεων."
|
||||
@@ -1379,11 +1383,11 @@ msgstr "Ανατρέξτε στην <0>τεκμηρίωση</0> για οδηγ
|
||||
msgid "Please sign in to your account"
|
||||
msgstr "Συνδεθείτε στον λογαριασμό σας"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Pool Health"
|
||||
msgstr "Κατάσταση pool"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Pool Usage"
|
||||
msgstr "Χρήση pool"
|
||||
|
||||
@@ -1432,10 +1436,21 @@ msgstr "Βάθος ουράς"
|
||||
msgid "Quiet Hours"
|
||||
msgstr "Ώρες σίγασης"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Raw"
|
||||
msgstr "Ακατέργαστο"
|
||||
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Raw usage of storage pool {displayName}"
|
||||
msgstr "Ακατέργαστη χρήση του pool αποθήκευσης {displayName}"
|
||||
|
||||
#. Disk read
|
||||
#. Disk read
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -1443,7 +1458,7 @@ msgstr "Ώρες σίγασης"
|
||||
msgid "Read"
|
||||
msgstr "Ανάγνωση"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Read errors"
|
||||
msgstr "Σφάλματα ανάγνωσης"
|
||||
|
||||
@@ -1454,7 +1469,7 @@ msgstr "Λήφθηκαν"
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Refresh"
|
||||
msgstr "Ανανέωση"
|
||||
|
||||
@@ -1643,7 +1658,7 @@ msgstr "Ώρα έναρξης"
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "Κατάσταση"
|
||||
@@ -1772,9 +1787,9 @@ msgstr "Αυτό θα διαγράψει οριστικά όλες τις επι
|
||||
msgid "Throughput of {extraFsName}"
|
||||
msgstr "Ρυθμός μεταφοράς του {extraFsName}"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Throughput of ZFS pool {poolName}"
|
||||
msgstr "Ρυθμός διαμεταγωγής του ZFS pool {poolName}"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Throughput of storage pool {displayName}"
|
||||
msgstr "Ρυθμός μεταφοράς του pool αποθήκευσης {displayName}"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Time format"
|
||||
@@ -1897,12 +1912,13 @@ msgstr "Ενεργοποιείται όταν η χρήση οποιουδήπο
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Type"
|
||||
msgstr "Τύπος"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Unhealthy"
|
||||
msgstr ""
|
||||
msgstr "Μη υγιές"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Unit file"
|
||||
@@ -1919,7 +1935,7 @@ msgid "Universal token"
|
||||
msgstr "Καθολικό διακριτικό"
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Unknown"
|
||||
msgstr "Άγνωστο"
|
||||
@@ -1945,7 +1961,7 @@ msgstr "Ενημέρωση"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Updated"
|
||||
msgstr "Ενημερώθηκε"
|
||||
@@ -1968,20 +1984,20 @@ msgstr "Χρόνος λειτουργίας"
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Usage"
|
||||
msgstr "Χρήση"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Usage of ZFS pool {poolName}"
|
||||
msgstr "Χρήση του ZFS pool {poolName}"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Usage of storage pool {displayName}"
|
||||
msgstr "Χρήση του pool αποθήκευσης {displayName}"
|
||||
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Used"
|
||||
msgstr "Χρησιμοποιείται"
|
||||
|
||||
@@ -2058,7 +2074,7 @@ msgstr "Εντολή Windows"
|
||||
#. Disk write
|
||||
#. Disk write
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -2066,7 +2082,7 @@ msgstr "Εντολή Windows"
|
||||
msgid "Write"
|
||||
msgstr "Εγγραφή"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Write errors"
|
||||
msgstr "Σφάλματα εγγραφής"
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ msgstr "5 min"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Actions"
|
||||
msgstr "Actions"
|
||||
@@ -191,7 +191,7 @@ msgstr "Are you sure?"
|
||||
msgid "Automatic copy requires a secure context."
|
||||
msgstr "Automatic copy requires a secure context."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Disk space available"
|
||||
msgid "Available"
|
||||
msgstr "Available"
|
||||
@@ -333,7 +333,7 @@ msgid "Capabilities"
|
||||
msgstr "Capabilities"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Capacity"
|
||||
msgstr "Capacity"
|
||||
|
||||
@@ -386,14 +386,14 @@ msgstr "Check your monitoring service"
|
||||
msgid "Check your notification service"
|
||||
msgstr "Check your notification service"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Checksum errors"
|
||||
msgstr "Checksum errors"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Clear"
|
||||
msgstr "Clear"
|
||||
@@ -406,7 +406,7 @@ msgstr "Click on a container to view more information."
|
||||
msgid "Click on a device to view more information."
|
||||
msgstr "Click on a device to view more information."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Click on a pool to view vdev and dataset details."
|
||||
msgstr "Click on a pool to view vdev and dataset details."
|
||||
|
||||
@@ -870,8 +870,8 @@ msgstr "Fans"
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system/chart-card.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Filter..."
|
||||
@@ -893,8 +893,8 @@ msgstr "For <0>{min}</0> {min, plural, one {minute} other {minutes}}"
|
||||
msgid "Forgot password?"
|
||||
msgstr "Forgot password?"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Free space"
|
||||
msgid "Free"
|
||||
msgstr "Free"
|
||||
@@ -943,7 +943,7 @@ msgid "Grid"
|
||||
msgstr "Grid"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Health"
|
||||
msgstr "Health"
|
||||
|
||||
@@ -1141,7 +1141,7 @@ msgstr "Memory usage of containers"
|
||||
msgid "Model"
|
||||
msgstr "Model"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Mountpoint"
|
||||
msgstr "Mountpoint"
|
||||
|
||||
@@ -1180,7 +1180,7 @@ msgstr "Network unit"
|
||||
msgid "No"
|
||||
msgstr "No"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "No detail data for this pool."
|
||||
msgstr "No detail data for this pool."
|
||||
|
||||
@@ -1207,7 +1207,7 @@ msgstr "No S.M.A.R.T. attributes available for this device."
|
||||
msgid "No systems found."
|
||||
msgstr "No systems found."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "None"
|
||||
msgstr "None"
|
||||
|
||||
@@ -1250,7 +1250,7 @@ msgstr "One-time password"
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Open menu"
|
||||
msgstr "Open menu"
|
||||
@@ -1341,6 +1341,10 @@ msgstr "Permanent"
|
||||
msgid "Persistence"
|
||||
msgstr "Persistence"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
msgid "Physical device space. True usable capacity is unknown. Pool disk usage alerts are disabled."
|
||||
msgstr "Physical device space. True usable capacity is unknown. Pool disk usage alerts are disabled."
|
||||
|
||||
#: 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."
|
||||
@@ -1374,11 +1378,11 @@ msgstr "Please see <0>the documentation</0> for instructions."
|
||||
msgid "Please sign in to your account"
|
||||
msgstr "Please sign in to your account"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Pool Health"
|
||||
msgstr "Pool Health"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Pool Usage"
|
||||
msgstr "Pool Usage"
|
||||
|
||||
@@ -1427,10 +1431,21 @@ msgstr "Queue Depth"
|
||||
msgid "Quiet Hours"
|
||||
msgstr "Quiet Hours"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Raw"
|
||||
msgstr "Raw"
|
||||
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Raw usage of storage pool {displayName}"
|
||||
msgstr "Raw usage of storage pool {displayName}"
|
||||
|
||||
#. Disk read
|
||||
#. Disk read
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -1438,7 +1453,7 @@ msgstr "Quiet Hours"
|
||||
msgid "Read"
|
||||
msgstr "Read"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Read errors"
|
||||
msgstr "Read errors"
|
||||
|
||||
@@ -1449,7 +1464,7 @@ msgstr "Received"
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Refresh"
|
||||
msgstr "Refresh"
|
||||
|
||||
@@ -1638,7 +1653,7 @@ msgstr "Start Time"
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "State"
|
||||
@@ -1767,9 +1782,9 @@ msgstr "This will permanently delete all selected records from the database."
|
||||
msgid "Throughput of {extraFsName}"
|
||||
msgstr "Throughput of {extraFsName}"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Throughput of ZFS pool {poolName}"
|
||||
msgstr "Throughput of ZFS pool {poolName}"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Throughput of storage pool {displayName}"
|
||||
msgstr "Throughput of storage pool {displayName}"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Time format"
|
||||
@@ -1892,6 +1907,7 @@ 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
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Type"
|
||||
msgstr "Type"
|
||||
|
||||
@@ -1914,7 +1930,7 @@ msgid "Universal token"
|
||||
msgstr "Universal token"
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Unknown"
|
||||
msgstr "Unknown"
|
||||
@@ -1940,7 +1956,7 @@ msgstr "Update"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Updated"
|
||||
msgstr "Updated"
|
||||
@@ -1963,20 +1979,20 @@ msgstr "Uptime"
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Usage"
|
||||
msgstr "Usage"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Usage of ZFS pool {poolName}"
|
||||
msgstr "Usage of ZFS pool {poolName}"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Usage of storage pool {displayName}"
|
||||
msgstr "Usage of storage pool {displayName}"
|
||||
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Used"
|
||||
msgstr "Used"
|
||||
|
||||
@@ -2053,7 +2069,7 @@ msgstr "Windows command"
|
||||
#. Disk write
|
||||
#. Disk write
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -2061,7 +2077,7 @@ msgstr "Windows command"
|
||||
msgid "Write"
|
||||
msgstr "Write"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Write errors"
|
||||
msgstr "Write errors"
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: es\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-09-02 21:43\n"
|
||||
"PO-Revision-Date: 2026-09-10 00:33\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Spanish\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -61,7 +61,7 @@ msgstr "1 hora"
|
||||
#. Load average
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "1 min"
|
||||
msgstr "1 min"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 minute"
|
||||
@@ -78,7 +78,7 @@ msgstr "12 horas"
|
||||
#. Load average
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "15 min"
|
||||
msgstr "15 min"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "24 hours"
|
||||
@@ -91,13 +91,13 @@ msgstr "30 días"
|
||||
#. Load average
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "5 min"
|
||||
msgstr "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/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Actions"
|
||||
msgstr "Acciones"
|
||||
@@ -196,7 +196,7 @@ msgstr "¿Estás seguro?"
|
||||
msgid "Automatic copy requires a secure context."
|
||||
msgstr "La copia automática requiere un contexto seguro."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Disk space available"
|
||||
msgid "Available"
|
||||
msgstr "Disponible"
|
||||
@@ -258,7 +258,7 @@ msgstr "Ancho de banda"
|
||||
#. Battery label in systems table header
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Bat"
|
||||
msgstr "Bat"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
@@ -338,7 +338,7 @@ msgid "Capabilities"
|
||||
msgstr "Capacidades"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Capacity"
|
||||
msgstr "Capacidad"
|
||||
|
||||
@@ -348,7 +348,7 @@ msgstr "Precaución - posible pérdida de datos"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Celsius (°C)"
|
||||
msgstr "Celsius (°C)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change display units for metrics."
|
||||
@@ -391,14 +391,14 @@ msgstr "Compruebe su servicio de monitorización"
|
||||
msgid "Check your notification service"
|
||||
msgstr "Verifica tu servicio de notificaciones"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Checksum errors"
|
||||
msgstr "Errores de suma de comprobación"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Clear"
|
||||
msgstr "Limpiar"
|
||||
@@ -411,7 +411,7 @@ msgstr "Haz clic en un contenedor para ver más información."
|
||||
msgid "Click on a device to view more information."
|
||||
msgstr "Haz clic en un dispositivo para ver más información."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Click on a pool to view vdev and dataset details."
|
||||
msgstr "Haz clic en un pool para ver los detalles de los vdev y los conjuntos de datos."
|
||||
|
||||
@@ -503,7 +503,7 @@ msgstr "Copiar nombre"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Copy public key"
|
||||
msgstr ""
|
||||
msgstr "Copiar clave pública"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
@@ -530,7 +530,7 @@ msgstr "Núcleo"
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "CPU"
|
||||
msgstr "CPU"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "CPU Cores"
|
||||
@@ -783,7 +783,7 @@ msgstr "Efímero"
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Error"
|
||||
msgstr "Error"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Example:"
|
||||
@@ -826,7 +826,7 @@ msgstr "Exporta la configuración actual de sus sistemas."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Fahrenheit (°F)"
|
||||
msgstr "Fahrenheit (°F)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Failed"
|
||||
@@ -869,14 +869,14 @@ msgstr "Fallidos: {0}"
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
msgid "Fans"
|
||||
msgstr ""
|
||||
msgstr "Ventiladores"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system/chart-card.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Filter..."
|
||||
@@ -888,7 +888,7 @@ msgstr "Huella dactilar"
|
||||
|
||||
#: 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}}"
|
||||
@@ -898,8 +898,8 @@ msgstr "Por <0>{min}</0> {min, plural, one {minuto} other {minutos}}"
|
||||
msgid "Forgot password?"
|
||||
msgstr "¿Olvidaste tu contraseña?"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Free space"
|
||||
msgid "Free"
|
||||
msgstr "Libre"
|
||||
@@ -920,11 +920,11 @@ msgstr "Llena"
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "General"
|
||||
msgstr "General"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Global"
|
||||
msgstr "Global"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "GPU"
|
||||
@@ -948,7 +948,7 @@ msgid "Grid"
|
||||
msgstr "Cuadrícula"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Health"
|
||||
msgstr "Estado"
|
||||
|
||||
@@ -1146,7 +1146,7 @@ msgstr "Uso de memoria de los contenedores"
|
||||
msgid "Model"
|
||||
msgstr "Modelo"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Mountpoint"
|
||||
msgstr "Punto de montaje"
|
||||
|
||||
@@ -1183,9 +1183,9 @@ msgstr "Unidad de red"
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "No"
|
||||
msgstr "No"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "No detail data for this pool."
|
||||
msgstr "No hay datos detallados para este pool."
|
||||
|
||||
@@ -1212,7 +1212,7 @@ msgstr "No hay atributos S.M.A.R.T. disponibles para este dispositivo."
|
||||
msgid "No systems found."
|
||||
msgstr "No se encontraron sistemas."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "None"
|
||||
msgstr "Ninguno"
|
||||
|
||||
@@ -1255,7 +1255,7 @@ msgstr "Contraseña de un solo uso"
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Open menu"
|
||||
msgstr "Abrir menú"
|
||||
@@ -1346,6 +1346,10 @@ msgstr "Permanente"
|
||||
msgid "Persistence"
|
||||
msgstr "Persistencia"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
msgid "Physical device space. True usable capacity is unknown. Pool disk usage alerts are disabled."
|
||||
msgstr "Espacio físico del dispositivo. La capacidad útil real se desconoce. Las alertas de uso de disco del pool están desactivadas."
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
msgstr "Por favor, <0>configura un servidor SMTP</0> para asegurar que las alertas sean entregadas."
|
||||
@@ -1379,11 +1383,11 @@ msgstr "Por favor, consulta <0>la documentación</0> para obtener instrucciones.
|
||||
msgid "Please sign in to your account"
|
||||
msgstr "Por favor, inicia sesión en tu cuenta"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Pool Health"
|
||||
msgstr "Estado del pool"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Pool Usage"
|
||||
msgstr "Uso del pool"
|
||||
|
||||
@@ -1416,7 +1420,7 @@ msgstr "Proceso iniciado"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Public key"
|
||||
msgstr ""
|
||||
msgstr "Clave pública"
|
||||
|
||||
#. Use 'Key' if your language requires many more characters
|
||||
#: src/components/add-system.tsx
|
||||
@@ -1432,10 +1436,21 @@ msgstr "Profundidad de cola"
|
||||
msgid "Quiet Hours"
|
||||
msgstr "Horas de silencio"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Raw"
|
||||
msgstr "Bruto"
|
||||
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Raw usage of storage pool {displayName}"
|
||||
msgstr "Uso bruto del pool de almacenamiento {displayName}"
|
||||
|
||||
#. Disk read
|
||||
#. Disk read
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -1443,7 +1458,7 @@ msgstr "Horas de silencio"
|
||||
msgid "Read"
|
||||
msgstr "Lectura"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Read errors"
|
||||
msgstr "Errores de lectura"
|
||||
|
||||
@@ -1454,7 +1469,7 @@ msgstr "Recibido"
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Refresh"
|
||||
msgstr "Actualizar"
|
||||
|
||||
@@ -1643,7 +1658,7 @@ 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/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "Estado"
|
||||
@@ -1671,7 +1686,7 @@ msgstr "Uso de swap"
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Switch theme"
|
||||
msgstr ""
|
||||
msgstr "Cambiar tema"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
@@ -1690,7 +1705,7 @@ msgstr "Sistema"
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
msgid "System fan speeds (RPM)"
|
||||
msgstr ""
|
||||
msgstr "Velocidades de los ventiladores del sistema (RPM)"
|
||||
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "System load averages over time"
|
||||
@@ -1772,9 +1787,9 @@ msgstr "Esto eliminará permanentemente todos los registros seleccionados de la
|
||||
msgid "Throughput of {extraFsName}"
|
||||
msgstr "Rendimiento de {extraFsName}"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Throughput of ZFS pool {poolName}"
|
||||
msgstr "Rendimiento del pool ZFS {poolName}"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Throughput of storage pool {displayName}"
|
||||
msgstr "Rendimiento del pool de almacenamiento {displayName}"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Time format"
|
||||
@@ -1787,7 +1802,7 @@ msgstr "A correo(s)"
|
||||
#: 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
|
||||
@@ -1806,7 +1821,7 @@ msgstr "Los tokens y las huellas digitales se utilizan para autenticar las conex
|
||||
#: src/components/ui/chart.tsx
|
||||
#: src/components/ui/chart.tsx
|
||||
msgid "Total"
|
||||
msgstr "Total"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/network-sheet.tsx
|
||||
msgid "Total data received for each interface"
|
||||
@@ -1819,12 +1834,12 @@ msgstr "Datos totales enviados por cada interfaz"
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
msgctxt "Disk I/O"
|
||||
msgid "Total time spent on read/write (can exceed 100%)"
|
||||
msgstr ""
|
||||
msgstr "Tiempo total dedicado a lectura/escritura (puede superar el 100 %)"
|
||||
|
||||
#. placeholder {0}: data.length
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Total: {0}"
|
||||
msgstr "Total: {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Triggered by"
|
||||
@@ -1897,6 +1912,7 @@ 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
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Type"
|
||||
msgstr "Tipo"
|
||||
|
||||
@@ -1919,7 +1935,7 @@ msgid "Universal token"
|
||||
msgstr "Token universal"
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Unknown"
|
||||
msgstr "Desconocida"
|
||||
@@ -1945,7 +1961,7 @@ msgstr "Actualizar"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Updated"
|
||||
msgstr "Actualizado"
|
||||
@@ -1968,20 +1984,20 @@ msgstr "Tiempo de actividad"
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Usage"
|
||||
msgstr "Uso"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Usage of ZFS pool {poolName}"
|
||||
msgstr "Uso del pool ZFS {poolName}"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Usage of storage pool {displayName}"
|
||||
msgstr "Uso del pool de almacenamiento {displayName}"
|
||||
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Used"
|
||||
msgstr "Usado"
|
||||
|
||||
@@ -2058,7 +2074,7 @@ msgstr "Comando Windows"
|
||||
#. Disk write
|
||||
#. Disk write
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -2066,7 +2082,7 @@ msgstr "Comando Windows"
|
||||
msgid "Write"
|
||||
msgstr "Escritura"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Write errors"
|
||||
msgstr "Errores de escritura"
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: fa\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-09-02 21:43\n"
|
||||
"PO-Revision-Date: 2026-09-10 00:33\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Persian\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -97,7 +97,7 @@ msgstr "۵ دقیقه"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Actions"
|
||||
msgstr "عملیات"
|
||||
@@ -196,7 +196,7 @@ msgstr "آیا مطمئن هستید؟"
|
||||
msgid "Automatic copy requires a secure context."
|
||||
msgstr "کپی خودکار نیاز به یک زمینه امن دارد."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Disk space available"
|
||||
msgid "Available"
|
||||
msgstr "در دسترس"
|
||||
@@ -338,7 +338,7 @@ msgid "Capabilities"
|
||||
msgstr "قابلیتها"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Capacity"
|
||||
msgstr "ظرفیت"
|
||||
|
||||
@@ -391,14 +391,14 @@ msgstr "سرویس نظارتی خود را بررسی کنید"
|
||||
msgid "Check your notification service"
|
||||
msgstr "سرویس اطلاعرسانی خود را بررسی کنید"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Checksum errors"
|
||||
msgstr "خطاهای مجموع بررسی"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Clear"
|
||||
msgstr "پاک کردن"
|
||||
@@ -411,7 +411,7 @@ msgstr "برای مشاهده اطلاعات بیشتر روی کانتینر ک
|
||||
msgid "Click on a device to view more information."
|
||||
msgstr "برای مشاهده اطلاعات بیشتر روی دستگاه کلیک کنید."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Click on a pool to view vdev and dataset details."
|
||||
msgstr "برای مشاهده جزئیات vdev و مجموعهدادهها روی یک استخر کلیک کنید."
|
||||
|
||||
@@ -503,7 +503,7 @@ msgstr "کپی نام"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Copy public key"
|
||||
msgstr ""
|
||||
msgstr "کپی کردن کلید عمومی"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
@@ -869,14 +869,14 @@ msgstr "ناموفق: {0}"
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
msgid "Fans"
|
||||
msgstr ""
|
||||
msgstr "فنها"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system/chart-card.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Filter..."
|
||||
@@ -898,8 +898,8 @@ msgstr "برای <0>{min}</0> {min, plural, one {دقیقه} other {دقیقه}}
|
||||
msgid "Forgot password?"
|
||||
msgstr "رمز عبور را فراموش کردهاید؟"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Free space"
|
||||
msgid "Free"
|
||||
msgstr "آزاد"
|
||||
@@ -948,7 +948,7 @@ msgid "Grid"
|
||||
msgstr "جدول"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Health"
|
||||
msgstr "سلامتی"
|
||||
|
||||
@@ -1146,7 +1146,7 @@ msgstr "میزان استفاده حافظه کانتینرها"
|
||||
msgid "Model"
|
||||
msgstr "مدل"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Mountpoint"
|
||||
msgstr "نقطه اتصال"
|
||||
|
||||
@@ -1185,7 +1185,7 @@ msgstr "واحد شبکه"
|
||||
msgid "No"
|
||||
msgstr "خیر"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "No detail data for this pool."
|
||||
msgstr "دادهٔ جزئی برای این استخر موجود نیست."
|
||||
|
||||
@@ -1212,7 +1212,7 @@ msgstr "هیچ ویژگی S.M.A.R.T برای این دستگاه موجود نی
|
||||
msgid "No systems found."
|
||||
msgstr "هیچ سیستمی یافت نشد."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "None"
|
||||
msgstr "هیچکدام"
|
||||
|
||||
@@ -1255,7 +1255,7 @@ msgstr "رمز عبور یکبار مصرف"
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Open menu"
|
||||
msgstr "باز کردن منو"
|
||||
@@ -1346,6 +1346,10 @@ msgstr "دائمی"
|
||||
msgid "Persistence"
|
||||
msgstr "ماندگاری"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
msgid "Physical device space. True usable capacity is unknown. Pool disk usage alerts are disabled."
|
||||
msgstr "فضای فیزیکی دستگاه. ظرفیت واقعی قابل استفاده نامشخص است. هشدارهای استفاده از دیسک استخر غیرفعال هستند."
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
msgstr "لطفاً برای اطمینان از تحویل هشدارها، یک <0>سرور SMTP پیکربندی کنید</0>."
|
||||
@@ -1379,11 +1383,11 @@ msgstr "لطفاً برای دستورالعملها به <0>مستندات</
|
||||
msgid "Please sign in to your account"
|
||||
msgstr "لطفاً به حساب کاربری خود وارد شوید"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Pool Health"
|
||||
msgstr "سلامت استخر"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Pool Usage"
|
||||
msgstr "استفاده از استخر"
|
||||
|
||||
@@ -1416,7 +1420,7 @@ msgstr "فرآیند شروع شد"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Public key"
|
||||
msgstr ""
|
||||
msgstr "کلید عمومی"
|
||||
|
||||
#. Use 'Key' if your language requires many more characters
|
||||
#: src/components/add-system.tsx
|
||||
@@ -1432,10 +1436,21 @@ msgstr "عمق صف"
|
||||
msgid "Quiet Hours"
|
||||
msgstr "ساعات آرام"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Raw"
|
||||
msgstr "خام"
|
||||
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Raw usage of storage pool {displayName}"
|
||||
msgstr "استفاده خام از استخر ذخیرهسازی {displayName}"
|
||||
|
||||
#. Disk read
|
||||
#. Disk read
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -1443,7 +1458,7 @@ msgstr "ساعات آرام"
|
||||
msgid "Read"
|
||||
msgstr "خواندن"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Read errors"
|
||||
msgstr "خطاهای خواندن"
|
||||
|
||||
@@ -1454,7 +1469,7 @@ msgstr "دریافت شد"
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Refresh"
|
||||
msgstr "تازهسازی"
|
||||
|
||||
@@ -1643,7 +1658,7 @@ msgstr "زمان شروع"
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "وضعیت"
|
||||
@@ -1671,7 +1686,7 @@ msgstr "میزان استفاده از Swap"
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Switch theme"
|
||||
msgstr ""
|
||||
msgstr "تغییر تم"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
@@ -1690,7 +1705,7 @@ msgstr "سیستم"
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
msgid "System fan speeds (RPM)"
|
||||
msgstr ""
|
||||
msgstr "سرعت فنهای سیستم (RPM)"
|
||||
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "System load averages over time"
|
||||
@@ -1772,9 +1787,9 @@ msgstr "این کار تمام رکوردهای انتخاب شده را برا
|
||||
msgid "Throughput of {extraFsName}"
|
||||
msgstr "توان عملیاتی {extraFsName}"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Throughput of ZFS pool {poolName}"
|
||||
msgstr "توان عملیاتی استخر ZFS {poolName}"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Throughput of storage pool {displayName}"
|
||||
msgstr "توان عملیاتی استخر ذخیرهسازی {displayName}"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Time format"
|
||||
@@ -1819,7 +1834,7 @@ msgstr "دادههای کل ارسال شده برای هر رابط"
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
msgctxt "Disk I/O"
|
||||
msgid "Total time spent on read/write (can exceed 100%)"
|
||||
msgstr ""
|
||||
msgstr "کل زمان صرفشده برای خواندن/نوشتن (ممکن است از ۱۰۰٪ بیشتر شود)"
|
||||
|
||||
#. placeholder {0}: data.length
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
@@ -1897,6 +1912,7 @@ msgstr "هنگامی که استفاده از هر دیسکی از یک آستا
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Type"
|
||||
msgstr "نوع"
|
||||
|
||||
@@ -1919,7 +1935,7 @@ msgid "Universal token"
|
||||
msgstr "توکن جهانی"
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Unknown"
|
||||
msgstr "ناشناخته"
|
||||
@@ -1945,7 +1961,7 @@ msgstr "بهروزرسانی"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Updated"
|
||||
msgstr "بهروزرسانی شد"
|
||||
@@ -1968,20 +1984,20 @@ msgstr "آپتایم"
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Usage"
|
||||
msgstr "میزان استفاده"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Usage of ZFS pool {poolName}"
|
||||
msgstr "استفاده از استخر ZFS {poolName}"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Usage of storage pool {displayName}"
|
||||
msgstr "استفاده از استخر ذخیرهسازی {displayName}"
|
||||
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Used"
|
||||
msgstr "استفاده شده"
|
||||
|
||||
@@ -2058,7 +2074,7 @@ msgstr "دستور Windows"
|
||||
#. Disk write
|
||||
#. Disk write
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -2066,7 +2082,7 @@ msgstr "دستور Windows"
|
||||
msgid "Write"
|
||||
msgstr "نوشتن"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Write errors"
|
||||
msgstr "خطاهای نوشتن"
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: fr\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-09-02 21:42\n"
|
||||
"PO-Revision-Date: 2026-09-10 00:33\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: French\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
@@ -52,7 +52,7 @@ msgstr "E/S {diskName}"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "{threads, plural, one {# thread} other {# threads}}"
|
||||
msgstr "{threads, plural, one {# thread} other {# threads}}"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 hour"
|
||||
@@ -61,11 +61,11 @@ msgstr "1 heure"
|
||||
#. Load average
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "1 min"
|
||||
msgstr "1 min"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 minute"
|
||||
msgstr "1 minute"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 week"
|
||||
@@ -78,7 +78,7 @@ msgstr "12 heures"
|
||||
#. Load average
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "15 min"
|
||||
msgstr "15 min"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "24 hours"
|
||||
@@ -91,23 +91,23 @@ msgstr "30 jours"
|
||||
#. Load average
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "5 min"
|
||||
msgstr "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/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Actions"
|
||||
msgstr "Actions"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Active"
|
||||
msgstr "Active"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/active-alerts.tsx
|
||||
msgid "Active Alerts"
|
||||
@@ -142,7 +142,7 @@ msgstr "Ajuster la largeur de la mise en page principale"
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Admin"
|
||||
msgstr "Admin"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "After"
|
||||
@@ -154,7 +154,7 @@ msgstr "Après avoir défini les variables d'environnement, redémarrez votre hu
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Agent"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
@@ -196,7 +196,7 @@ msgstr "Êtes-vous sûr ?"
|
||||
msgid "Automatic copy requires a secure context."
|
||||
msgstr "La copie automatique nécessite un contexte sécurisé."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Disk space available"
|
||||
msgid "Available"
|
||||
msgstr "Disponible"
|
||||
@@ -258,7 +258,7 @@ msgstr "Bande passante"
|
||||
#. Battery label in systems table header
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Bat"
|
||||
msgstr "Bat"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
@@ -300,7 +300,7 @@ msgstr "Binaire"
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bits (Kbps, Mbps, Gbps)"
|
||||
msgstr "Bits (Kbps, Mbps, Gbps)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Boot state"
|
||||
@@ -309,7 +309,7 @@ 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)"
|
||||
msgstr "Bytes (KB/s, MB/s, GB/s)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
msgid "Cache / Buffers"
|
||||
@@ -338,7 +338,7 @@ msgid "Capabilities"
|
||||
msgstr "Capacités"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Capacity"
|
||||
msgstr "Capacité"
|
||||
|
||||
@@ -348,7 +348,7 @@ msgstr "Attention - perte de données potentielle"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Celsius (°C)"
|
||||
msgstr "Celsius (°C)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change display units for metrics."
|
||||
@@ -360,7 +360,7 @@ msgstr "Modifier les options générales de l'application."
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
msgid "Charge"
|
||||
msgstr "Charge"
|
||||
msgstr ""
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/lib/i18n.ts
|
||||
@@ -391,14 +391,14 @@ msgstr "Vérifiez votre service de surveillance"
|
||||
msgid "Check your notification service"
|
||||
msgstr "Vérifiez votre service de notification"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Checksum errors"
|
||||
msgstr "Erreurs de somme de contrôle"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Clear"
|
||||
msgstr "Effacer"
|
||||
@@ -411,7 +411,7 @@ msgstr "Cliquez sur un conteneur pour voir plus d'informations."
|
||||
msgid "Click on a device to view more information."
|
||||
msgstr "Cliquez sur un appareil pour voir plus d'informations."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Click on a pool to view vdev and dataset details."
|
||||
msgstr "Cliquez sur un pool pour afficher les détails des vdev et des jeux de données."
|
||||
|
||||
@@ -503,7 +503,7 @@ msgstr "Copier le nom"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Copy public key"
|
||||
msgstr ""
|
||||
msgstr "Copier la clé publique"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
@@ -530,7 +530,7 @@ msgstr "Cœur"
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "CPU"
|
||||
msgstr "CPU"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "CPU Cores"
|
||||
@@ -614,7 +614,7 @@ msgstr "État actuel"
|
||||
#. Power Cycles
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Cycles"
|
||||
msgstr "Cycles"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
@@ -643,7 +643,7 @@ msgstr "Supprimer l'empreinte"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Description"
|
||||
msgstr "Description"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
msgid "Detail"
|
||||
@@ -696,7 +696,7 @@ msgstr "Entrée/Sortie réseau Docker"
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Documentation"
|
||||
msgstr "Documentation"
|
||||
msgstr ""
|
||||
|
||||
#. Context: System is down
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
@@ -732,7 +732,7 @@ msgstr "Modifier {foo}"
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
#: src/components/login/otp-forms.tsx
|
||||
msgid "Email"
|
||||
msgstr "Email"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Email notifications"
|
||||
@@ -826,7 +826,7 @@ msgstr "Exportez la configuration actuelle de vos systèmes."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Fahrenheit (°F)"
|
||||
msgstr "Fahrenheit (°F)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Failed"
|
||||
@@ -869,14 +869,14 @@ msgstr "Échec : {0}"
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
msgid "Fans"
|
||||
msgstr ""
|
||||
msgstr "Ventilateurs"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system/chart-card.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Filter..."
|
||||
@@ -898,8 +898,8 @@ msgstr "Pendant <0>{min}</0> {min, plural, one {minute} other {minutes}}"
|
||||
msgid "Forgot password?"
|
||||
msgstr "Mot de passe oublié ?"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Free space"
|
||||
msgid "Free"
|
||||
msgstr "Espace libre"
|
||||
@@ -924,11 +924,11 @@ msgstr "Général"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Global"
|
||||
msgstr "Global"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "GPU"
|
||||
msgstr "GPU"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
msgid "GPU Engines"
|
||||
@@ -948,7 +948,7 @@ msgid "Grid"
|
||||
msgstr "Grille"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Health"
|
||||
msgstr "Santé"
|
||||
|
||||
@@ -1009,7 +1009,7 @@ msgstr "Si vous avez perdu le mot de passe de votre compte administrateur, vous
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
msgctxt "Docker image"
|
||||
msgid "Image"
|
||||
msgstr "Image"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Inactive"
|
||||
@@ -1113,7 +1113,7 @@ msgstr "Guide pour une installation manuelle"
|
||||
#. Chart select field. Please try to keep this short.
|
||||
#: src/components/routes/system/chart-card.tsx
|
||||
msgid "Max 1 min"
|
||||
msgstr "Max 1 min"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
@@ -1146,7 +1146,7 @@ msgstr "Utilisation de la mémoire des conteneurs"
|
||||
msgid "Model"
|
||||
msgstr "Modèle"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Mountpoint"
|
||||
msgstr "Point de montage"
|
||||
|
||||
@@ -1185,7 +1185,7 @@ msgstr "Unité réseau"
|
||||
msgid "No"
|
||||
msgstr "Non"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "No detail data for this pool."
|
||||
msgstr "Aucune donnée détaillée disponible pour ce pool."
|
||||
|
||||
@@ -1212,7 +1212,7 @@ msgstr "Aucun attribut S.M.A.R.T. disponible pour cet appareil."
|
||||
msgid "No systems found."
|
||||
msgstr "Aucun système trouvé."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "None"
|
||||
msgstr "Aucun"
|
||||
|
||||
@@ -1220,7 +1220,7 @@ msgstr "Aucun"
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Notifications"
|
||||
msgstr "Notifications"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Notifications may include recent container log excerpts."
|
||||
@@ -1255,7 +1255,7 @@ msgstr "Mot de passe à usage unique"
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Open menu"
|
||||
msgstr "Ouvrir le menu"
|
||||
@@ -1276,7 +1276,7 @@ msgstr "Écraser les alertes existantes"
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Page"
|
||||
msgstr "Page"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: table.getState().pagination.pageIndex + 1
|
||||
#. placeholder {1}: table.getPageCount()
|
||||
@@ -1311,7 +1311,7 @@ msgstr "Passé"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Pause"
|
||||
msgstr "Pause"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Paused"
|
||||
@@ -1340,12 +1340,16 @@ msgstr "Pourcentage de temps passé dans chaque état"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Permanent"
|
||||
msgstr "Permanent"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Persistence"
|
||||
msgstr "Persistance"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
msgid "Physical device space. True usable capacity is unknown. Pool disk usage alerts are disabled."
|
||||
msgstr "Espace physique de l'appareil. La capacité réellement utilisable est inconnue. Les alertes d'utilisation des disques du pool sont désactivées."
|
||||
|
||||
#: 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."
|
||||
@@ -1379,22 +1383,22 @@ msgstr "Veuillez consulter <0>la documentation</0> pour les instructions."
|
||||
msgid "Please sign in to your account"
|
||||
msgstr "Veuillez vous connecter à votre compte"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Pool Health"
|
||||
msgstr "État du pool"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Pool Usage"
|
||||
msgstr "Utilisation du pool"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Port"
|
||||
msgstr "Port"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
msgctxt "Container ports"
|
||||
msgid "Ports"
|
||||
msgstr "Ports"
|
||||
msgstr ""
|
||||
|
||||
#. Power On Time
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
@@ -1416,7 +1420,7 @@ msgstr "Processus démarré"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Public key"
|
||||
msgstr ""
|
||||
msgstr "Clé publique"
|
||||
|
||||
#. Use 'Key' if your language requires many more characters
|
||||
#: src/components/add-system.tsx
|
||||
@@ -1432,10 +1436,21 @@ msgstr "Profondeur de file d'attente"
|
||||
msgid "Quiet Hours"
|
||||
msgstr "Heures calmes"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Raw"
|
||||
msgstr "Brut"
|
||||
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Raw usage of storage pool {displayName}"
|
||||
msgstr "Utilisation brute du pool de stockage {displayName}"
|
||||
|
||||
#. Disk read
|
||||
#. Disk read
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -1443,7 +1458,7 @@ msgstr "Heures calmes"
|
||||
msgid "Read"
|
||||
msgstr "Lecture"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Read errors"
|
||||
msgstr "Erreurs de lecture"
|
||||
|
||||
@@ -1454,7 +1469,7 @@ msgstr "Reçu"
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Refresh"
|
||||
msgstr "Actualiser"
|
||||
|
||||
@@ -1599,7 +1614,7 @@ msgstr "Détails du service"
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Services"
|
||||
msgstr "Services"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
@@ -1643,7 +1658,7 @@ 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/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "État"
|
||||
@@ -1671,7 +1686,7 @@ msgstr "Utilisation du swap"
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Switch theme"
|
||||
msgstr ""
|
||||
msgstr "Changer de thème"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
@@ -1690,7 +1705,7 @@ msgstr "Système"
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
msgid "System fan speeds (RPM)"
|
||||
msgstr ""
|
||||
msgstr "Vitesses des ventilateurs du système (RPM)"
|
||||
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "System load averages over time"
|
||||
@@ -1772,9 +1787,9 @@ msgstr "Ceci supprimera définitivement tous les enregistrements sélectionnés
|
||||
msgid "Throughput of {extraFsName}"
|
||||
msgstr "Débit de {extraFsName}"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Throughput of ZFS pool {poolName}"
|
||||
msgstr "Débit du pool ZFS {poolName}"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Throughput of storage pool {displayName}"
|
||||
msgstr "Débit du pool de stockage {displayName}"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Time format"
|
||||
@@ -1787,7 +1802,7 @@ msgstr "Aux email(s)"
|
||||
#: 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
|
||||
@@ -1806,7 +1821,7 @@ msgstr "Les tokens et les empreintes sont utilisés pour authentifier les connex
|
||||
#: src/components/ui/chart.tsx
|
||||
#: src/components/ui/chart.tsx
|
||||
msgid "Total"
|
||||
msgstr "Total"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/network-sheet.tsx
|
||||
msgid "Total data received for each interface"
|
||||
@@ -1897,8 +1912,9 @@ 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
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Type"
|
||||
msgstr "Type"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Unhealthy"
|
||||
@@ -1919,7 +1935,7 @@ msgid "Universal token"
|
||||
msgstr "Token universel"
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Unknown"
|
||||
msgstr "Inconnue"
|
||||
@@ -1945,7 +1961,7 @@ msgstr "Mettre à jour"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Updated"
|
||||
msgstr "Mis à jour"
|
||||
@@ -1968,20 +1984,20 @@ msgstr "Temps de fonctionnement"
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Usage"
|
||||
msgstr "Utilisation"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Usage of ZFS pool {poolName}"
|
||||
msgstr "Utilisation du pool ZFS {poolName}"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Usage of storage pool {displayName}"
|
||||
msgstr "Utilisation du pool de stockage {displayName}"
|
||||
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Used"
|
||||
msgstr "Utilisé"
|
||||
|
||||
@@ -2058,7 +2074,7 @@ msgstr "Commande Windows"
|
||||
#. Disk write
|
||||
#. Disk write
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -2066,7 +2082,7 @@ msgstr "Commande Windows"
|
||||
msgid "Write"
|
||||
msgstr "Écriture"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Write errors"
|
||||
msgstr "Erreurs d’écriture"
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: he\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-09-02 21:43\n"
|
||||
"PO-Revision-Date: 2026-09-10 00:33\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Hebrew\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n==2 ? 1 : 2);\n"
|
||||
@@ -97,7 +97,7 @@ msgstr "5 דק'"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Actions"
|
||||
msgstr "פעולות"
|
||||
@@ -196,7 +196,7 @@ msgstr "האם אתה בטוח?"
|
||||
msgid "Automatic copy requires a secure context."
|
||||
msgstr "העתקה אוטומטית דורשת הקשר מאובטח."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Disk space available"
|
||||
msgid "Available"
|
||||
msgstr "זמין"
|
||||
@@ -338,7 +338,7 @@ msgid "Capabilities"
|
||||
msgstr "יכולות"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Capacity"
|
||||
msgstr "קיבולת"
|
||||
|
||||
@@ -391,14 +391,14 @@ msgstr "בדוק את שירות הניטור שלך"
|
||||
msgid "Check your notification service"
|
||||
msgstr "בדוק את שירות ההתראות שלך"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Checksum errors"
|
||||
msgstr "שגיאות סכום ביקורת"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Clear"
|
||||
msgstr "נקה"
|
||||
@@ -411,7 +411,7 @@ msgstr "לחץ על קונטיינר כדי לצפות במידע נוסף."
|
||||
msgid "Click on a device to view more information."
|
||||
msgstr "לחץ על התקן כדי לצפות במידע נוסף."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Click on a pool to view vdev and dataset details."
|
||||
msgstr "לחצו על מאגר כדי להציג פרטי vdev ומערכי נתונים."
|
||||
|
||||
@@ -503,7 +503,7 @@ msgstr "העתק שם"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Copy public key"
|
||||
msgstr ""
|
||||
msgstr "העתק מפתח ציבורי"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
@@ -530,7 +530,7 @@ msgstr "ליבה"
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "CPU"
|
||||
msgstr "CPU"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "CPU Cores"
|
||||
@@ -869,14 +869,14 @@ msgstr "נכשל: {0}"
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
msgid "Fans"
|
||||
msgstr ""
|
||||
msgstr "מאווררים"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system/chart-card.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Filter..."
|
||||
@@ -898,8 +898,8 @@ msgstr "למשך <0>{min}</0> {min, plural, one {דקה} other {דקות}}"
|
||||
msgid "Forgot password?"
|
||||
msgstr "שכחת סיסמה?"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Free space"
|
||||
msgid "Free"
|
||||
msgstr "פנוי"
|
||||
@@ -948,7 +948,7 @@ msgid "Grid"
|
||||
msgstr "רשת"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Health"
|
||||
msgstr "בריאות"
|
||||
|
||||
@@ -1146,7 +1146,7 @@ msgstr "שימוש בזיכרון של קונטיינרים"
|
||||
msgid "Model"
|
||||
msgstr "דגם"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Mountpoint"
|
||||
msgstr "נקודת עגינה"
|
||||
|
||||
@@ -1185,7 +1185,7 @@ msgstr "יחידת רשת"
|
||||
msgid "No"
|
||||
msgstr "לא"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "No detail data for this pool."
|
||||
msgstr "אין נתונים מפורטים עבור מאגר זה."
|
||||
|
||||
@@ -1212,7 +1212,7 @@ msgstr "אין מאפייני S.M.A.R.T. זמינים עבור התקן זה."
|
||||
msgid "No systems found."
|
||||
msgstr "לא נמצאו מערכות."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "None"
|
||||
msgstr "ללא"
|
||||
|
||||
@@ -1255,7 +1255,7 @@ msgstr "סיסמה חד-פעמית"
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Open menu"
|
||||
msgstr "פתח תפריט"
|
||||
@@ -1346,6 +1346,10 @@ msgstr "קבוע"
|
||||
msgid "Persistence"
|
||||
msgstr "עקביות"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
msgid "Physical device space. True usable capacity is unknown. Pool disk usage alerts are disabled."
|
||||
msgstr "שטח פיזי של ההתקן. הקיבולת האמיתית הניתנת לשימוש אינה ידועה. התראות השימוש בדיסקים של המאגר מושבתות."
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
msgstr "אנא <0>הגדר שרת SMTP</0> כדי להבטיח שהתראות יישלחו."
|
||||
@@ -1379,11 +1383,11 @@ msgstr "אנא ראה <0>את התיעוד</0> להוראות."
|
||||
msgid "Please sign in to your account"
|
||||
msgstr "אנא התחבר לחשבון שלך"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Pool Health"
|
||||
msgstr "מצב המאגר"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Pool Usage"
|
||||
msgstr "שימוש במאגר"
|
||||
|
||||
@@ -1416,7 +1420,7 @@ msgstr "תהליך התחיל"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Public key"
|
||||
msgstr ""
|
||||
msgstr "מפתח ציבורי"
|
||||
|
||||
#. Use 'Key' if your language requires many more characters
|
||||
#: src/components/add-system.tsx
|
||||
@@ -1432,10 +1436,21 @@ msgstr "עומק תור"
|
||||
msgid "Quiet Hours"
|
||||
msgstr "שעות שקט"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Raw"
|
||||
msgstr "גולמי"
|
||||
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Raw usage of storage pool {displayName}"
|
||||
msgstr "שימוש גולמי במאגר האחסון {displayName}"
|
||||
|
||||
#. Disk read
|
||||
#. Disk read
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -1443,7 +1458,7 @@ msgstr "שעות שקט"
|
||||
msgid "Read"
|
||||
msgstr "קריאה"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Read errors"
|
||||
msgstr "שגיאות קריאה"
|
||||
|
||||
@@ -1454,7 +1469,7 @@ msgstr "התקבל"
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Refresh"
|
||||
msgstr "רענן"
|
||||
|
||||
@@ -1643,7 +1658,7 @@ msgstr "זמן התחלה"
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "מצב"
|
||||
@@ -1671,7 +1686,7 @@ msgstr "שימוש ב-Swap"
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Switch theme"
|
||||
msgstr ""
|
||||
msgstr "החלף ערכת נושא"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
@@ -1690,7 +1705,7 @@ msgstr "מערכת"
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
msgid "System fan speeds (RPM)"
|
||||
msgstr ""
|
||||
msgstr "מהירויות מאווררי המערכת (RPM)"
|
||||
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "System load averages over time"
|
||||
@@ -1772,9 +1787,9 @@ msgstr "פעולה זו תמחק לצמיתות את כל הרשומות שנב
|
||||
msgid "Throughput of {extraFsName}"
|
||||
msgstr "תפוקה של {extraFsName}"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Throughput of ZFS pool {poolName}"
|
||||
msgstr "תפוקת מאגר ZFS {poolName}"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Throughput of storage pool {displayName}"
|
||||
msgstr "תפוקה של מאגר האחסון {displayName}"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Time format"
|
||||
@@ -1787,7 +1802,7 @@ msgstr "לאימייל(ים)"
|
||||
#: 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
|
||||
@@ -1897,6 +1912,7 @@ msgstr "מופעל כאשר שימוש בכל דיסק עולה על סף"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Type"
|
||||
msgstr "סוג"
|
||||
|
||||
@@ -1919,7 +1935,7 @@ msgid "Universal token"
|
||||
msgstr "token אוניברסלי"
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Unknown"
|
||||
msgstr "לא ידוע"
|
||||
@@ -1945,7 +1961,7 @@ msgstr "עדכן"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Updated"
|
||||
msgstr "עודכן"
|
||||
@@ -1968,20 +1984,20 @@ msgstr "זמן פעילות"
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Usage"
|
||||
msgstr "שימוש"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Usage of ZFS pool {poolName}"
|
||||
msgstr "שימוש במאגר ZFS {poolName}"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Usage of storage pool {displayName}"
|
||||
msgstr "שימוש במאגר האחסון {displayName}"
|
||||
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Used"
|
||||
msgstr "בשימוש"
|
||||
|
||||
@@ -2058,7 +2074,7 @@ msgstr "פקודת Windows"
|
||||
#. Disk write
|
||||
#. Disk write
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -2066,7 +2082,7 @@ msgstr "פקודת Windows"
|
||||
msgid "Write"
|
||||
msgstr "כתיבה"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Write errors"
|
||||
msgstr "שגיאות כתיבה"
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: hr\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-09-02 21:43\n"
|
||||
"PO-Revision-Date: 2026-09-10 00:33\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"
|
||||
@@ -97,7 +97,7 @@ msgstr "5 minuta"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Actions"
|
||||
msgstr "Akcije"
|
||||
@@ -142,7 +142,7 @@ msgstr "Prilagodite širinu glavnog rasporeda"
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Admin"
|
||||
msgstr "Admin"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "After"
|
||||
@@ -154,7 +154,7 @@ msgstr "Nakon postavljanja varijabli okruženja, ponovno pokrenite svoj Beszel h
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Agent"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
@@ -196,7 +196,7 @@ msgstr "Jeste li sigurni?"
|
||||
msgid "Automatic copy requires a secure context."
|
||||
msgstr "Automatsko kopiranje zahtijeva siguran kontekst."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Disk space available"
|
||||
msgid "Available"
|
||||
msgstr "Dostupno"
|
||||
@@ -338,7 +338,7 @@ msgid "Capabilities"
|
||||
msgstr "Mogućnosti"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Capacity"
|
||||
msgstr "Kapacitet"
|
||||
|
||||
@@ -348,7 +348,7 @@ msgstr "Oprez - mogući gubitak podataka"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Celsius (°C)"
|
||||
msgstr "Celsius (°C)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change display units for metrics."
|
||||
@@ -391,14 +391,14 @@ msgstr "Provjerite svoju uslugu nadzora"
|
||||
msgid "Check your notification service"
|
||||
msgstr "Provjerite svoju obavještajnu uslugu"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Checksum errors"
|
||||
msgstr "Pogreške kontrolnog zbroja"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Clear"
|
||||
msgstr "Očisti"
|
||||
@@ -411,7 +411,7 @@ msgstr "Kliknite na spremnik za prikaz više informacija."
|
||||
msgid "Click on a device to view more information."
|
||||
msgstr "Kliknite na uređaj da biste vidjeli više informacija."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Click on a pool to view vdev and dataset details."
|
||||
msgstr "Kliknite na spremište za prikaz pojedinosti o vdevovima i skupovima podataka."
|
||||
|
||||
@@ -503,7 +503,7 @@ msgstr "Kopiraj naziv"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Copy public key"
|
||||
msgstr ""
|
||||
msgstr "Kopiraj javni ključ"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
@@ -661,7 +661,7 @@ msgstr "Prazni se"
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Disk"
|
||||
msgstr "Disk"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Disk unit"
|
||||
@@ -732,7 +732,7 @@ msgstr "Uredi {foo}"
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
#: src/components/login/otp-forms.tsx
|
||||
msgid "Email"
|
||||
msgstr "Email"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Email notifications"
|
||||
@@ -869,14 +869,14 @@ msgstr "Neuspjelo: {0}"
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
msgid "Fans"
|
||||
msgstr ""
|
||||
msgstr "Ventilatori"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system/chart-card.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Filter..."
|
||||
@@ -898,8 +898,8 @@ msgstr "Za <0>{min}</0> {min, plural, one {minutu} other {minute}}"
|
||||
msgid "Forgot password?"
|
||||
msgstr "Zaboravljena lozinka?"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Free space"
|
||||
msgid "Free"
|
||||
msgstr "Slobodno"
|
||||
@@ -948,7 +948,7 @@ msgid "Grid"
|
||||
msgstr "Rešetka"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Health"
|
||||
msgstr "Zdravlje"
|
||||
|
||||
@@ -972,7 +972,7 @@ msgstr "Homebrew naredba"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Host / IP"
|
||||
msgstr "Host / IP"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP Method"
|
||||
@@ -1146,7 +1146,7 @@ msgstr "Upotreba memorije spremnika"
|
||||
msgid "Model"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Mountpoint"
|
||||
msgstr "Točka montiranja"
|
||||
|
||||
@@ -1185,7 +1185,7 @@ msgstr "Mjerna jedinica za mrežu"
|
||||
msgid "No"
|
||||
msgstr "Ne"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "No detail data for this pool."
|
||||
msgstr "Nema detaljnih podataka za ovo spremište."
|
||||
|
||||
@@ -1212,7 +1212,7 @@ msgstr "Nema dostupnih S.M.A.R.T. atributa za ovaj uređaj."
|
||||
msgid "No systems found."
|
||||
msgstr "Nije pronađen nijedan sustav."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "None"
|
||||
msgstr "Nema"
|
||||
|
||||
@@ -1255,7 +1255,7 @@ msgstr "Jednokratna lozinka"
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Open menu"
|
||||
msgstr "Otvori meni"
|
||||
@@ -1346,6 +1346,10 @@ msgstr "Trajan"
|
||||
msgid "Persistence"
|
||||
msgstr "Postojanost"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
msgid "Physical device space. True usable capacity is unknown. Pool disk usage alerts are disabled."
|
||||
msgstr "Fizički prostor uređaja. Stvarni iskoristivi kapacitet nije poznat. Upozorenja o iskorištenosti diskova spremišta su onemogućena."
|
||||
|
||||
#: 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."
|
||||
@@ -1379,17 +1383,17 @@ msgstr "Molimo provjerite <0>dokumentaciju</0> za upute."
|
||||
msgid "Please sign in to your account"
|
||||
msgstr "Molimo prijavite se u svoj račun"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Pool Health"
|
||||
msgstr "Stanje spremišta"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Pool Usage"
|
||||
msgstr "Iskorištenost spremišta"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Port"
|
||||
msgstr "Port"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
msgctxt "Container ports"
|
||||
@@ -1416,7 +1420,7 @@ msgstr "Proces pokrenut"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Public key"
|
||||
msgstr ""
|
||||
msgstr "Javni ključ"
|
||||
|
||||
#. Use 'Key' if your language requires many more characters
|
||||
#: src/components/add-system.tsx
|
||||
@@ -1432,10 +1436,21 @@ msgstr "Dubina reda"
|
||||
msgid "Quiet Hours"
|
||||
msgstr "Tihi sati"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Raw"
|
||||
msgstr "Sirovo"
|
||||
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Raw usage of storage pool {displayName}"
|
||||
msgstr "Sirova iskorištenost spremišta {displayName}"
|
||||
|
||||
#. Disk read
|
||||
#. Disk read
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -1443,7 +1458,7 @@ msgstr "Tihi sati"
|
||||
msgid "Read"
|
||||
msgstr "Pročitaj"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Read errors"
|
||||
msgstr "Pogreške čitanja"
|
||||
|
||||
@@ -1454,7 +1469,7 @@ msgstr "Primljeno"
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Refresh"
|
||||
msgstr "Osvježi"
|
||||
|
||||
@@ -1643,7 +1658,7 @@ 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/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "Stanje"
|
||||
@@ -1654,7 +1669,7 @@ msgstr "Stanje"
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Status"
|
||||
msgstr "Status"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Sub State"
|
||||
@@ -1671,7 +1686,7 @@ msgstr "Swap Iskorištenost"
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Switch theme"
|
||||
msgstr ""
|
||||
msgstr "Promijeni temu"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
@@ -1690,7 +1705,7 @@ msgstr "Sustav"
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
msgid "System fan speeds (RPM)"
|
||||
msgstr ""
|
||||
msgstr "Brzine ventilatora sustava (RPM)"
|
||||
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "System load averages over time"
|
||||
@@ -1725,7 +1740,7 @@ msgstr "Zadaci"
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Temp"
|
||||
msgstr "Temp"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
#: src/lib/alerts.ts
|
||||
@@ -1772,9 +1787,9 @@ msgstr "Ovom radnjom će se trajno izbrisati svi odabrani zapisi iz baze podatak
|
||||
msgid "Throughput of {extraFsName}"
|
||||
msgstr "Protok {extraFsName}"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Throughput of ZFS pool {poolName}"
|
||||
msgstr "Propusnost ZFS spremišta {poolName}"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Throughput of storage pool {displayName}"
|
||||
msgstr "Protok spremišta {displayName}"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Time format"
|
||||
@@ -1787,7 +1802,7 @@ msgstr "Primaoci emaila"
|
||||
#: 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
|
||||
@@ -1819,7 +1834,7 @@ msgstr "Ukupni podaci poslani za svako sučelje"
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
msgctxt "Disk I/O"
|
||||
msgid "Total time spent on read/write (can exceed 100%)"
|
||||
msgstr ""
|
||||
msgstr "Ukupno vrijeme utrošeno na čitanje/pisanje (može prelaziti 100 %)"
|
||||
|
||||
#. placeholder {0}: data.length
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
@@ -1897,6 +1912,7 @@ 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
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Type"
|
||||
msgstr "Vrsta"
|
||||
|
||||
@@ -1919,7 +1935,7 @@ msgid "Universal token"
|
||||
msgstr "Sveopći token"
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Unknown"
|
||||
msgstr "Nepoznato"
|
||||
@@ -1945,7 +1961,7 @@ msgstr "Ažuriraj"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Updated"
|
||||
msgstr "Ažurirano"
|
||||
@@ -1968,20 +1984,20 @@ msgstr "Vrijeme rada"
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Usage"
|
||||
msgstr "Iskorištenost"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Usage of ZFS pool {poolName}"
|
||||
msgstr "Iskorištenost ZFS spremišta {poolName}"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Usage of storage pool {displayName}"
|
||||
msgstr "Iskorištenost spremišta {displayName}"
|
||||
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Used"
|
||||
msgstr "Iskorišteno"
|
||||
|
||||
@@ -2058,7 +2074,7 @@ msgstr "Windows naredba"
|
||||
#. Disk write
|
||||
#. Disk write
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -2066,7 +2082,7 @@ msgstr "Windows naredba"
|
||||
msgid "Write"
|
||||
msgstr "Piši"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Write errors"
|
||||
msgstr "Pogreške zapisivanja"
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: hu\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-09-02 21:43\n"
|
||||
"PO-Revision-Date: 2026-09-10 00:33\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Hungarian\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -48,7 +48,7 @@ msgstr "{count, plural, one {{countString} perc} few {{countString} perc} many {
|
||||
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
msgid "{diskName} I/O"
|
||||
msgstr "{diskName} I/O"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "{threads, plural, one {# thread} other {# threads}}"
|
||||
@@ -97,7 +97,7 @@ msgstr "5 perc"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Actions"
|
||||
msgstr "Műveletek"
|
||||
@@ -196,7 +196,7 @@ msgstr "Biztos vagy benne?"
|
||||
msgid "Automatic copy requires a secure context."
|
||||
msgstr "Az automatikus másolás biztonságos környezetet igényel."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Disk space available"
|
||||
msgid "Available"
|
||||
msgstr "Elérhető"
|
||||
@@ -338,7 +338,7 @@ msgid "Capabilities"
|
||||
msgstr "Képességek"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Capacity"
|
||||
msgstr "Kapacitás"
|
||||
|
||||
@@ -348,7 +348,7 @@ msgstr "Figyelem - potenciális adatvesztés"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Celsius (°C)"
|
||||
msgstr "Celsius (°C)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change display units for metrics."
|
||||
@@ -391,14 +391,14 @@ msgstr "Ellenőrizze a megfigyelő szolgáltatást"
|
||||
msgid "Check your notification service"
|
||||
msgstr "Ellenőrizd az értesítési szolgáltatásodat"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Checksum errors"
|
||||
msgstr "Ellenőrzőösszeg-hibák"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Clear"
|
||||
msgstr "Törlés"
|
||||
@@ -411,7 +411,7 @@ msgstr "Kattintson egy konténerre a további információk megtekintéséhez."
|
||||
msgid "Click on a device to view more information."
|
||||
msgstr "Kattintson egy eszközre további információk megtekintéséhez."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Click on a pool to view vdev and dataset details."
|
||||
msgstr "Kattintson egy tárkészletre a vdev- és adatkészlet-részletek megtekintéséhez."
|
||||
|
||||
@@ -503,7 +503,7 @@ msgstr "Név másolása"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Copy public key"
|
||||
msgstr ""
|
||||
msgstr "Nyilvános kulcs másolása"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
@@ -530,7 +530,7 @@ msgstr "Mag"
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "CPU"
|
||||
msgstr "CPU"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "CPU Cores"
|
||||
@@ -732,7 +732,7 @@ msgstr "Szerkesztés {foo}"
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
#: src/components/login/otp-forms.tsx
|
||||
msgid "Email"
|
||||
msgstr "Email"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Email notifications"
|
||||
@@ -826,7 +826,7 @@ msgstr "Exportálja a jelenlegi rendszerkonfigurációt."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Fahrenheit (°F)"
|
||||
msgstr "Fahrenheit (°F)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Failed"
|
||||
@@ -869,14 +869,14 @@ msgstr "Sikertelen: {0}"
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
msgid "Fans"
|
||||
msgstr ""
|
||||
msgstr "Ventilátorok"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system/chart-card.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Filter..."
|
||||
@@ -888,7 +888,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}}"
|
||||
@@ -898,8 +898,8 @@ msgstr "<0>{min}</0> {min, plural, one {percig} other {percig}}"
|
||||
msgid "Forgot password?"
|
||||
msgstr "Elfelejtette a jelszavát?"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Free space"
|
||||
msgid "Free"
|
||||
msgstr "Szabad"
|
||||
@@ -948,7 +948,7 @@ msgid "Grid"
|
||||
msgstr "Rács"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Health"
|
||||
msgstr "Egészség"
|
||||
|
||||
@@ -1146,7 +1146,7 @@ msgstr "Konténerek memóriahasználata"
|
||||
msgid "Model"
|
||||
msgstr "Modell"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Mountpoint"
|
||||
msgstr "Csatolási pont"
|
||||
|
||||
@@ -1185,7 +1185,7 @@ msgstr "Sávszélesség mértékegysége"
|
||||
msgid "No"
|
||||
msgstr "Nem"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "No detail data for this pool."
|
||||
msgstr "Ehhez a tárkészlethez nem érhetők el részletes adatok."
|
||||
|
||||
@@ -1212,7 +1212,7 @@ msgstr "Ehhez az eszközhöz nem állnak rendelkezésre S.M.A.R.T. attribútumok
|
||||
msgid "No systems found."
|
||||
msgstr "Nem található rendszer."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "None"
|
||||
msgstr "Nincs"
|
||||
|
||||
@@ -1255,7 +1255,7 @@ msgstr "Egyszeri jelszó"
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Open menu"
|
||||
msgstr "Menü megnyitása"
|
||||
@@ -1346,6 +1346,10 @@ msgstr "Állandó"
|
||||
msgid "Persistence"
|
||||
msgstr "Tartósság"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
msgid "Physical device space. True usable capacity is unknown. Pool disk usage alerts are disabled."
|
||||
msgstr "Fizikai eszközterület. A valódi használható kapacitás ismeretlen. A készlet lemezhasználati riasztásai le vannak tiltva."
|
||||
|
||||
#: 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."
|
||||
@@ -1379,17 +1383,17 @@ msgstr "Kérjük, nézze meg <0>a dokumentációt</0> az utasításokért."
|
||||
msgid "Please sign in to your account"
|
||||
msgstr "Kérjük, jelentkezzen be a fiókjába"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Pool Health"
|
||||
msgstr "Tárkészlet állapota"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Pool Usage"
|
||||
msgstr "Tárkészlet kihasználtsága"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Port"
|
||||
msgstr "Port"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
msgctxt "Container ports"
|
||||
@@ -1416,7 +1420,7 @@ msgstr "Folyamat elindítva"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Public key"
|
||||
msgstr ""
|
||||
msgstr "Nyilvános kulcs"
|
||||
|
||||
#. Use 'Key' if your language requires many more characters
|
||||
#: src/components/add-system.tsx
|
||||
@@ -1432,10 +1436,21 @@ msgstr "Várakozási sor mélysége"
|
||||
msgid "Quiet Hours"
|
||||
msgstr "Csendes órák"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Raw"
|
||||
msgstr "Nyers"
|
||||
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Raw usage of storage pool {displayName}"
|
||||
msgstr "A(z) {displayName} tárolókészlet nyers kihasználtsága"
|
||||
|
||||
#. Disk read
|
||||
#. Disk read
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -1443,7 +1458,7 @@ msgstr "Csendes órák"
|
||||
msgid "Read"
|
||||
msgstr "Olvasás"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Read errors"
|
||||
msgstr "Olvasási hibák"
|
||||
|
||||
@@ -1454,7 +1469,7 @@ msgstr "Fogadott"
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Refresh"
|
||||
msgstr "Frissítés"
|
||||
|
||||
@@ -1643,7 +1658,7 @@ 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/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "Állapot"
|
||||
@@ -1671,7 +1686,7 @@ msgstr "Swap használat"
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Switch theme"
|
||||
msgstr ""
|
||||
msgstr "Téma váltása"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
@@ -1690,7 +1705,7 @@ msgstr "Rendszer"
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
msgid "System fan speeds (RPM)"
|
||||
msgstr ""
|
||||
msgstr "Rendszerventilátorok sebessége (RPM)"
|
||||
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "System load averages over time"
|
||||
@@ -1772,9 +1787,9 @@ msgstr "Ez véglegesen törli az összes kijelölt bejegyzést az adatbázisból
|
||||
msgid "Throughput of {extraFsName}"
|
||||
msgstr "A {extraFsName} átviteli teljesítménye"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Throughput of ZFS pool {poolName}"
|
||||
msgstr "{poolName} ZFS-tárkészlet átviteli sebessége"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Throughput of storage pool {displayName}"
|
||||
msgstr "A(z) {displayName} tárolókészlet átviteli teljesítménye"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Time format"
|
||||
@@ -1787,7 +1802,7 @@ msgstr "E-mailben"
|
||||
#: 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
|
||||
@@ -1819,7 +1834,7 @@ msgstr "Összes elküldött adat minden interfészenként"
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
msgctxt "Disk I/O"
|
||||
msgid "Total time spent on read/write (can exceed 100%)"
|
||||
msgstr ""
|
||||
msgstr "Olvasással/írással töltött teljes idő (meghaladhatja a 100%-ot)"
|
||||
|
||||
#. placeholder {0}: data.length
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
@@ -1897,6 +1912,7 @@ msgstr "Riaszt, ha a lemezhasználat 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
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Type"
|
||||
msgstr "Típus"
|
||||
|
||||
@@ -1919,7 +1935,7 @@ msgid "Universal token"
|
||||
msgstr "Univerzális token"
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Unknown"
|
||||
msgstr "Ismeretlen"
|
||||
@@ -1945,7 +1961,7 @@ msgstr "Frissítés"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Updated"
|
||||
msgstr "Frissítve"
|
||||
@@ -1968,20 +1984,20 @@ msgstr "Üzemidő"
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Usage"
|
||||
msgstr "Használat"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Usage of ZFS pool {poolName}"
|
||||
msgstr "{poolName} ZFS-tárkészlet kihasználtsága"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Usage of storage pool {displayName}"
|
||||
msgstr "A(z) {displayName} tárolókészlet kihasználtsága"
|
||||
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Used"
|
||||
msgstr "Felhasznált"
|
||||
|
||||
@@ -2058,7 +2074,7 @@ msgstr "Windows parancs"
|
||||
#. Disk write
|
||||
#. Disk write
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -2066,7 +2082,7 @@ msgstr "Windows parancs"
|
||||
msgid "Write"
|
||||
msgstr "Írás"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Write errors"
|
||||
msgstr "Írási hibák"
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: id\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-09-02 21:43\n"
|
||||
"PO-Revision-Date: 2026-09-10 00:33\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Indonesian\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
@@ -97,7 +97,7 @@ msgstr "5 mnt"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Actions"
|
||||
msgstr "Aksi"
|
||||
@@ -142,7 +142,7 @@ msgstr "Sesuaikan lebar layar utama"
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Admin"
|
||||
msgstr "Admin"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "After"
|
||||
@@ -196,7 +196,7 @@ msgstr "Apakah anda yakin?"
|
||||
msgid "Automatic copy requires a secure context."
|
||||
msgstr "Copy memerlukan https."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Disk space available"
|
||||
msgid "Available"
|
||||
msgstr "Tersedia"
|
||||
@@ -313,7 +313,7 @@ msgstr "Byte (KB/s, MB/s, GB/s)"
|
||||
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
msgid "Cache / Buffers"
|
||||
msgstr "Cache / Buffers"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Can reload"
|
||||
@@ -338,7 +338,7 @@ msgid "Capabilities"
|
||||
msgstr "Kapabilitas"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Capacity"
|
||||
msgstr "Kapasitas"
|
||||
|
||||
@@ -348,7 +348,7 @@ msgstr "Perhatian - potensi kehilangan data"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Celsius (°C)"
|
||||
msgstr "Celsius (°C)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change display units for metrics."
|
||||
@@ -391,14 +391,14 @@ msgstr "Periksa layanan pemantauan Anda"
|
||||
msgid "Check your notification service"
|
||||
msgstr "Periksa jasa penyedia notifikasi anda"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Checksum errors"
|
||||
msgstr "Kesalahan checksum"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Clear"
|
||||
msgstr "Bersihkan"
|
||||
@@ -411,7 +411,7 @@ msgstr "Klik pada kontainer untuk melihat informasi lebih banyak."
|
||||
msgid "Click on a device to view more information."
|
||||
msgstr "Klik pada perangkat untuk melihat informasi lebih banyak."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Click on a pool to view vdev and dataset details."
|
||||
msgstr "Klik pool untuk melihat detail vdev dan dataset."
|
||||
|
||||
@@ -503,7 +503,7 @@ msgstr "Salin nama"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Copy public key"
|
||||
msgstr ""
|
||||
msgstr "Salin kunci publik"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
@@ -869,14 +869,14 @@ msgstr "Gagal: {0}"
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
msgid "Fans"
|
||||
msgstr ""
|
||||
msgstr "Kipas"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system/chart-card.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Filter..."
|
||||
@@ -898,8 +898,8 @@ msgstr "Untuk <0>{min}</0> {min, plural, one {menit} other {menit}}"
|
||||
msgid "Forgot password?"
|
||||
msgstr "Lupa kata sandi?"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Free space"
|
||||
msgid "Free"
|
||||
msgstr "Kosong"
|
||||
@@ -948,7 +948,7 @@ msgid "Grid"
|
||||
msgstr "Kartu"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Health"
|
||||
msgstr "Kesehatan"
|
||||
|
||||
@@ -1146,7 +1146,7 @@ msgstr "Penggunaan memori container"
|
||||
msgid "Model"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Mountpoint"
|
||||
msgstr "Titik kait"
|
||||
|
||||
@@ -1185,7 +1185,7 @@ msgstr "Unit jaringan"
|
||||
msgid "No"
|
||||
msgstr "Tidak"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "No detail data for this pool."
|
||||
msgstr "Tidak ada data terperinci yang tersedia untuk pool ini."
|
||||
|
||||
@@ -1212,7 +1212,7 @@ msgstr "Tidak ada atribut S.M.A.R.T. yang tersedia untuk perangkat ini."
|
||||
msgid "No systems found."
|
||||
msgstr "Sistem tidak ditemukan."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "None"
|
||||
msgstr "Tidak ada"
|
||||
|
||||
@@ -1255,7 +1255,7 @@ msgstr "Kata sandi sekali pakai (OTP)"
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Open menu"
|
||||
msgstr "Buka menu"
|
||||
@@ -1346,6 +1346,10 @@ msgstr "Permanen"
|
||||
msgid "Persistence"
|
||||
msgstr "Tetap berlaku"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
msgid "Physical device space. True usable capacity is unknown. Pool disk usage alerts are disabled."
|
||||
msgstr "Ruang perangkat fisik. Kapasitas sebenarnya yang dapat digunakan tidak diketahui. Peringatan penggunaan disk pool dinonaktifkan."
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
msgstr "Silakan <0>konfigurasi server SMTP</0> untuk memastikan peringatan dikirimkan."
|
||||
@@ -1379,11 +1383,11 @@ msgstr "Silakan lihat <0>dokumentasi</0> untuk instruksi."
|
||||
msgid "Please sign in to your account"
|
||||
msgstr "Silakan masuk ke akun anda"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Pool Health"
|
||||
msgstr "Kesehatan pool"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Pool Usage"
|
||||
msgstr "Penggunaan pool"
|
||||
|
||||
@@ -1416,7 +1420,7 @@ msgstr "Proses dimulai"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Public key"
|
||||
msgstr ""
|
||||
msgstr "Kunci publik"
|
||||
|
||||
#. Use 'Key' if your language requires many more characters
|
||||
#: src/components/add-system.tsx
|
||||
@@ -1432,10 +1436,21 @@ msgstr "Kedalaman Antrian"
|
||||
msgid "Quiet Hours"
|
||||
msgstr "Jam Tenang"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Raw"
|
||||
msgstr "Mentah"
|
||||
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Raw usage of storage pool {displayName}"
|
||||
msgstr "Penggunaan mentah pool penyimpanan {displayName}"
|
||||
|
||||
#. Disk read
|
||||
#. Disk read
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -1443,7 +1458,7 @@ msgstr "Jam Tenang"
|
||||
msgid "Read"
|
||||
msgstr "Baca"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Read errors"
|
||||
msgstr "Kesalahan baca"
|
||||
|
||||
@@ -1454,7 +1469,7 @@ msgstr "Diterima"
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Refresh"
|
||||
msgstr "Muat ulang"
|
||||
|
||||
@@ -1643,7 +1658,7 @@ msgstr "Waktu Mulai"
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "Status"
|
||||
@@ -1671,7 +1686,7 @@ msgstr "Penggunaan Swap"
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Switch theme"
|
||||
msgstr ""
|
||||
msgstr "Ganti tema"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
@@ -1690,7 +1705,7 @@ msgstr "Sistem"
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
msgid "System fan speeds (RPM)"
|
||||
msgstr ""
|
||||
msgstr "Kecepatan kipas sistem (RPM)"
|
||||
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "System load averages over time"
|
||||
@@ -1772,9 +1787,9 @@ msgstr "Ini akan menghapus secara permanen semua record yang dipilih dari databa
|
||||
msgid "Throughput of {extraFsName}"
|
||||
msgstr "Throughput dari {extraFsName}"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Throughput of ZFS pool {poolName}"
|
||||
msgstr "Throughput pool ZFS {poolName}"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Throughput of storage pool {displayName}"
|
||||
msgstr "Laju transfer pool penyimpanan {displayName}"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Time format"
|
||||
@@ -1819,7 +1834,7 @@ msgstr "Total data yang dikirim untuk setiap antarmuka"
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
msgctxt "Disk I/O"
|
||||
msgid "Total time spent on read/write (can exceed 100%)"
|
||||
msgstr ""
|
||||
msgstr "Total waktu yang dihabiskan untuk baca/tulis (dapat melebihi 100%)"
|
||||
|
||||
#. placeholder {0}: data.length
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
@@ -1897,6 +1912,7 @@ msgstr "Dipicu ketika penggunaan disk apa pun melebihi ambang batas"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Type"
|
||||
msgstr "Tipe"
|
||||
|
||||
@@ -1919,7 +1935,7 @@ msgid "Universal token"
|
||||
msgstr "Token universal"
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Unknown"
|
||||
msgstr "Tidak diketahui"
|
||||
@@ -1945,7 +1961,7 @@ msgstr "Perbarui"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Updated"
|
||||
msgstr "Diperbarui"
|
||||
@@ -1968,20 +1984,20 @@ msgstr "Waktu aktif"
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Usage"
|
||||
msgstr "Penggunaan"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Usage of ZFS pool {poolName}"
|
||||
msgstr "Penggunaan pool ZFS {poolName}"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Usage of storage pool {displayName}"
|
||||
msgstr "Penggunaan pool penyimpanan {displayName}"
|
||||
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Used"
|
||||
msgstr "Digunakan"
|
||||
|
||||
@@ -2058,7 +2074,7 @@ msgstr "Perintah Windows"
|
||||
#. Disk write
|
||||
#. Disk write
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -2066,7 +2082,7 @@ msgstr "Perintah Windows"
|
||||
msgid "Write"
|
||||
msgstr "Tulis"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Write errors"
|
||||
msgstr "Kesalahan tulis"
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: it\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-09-02 21:43\n"
|
||||
"PO-Revision-Date: 2026-09-10 00:33\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Italian\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -61,7 +61,7 @@ msgstr "1 ora"
|
||||
#. Load average
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "1 min"
|
||||
msgstr "1 min"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 minute"
|
||||
@@ -78,7 +78,7 @@ msgstr "12 ore"
|
||||
#. Load average
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "15 min"
|
||||
msgstr "15 min"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "24 hours"
|
||||
@@ -91,13 +91,13 @@ msgstr "30 giorni"
|
||||
#. Load average
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "5 min"
|
||||
msgstr "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/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Actions"
|
||||
msgstr "Azioni"
|
||||
@@ -196,7 +196,7 @@ msgstr "Sei sicuro?"
|
||||
msgid "Automatic copy requires a secure context."
|
||||
msgstr "La copia automatica richiede un contesto sicuro."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Disk space available"
|
||||
msgid "Available"
|
||||
msgstr "Disponibile"
|
||||
@@ -338,7 +338,7 @@ msgid "Capabilities"
|
||||
msgstr "Funzionalità"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Capacity"
|
||||
msgstr "Capacità"
|
||||
|
||||
@@ -348,7 +348,7 @@ msgstr "Attenzione - possibile perdita di dati"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Celsius (°C)"
|
||||
msgstr "Celsius (°C)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change display units for metrics."
|
||||
@@ -391,14 +391,14 @@ msgstr "Controlla il tuo servizio di monitoraggio"
|
||||
msgid "Check your notification service"
|
||||
msgstr "Controlla il tuo servizio di notifica"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Checksum errors"
|
||||
msgstr "Errori di checksum"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Clear"
|
||||
msgstr "Cancella"
|
||||
@@ -411,7 +411,7 @@ msgstr "Fare clic su un contenitore per visualizzare ulteriori informazioni."
|
||||
msgid "Click on a device to view more information."
|
||||
msgstr "Fare clic su un dispositivo per visualizzare più informazioni."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Click on a pool to view vdev and dataset details."
|
||||
msgstr "Fai clic su un pool per visualizzare i dettagli di vdev e dataset."
|
||||
|
||||
@@ -447,7 +447,7 @@ msgstr "La connessione è interrotta"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Container"
|
||||
msgstr "Container"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Container Health"
|
||||
@@ -503,7 +503,7 @@ msgstr "Copia nome"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Copy public key"
|
||||
msgstr ""
|
||||
msgstr "Copia chiave pubblica"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
@@ -530,7 +530,7 @@ msgstr "Interne"
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "CPU"
|
||||
msgstr "CPU"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "CPU Cores"
|
||||
@@ -679,7 +679,7 @@ msgstr "Utilizzo del disco di {extraFsName}"
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgctxt "Layout display options"
|
||||
msgid "Display"
|
||||
msgstr "Display"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/charts/cpu-charts.tsx
|
||||
msgid "Docker CPU Usage"
|
||||
@@ -732,7 +732,7 @@ msgstr "Modifica {foo}"
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
#: src/components/login/otp-forms.tsx
|
||||
msgid "Email"
|
||||
msgstr "Email"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Email notifications"
|
||||
@@ -826,7 +826,7 @@ msgstr "Esporta la configurazione attuale dei tuoi sistemi."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Fahrenheit (°F)"
|
||||
msgstr "Fahrenheit (°F)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Failed"
|
||||
@@ -869,14 +869,14 @@ msgstr "Fallito: {0}"
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
msgid "Fans"
|
||||
msgstr ""
|
||||
msgstr "Ventole"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system/chart-card.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Filter..."
|
||||
@@ -888,7 +888,7 @@ msgstr "Impronta digitale"
|
||||
|
||||
#: 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}}"
|
||||
@@ -898,8 +898,8 @@ msgstr "Per <0>{min}</0> {min, plural, one {minuto} other {minuti}}"
|
||||
msgid "Forgot password?"
|
||||
msgstr "Password dimenticata?"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Free space"
|
||||
msgid "Free"
|
||||
msgstr "Libero"
|
||||
@@ -928,7 +928,7 @@ msgstr "Globale"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "GPU"
|
||||
msgstr "GPU"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
msgid "GPU Engines"
|
||||
@@ -948,13 +948,13 @@ msgid "Grid"
|
||||
msgstr "Griglia"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Health"
|
||||
msgstr "Stato"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Heartbeat"
|
||||
msgstr "Heartbeat"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat Monitoring"
|
||||
@@ -972,7 +972,7 @@ msgstr "Comando Homebrew"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Host / IP"
|
||||
msgstr "Host / IP"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP Method"
|
||||
@@ -1113,7 +1113,7 @@ msgstr "Istruzioni di configurazione manuale"
|
||||
#. Chart select field. Please try to keep this short.
|
||||
#: src/components/routes/system/chart-card.tsx
|
||||
msgid "Max 1 min"
|
||||
msgstr "Max 1 min"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
@@ -1146,7 +1146,7 @@ msgstr "Utilizzo della memoria dei container"
|
||||
msgid "Model"
|
||||
msgstr "Modello"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Mountpoint"
|
||||
msgstr "Punto di montaggio"
|
||||
|
||||
@@ -1183,9 +1183,9 @@ msgstr "Unità rete"
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "No"
|
||||
msgstr "No"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "No detail data for this pool."
|
||||
msgstr "Nessun dato dettagliato disponibile per questo pool."
|
||||
|
||||
@@ -1212,7 +1212,7 @@ msgstr "Nessun attributo S.M.A.R.T. disponibile per questo dispositivo."
|
||||
msgid "No systems found."
|
||||
msgstr "Nessun sistema trovato."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "None"
|
||||
msgstr "Nessuno"
|
||||
|
||||
@@ -1255,7 +1255,7 @@ msgstr "Password monouso"
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Open menu"
|
||||
msgstr "Apri menu"
|
||||
@@ -1291,7 +1291,7 @@ msgstr "Pagine / Impostazioni"
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Password"
|
||||
msgstr "Password"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Password must be at least 8 characters."
|
||||
@@ -1346,6 +1346,10 @@ msgstr "Permanente"
|
||||
msgid "Persistence"
|
||||
msgstr "Persistenza"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
msgid "Physical device space. True usable capacity is unknown. Pool disk usage alerts are disabled."
|
||||
msgstr "Spazio fisico del dispositivo. La capacità effettiva utilizzabile è sconosciuta. Gli avvisi di utilizzo del disco del pool sono disabilitati."
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
msgstr "Si prega di <0>configurare un server SMTP</0> per garantire la consegna degli avvisi."
|
||||
@@ -1379,11 +1383,11 @@ msgstr "Si prega di consultare <0>la documentazione</0> per le istruzioni."
|
||||
msgid "Please sign in to your account"
|
||||
msgstr "Si prega di accedere al proprio account"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Pool Health"
|
||||
msgstr "Stato del pool"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Pool Usage"
|
||||
msgstr "Utilizzo del pool"
|
||||
|
||||
@@ -1416,7 +1420,7 @@ msgstr "Processo avviato"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Public key"
|
||||
msgstr ""
|
||||
msgstr "Chiave pubblica"
|
||||
|
||||
#. Use 'Key' if your language requires many more characters
|
||||
#: src/components/add-system.tsx
|
||||
@@ -1432,10 +1436,21 @@ msgstr "Profondità coda"
|
||||
msgid "Quiet Hours"
|
||||
msgstr "Ore silenziose"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Raw"
|
||||
msgstr "Grezzo"
|
||||
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Raw usage of storage pool {displayName}"
|
||||
msgstr "Utilizzo grezzo del pool di archiviazione {displayName}"
|
||||
|
||||
#. Disk read
|
||||
#. Disk read
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -1443,7 +1458,7 @@ msgstr "Ore silenziose"
|
||||
msgid "Read"
|
||||
msgstr "Lettura"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Read errors"
|
||||
msgstr "Errori di lettura"
|
||||
|
||||
@@ -1454,7 +1469,7 @@ msgstr "Ricevuto"
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Refresh"
|
||||
msgstr "Aggiorna"
|
||||
|
||||
@@ -1501,7 +1516,7 @@ msgstr "Riprendi"
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgctxt "Root disk label"
|
||||
msgid "Root"
|
||||
msgstr "Root"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Rotate token"
|
||||
@@ -1643,7 +1658,7 @@ msgstr "Ora di inizio"
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "Stato"
|
||||
@@ -1671,7 +1686,7 @@ msgstr "Utilizzo Swap"
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Switch theme"
|
||||
msgstr ""
|
||||
msgstr "Cambia tema"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
@@ -1690,7 +1705,7 @@ msgstr "Sistema"
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
msgid "System fan speeds (RPM)"
|
||||
msgstr ""
|
||||
msgstr "Velocità delle ventole di sistema (RPM)"
|
||||
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "System load averages over time"
|
||||
@@ -1742,7 +1757,7 @@ msgstr "Temperature dei sensori di sistema"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Test <0>URL</0>"
|
||||
msgstr "Test <0>URL</0>"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Test heartbeat"
|
||||
@@ -1772,9 +1787,9 @@ msgstr "Questo eliminerà permanentemente tutti i record selezionati dal databas
|
||||
msgid "Throughput of {extraFsName}"
|
||||
msgstr "Throughput di {extraFsName}"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Throughput of ZFS pool {poolName}"
|
||||
msgstr "Throughput del pool ZFS {poolName}"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Throughput of storage pool {displayName}"
|
||||
msgstr "Throughput del pool di archiviazione {displayName}"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Time format"
|
||||
@@ -1787,7 +1802,7 @@ msgstr "A email(s)"
|
||||
#: 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
|
||||
@@ -1819,7 +1834,7 @@ msgstr "Dati totali inviati per ogni interfaccia"
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
msgctxt "Disk I/O"
|
||||
msgid "Total time spent on read/write (can exceed 100%)"
|
||||
msgstr ""
|
||||
msgstr "Tempo totale dedicato a lettura/scrittura (può superare il 100%)"
|
||||
|
||||
#. placeholder {0}: data.length
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
@@ -1897,6 +1912,7 @@ msgstr "Attiva quando l'utilizzo di un disco supera una soglia"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Type"
|
||||
msgstr "Tipo"
|
||||
|
||||
@@ -1919,7 +1935,7 @@ msgid "Universal token"
|
||||
msgstr "Token universale"
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Unknown"
|
||||
msgstr "Sconosciuta"
|
||||
@@ -1945,7 +1961,7 @@ msgstr "Aggiorna"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Updated"
|
||||
msgstr "Aggiornato"
|
||||
@@ -1968,20 +1984,20 @@ msgstr "Tempo di attività"
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Usage"
|
||||
msgstr "Utilizzo"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Usage of ZFS pool {poolName}"
|
||||
msgstr "Utilizzo del pool ZFS {poolName}"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Usage of storage pool {displayName}"
|
||||
msgstr "Utilizzo del pool di archiviazione {displayName}"
|
||||
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Used"
|
||||
msgstr "Utilizzato"
|
||||
|
||||
@@ -2058,7 +2074,7 @@ msgstr "Comando Windows"
|
||||
#. Disk write
|
||||
#. Disk write
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -2066,7 +2082,7 @@ msgstr "Comando Windows"
|
||||
msgid "Write"
|
||||
msgstr "Scrittura"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Write errors"
|
||||
msgstr "Errori di scrittura"
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: ja\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-09-02 21:43\n"
|
||||
"PO-Revision-Date: 2026-09-10 00:33\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Japanese\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
@@ -97,7 +97,7 @@ msgstr "5分"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Actions"
|
||||
msgstr "アクション"
|
||||
@@ -196,7 +196,7 @@ msgstr "よろしいですか?"
|
||||
msgid "Automatic copy requires a secure context."
|
||||
msgstr "自動コピーには安全なコンテキストが必要です。"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Disk space available"
|
||||
msgid "Available"
|
||||
msgstr "利用可能"
|
||||
@@ -338,7 +338,7 @@ msgid "Capabilities"
|
||||
msgstr "機能"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Capacity"
|
||||
msgstr "容量"
|
||||
|
||||
@@ -391,14 +391,14 @@ msgstr "監視サービスを確認する"
|
||||
msgid "Check your notification service"
|
||||
msgstr "通知サービスを確認してください"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Checksum errors"
|
||||
msgstr "チェックサムエラー"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Clear"
|
||||
msgstr "クリア"
|
||||
@@ -411,7 +411,7 @@ msgstr "詳細情報を表示するにはコンテナをクリックしてくだ
|
||||
msgid "Click on a device to view more information."
|
||||
msgstr "詳細情報を表示するにはデバイスをクリックしてください。"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Click on a pool to view vdev and dataset details."
|
||||
msgstr "プールをクリックして、vdev とデータセットの詳細を表示します。"
|
||||
|
||||
@@ -503,7 +503,7 @@ msgstr "名前をコピーする"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Copy public key"
|
||||
msgstr ""
|
||||
msgstr "公開鍵をコピー"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
@@ -869,14 +869,14 @@ msgstr "失敗: {0}"
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
msgid "Fans"
|
||||
msgstr ""
|
||||
msgstr "ファン"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system/chart-card.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Filter..."
|
||||
@@ -898,8 +898,8 @@ msgstr "<0>{min}</0> {min, plural, one {分} other {分}}の間"
|
||||
msgid "Forgot password?"
|
||||
msgstr "パスワードをお忘れですか?"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Free space"
|
||||
msgid "Free"
|
||||
msgstr "空き"
|
||||
@@ -948,7 +948,7 @@ msgid "Grid"
|
||||
msgstr "グリッド"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Health"
|
||||
msgstr "ヘルス"
|
||||
|
||||
@@ -1146,7 +1146,7 @@ msgstr "コンテナのメモリ使用量"
|
||||
msgid "Model"
|
||||
msgstr "モデル"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Mountpoint"
|
||||
msgstr "マウントポイント"
|
||||
|
||||
@@ -1185,7 +1185,7 @@ msgstr "ネットワーク単位"
|
||||
msgid "No"
|
||||
msgstr "いいえ"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "No detail data for this pool."
|
||||
msgstr "このプールの詳細データはありません。"
|
||||
|
||||
@@ -1212,7 +1212,7 @@ msgstr "このデバイスのS.M.A.R.T.属性は利用できません。"
|
||||
msgid "No systems found."
|
||||
msgstr "システムが見つかりませんでした。"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "None"
|
||||
msgstr "なし"
|
||||
|
||||
@@ -1255,7 +1255,7 @@ msgstr "ワンタイムパスワード"
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Open menu"
|
||||
msgstr "メニューを開く"
|
||||
@@ -1346,6 +1346,10 @@ msgstr "永久"
|
||||
msgid "Persistence"
|
||||
msgstr "永続性"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
msgid "Physical device space. True usable capacity is unknown. Pool disk usage alerts are disabled."
|
||||
msgstr "物理デバイス容量。実際に使用可能な容量は不明です。プールのディスク使用量アラートは無効になっています。"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
msgstr "アラートが配信されるように<0>SMTPサーバーを設定</0>してください。"
|
||||
@@ -1379,11 +1383,11 @@ msgstr "手順については<0>ドキュメント</0>を参照してくださ
|
||||
msgid "Please sign in to your account"
|
||||
msgstr "アカウントにサインインしてください"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Pool Health"
|
||||
msgstr "プールの健全性"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Pool Usage"
|
||||
msgstr "プール使用量"
|
||||
|
||||
@@ -1416,7 +1420,7 @@ msgstr "プロセス開始"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Public key"
|
||||
msgstr ""
|
||||
msgstr "公開鍵"
|
||||
|
||||
#. Use 'Key' if your language requires many more characters
|
||||
#: src/components/add-system.tsx
|
||||
@@ -1432,10 +1436,21 @@ msgstr "キューの深さ"
|
||||
msgid "Quiet Hours"
|
||||
msgstr "サイレント時間"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Raw"
|
||||
msgstr "生"
|
||||
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Raw usage of storage pool {displayName}"
|
||||
msgstr "ストレージプール {displayName} の生使用量"
|
||||
|
||||
#. Disk read
|
||||
#. Disk read
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -1443,7 +1458,7 @@ msgstr "サイレント時間"
|
||||
msgid "Read"
|
||||
msgstr "読み取り"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Read errors"
|
||||
msgstr "読み取りエラー"
|
||||
|
||||
@@ -1454,7 +1469,7 @@ msgstr "受信"
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Refresh"
|
||||
msgstr "更新"
|
||||
|
||||
@@ -1643,7 +1658,7 @@ msgstr "開始時間"
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "状態"
|
||||
@@ -1671,7 +1686,7 @@ msgstr "スワップ使用量"
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Switch theme"
|
||||
msgstr ""
|
||||
msgstr "テーマを切り替え"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
@@ -1690,7 +1705,7 @@ msgstr "システム"
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
msgid "System fan speeds (RPM)"
|
||||
msgstr ""
|
||||
msgstr "システムファン速度 (RPM)"
|
||||
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "System load averages over time"
|
||||
@@ -1772,9 +1787,9 @@ msgstr "これにより、選択したすべてのレコードがデータベー
|
||||
msgid "Throughput of {extraFsName}"
|
||||
msgstr "{extraFsName}のスループット"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Throughput of ZFS pool {poolName}"
|
||||
msgstr "ZFS プール {poolName} のスループット"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Throughput of storage pool {displayName}"
|
||||
msgstr "ストレージプール {displayName} のスループット"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Time format"
|
||||
@@ -1819,7 +1834,7 @@ msgstr "各インターフェースの総送信データ量"
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
msgctxt "Disk I/O"
|
||||
msgid "Total time spent on read/write (can exceed 100%)"
|
||||
msgstr ""
|
||||
msgstr "読み取り/書き込みに費やした合計時間 (100% を超える場合があります)"
|
||||
|
||||
#. placeholder {0}: data.length
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
@@ -1897,6 +1912,7 @@ msgstr "ディスクの使用量がしきい値を超えたときにトリガー
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Type"
|
||||
msgstr "タイプ"
|
||||
|
||||
@@ -1919,7 +1935,7 @@ msgid "Universal token"
|
||||
msgstr "ユニバーサルトークン"
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Unknown"
|
||||
msgstr "不明"
|
||||
@@ -1945,7 +1961,7 @@ msgstr "更新"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Updated"
|
||||
msgstr "更新済み"
|
||||
@@ -1968,20 +1984,20 @@ msgstr "稼働時間"
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Usage"
|
||||
msgstr "使用量"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Usage of ZFS pool {poolName}"
|
||||
msgstr "ZFS プール {poolName} の使用量"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Usage of storage pool {displayName}"
|
||||
msgstr "ストレージプール {displayName} の使用量"
|
||||
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Used"
|
||||
msgstr "使用中"
|
||||
|
||||
@@ -2058,7 +2074,7 @@ msgstr "Windows コマンド"
|
||||
#. Disk write
|
||||
#. Disk write
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -2066,7 +2082,7 @@ msgstr "Windows コマンド"
|
||||
msgid "Write"
|
||||
msgstr "書き込み"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Write errors"
|
||||
msgstr "書き込みエラー"
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: ko\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-09-02 21:43\n"
|
||||
"PO-Revision-Date: 2026-09-10 00:33\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Korean\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
@@ -48,7 +48,7 @@ msgstr "{count, plural, one {{countString} 분} few {{countString} 분} many {{c
|
||||
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
msgid "{diskName} I/O"
|
||||
msgstr "{diskName} I/O"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "{threads, plural, one {# thread} other {# threads}}"
|
||||
@@ -97,7 +97,7 @@ msgstr "5분"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Actions"
|
||||
msgstr "작업"
|
||||
@@ -196,7 +196,7 @@ msgstr "확실합니까?"
|
||||
msgid "Automatic copy requires a secure context."
|
||||
msgstr "자동 복사는 안전한 컨텍스트가 필요합니다."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Disk space available"
|
||||
msgid "Available"
|
||||
msgstr "사용 가능"
|
||||
@@ -338,7 +338,7 @@ msgid "Capabilities"
|
||||
msgstr "권한"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Capacity"
|
||||
msgstr "용량"
|
||||
|
||||
@@ -391,14 +391,14 @@ msgstr "모니터링 서비스 확인"
|
||||
msgid "Check your notification service"
|
||||
msgstr "알림 서비스를 확인하세요."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Checksum errors"
|
||||
msgstr "체크섬 오류"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Clear"
|
||||
msgstr "지우기"
|
||||
@@ -411,7 +411,7 @@ msgstr "더 많은 정보를 보려면 컨테이너를 클릭하세요."
|
||||
msgid "Click on a device to view more information."
|
||||
msgstr "더 많은 정보를 보려면 장치를 클릭하세요."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Click on a pool to view vdev and dataset details."
|
||||
msgstr "풀을 클릭하여 vdev 및 데이터 세트 세부 정보를 확인하세요."
|
||||
|
||||
@@ -503,7 +503,7 @@ msgstr "이름 복사"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Copy public key"
|
||||
msgstr ""
|
||||
msgstr "공개 키 복사"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
@@ -530,7 +530,7 @@ msgstr "코어"
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "CPU"
|
||||
msgstr "CPU"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "CPU Cores"
|
||||
@@ -869,14 +869,14 @@ msgstr "실패: {0}"
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
msgid "Fans"
|
||||
msgstr ""
|
||||
msgstr "팬"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system/chart-card.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Filter..."
|
||||
@@ -898,8 +898,8 @@ msgstr "<0>{min}</0> {min, plural, one {분} other {분}} 동안"
|
||||
msgid "Forgot password?"
|
||||
msgstr "비밀번호를 잊으셨나요?"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Free space"
|
||||
msgid "Free"
|
||||
msgstr "여유 공간"
|
||||
@@ -948,7 +948,7 @@ msgid "Grid"
|
||||
msgstr "그리드"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Health"
|
||||
msgstr "상태"
|
||||
|
||||
@@ -1146,7 +1146,7 @@ msgstr "컨테이너 메모리 사용량"
|
||||
msgid "Model"
|
||||
msgstr "모델"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Mountpoint"
|
||||
msgstr "마운트 지점"
|
||||
|
||||
@@ -1185,7 +1185,7 @@ msgstr "네트워크 단위"
|
||||
msgid "No"
|
||||
msgstr "아니오"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "No detail data for this pool."
|
||||
msgstr "이 풀에 대한 상세 데이터가 없습니다."
|
||||
|
||||
@@ -1212,7 +1212,7 @@ msgstr "이 장치에 사용할 수 있는 S.M.A.R.T. 속성이 없습니다."
|
||||
msgid "No systems found."
|
||||
msgstr "시스템을 찾을 수 없습니다."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "None"
|
||||
msgstr "없음"
|
||||
|
||||
@@ -1255,7 +1255,7 @@ msgstr "OTP"
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Open menu"
|
||||
msgstr "메뉴 열기"
|
||||
@@ -1346,6 +1346,10 @@ msgstr "영구적"
|
||||
msgid "Persistence"
|
||||
msgstr "지속성"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
msgid "Physical device space. True usable capacity is unknown. Pool disk usage alerts are disabled."
|
||||
msgstr "물리적 장치 공간. 실제 사용 가능한 용량을 알 수 없습니다. 풀 디스크 사용량 알림이 비활성화되어 있습니다."
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
msgstr "알림이 전달되도록 <0>SMTP 서버를 구성</0>하세요."
|
||||
@@ -1379,11 +1383,11 @@ msgstr "사용법은 <0>문서</0>를 참조하세요."
|
||||
msgid "Please sign in to your account"
|
||||
msgstr "계정에 로그인하세요."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Pool Health"
|
||||
msgstr "풀 상태"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Pool Usage"
|
||||
msgstr "풀 사용량"
|
||||
|
||||
@@ -1416,7 +1420,7 @@ msgstr "프로세스 시작됨"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Public key"
|
||||
msgstr ""
|
||||
msgstr "공개 키"
|
||||
|
||||
#. Use 'Key' if your language requires many more characters
|
||||
#: src/components/add-system.tsx
|
||||
@@ -1432,10 +1436,21 @@ msgstr "대기열 깊이"
|
||||
msgid "Quiet Hours"
|
||||
msgstr "조용한 시간"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Raw"
|
||||
msgstr "원시"
|
||||
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Raw usage of storage pool {displayName}"
|
||||
msgstr "스토리지 풀 {displayName}의 원시 사용량"
|
||||
|
||||
#. Disk read
|
||||
#. Disk read
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -1443,7 +1458,7 @@ msgstr "조용한 시간"
|
||||
msgid "Read"
|
||||
msgstr "읽기"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Read errors"
|
||||
msgstr "읽기 오류"
|
||||
|
||||
@@ -1454,7 +1469,7 @@ msgstr "수신됨"
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Refresh"
|
||||
msgstr "새로고침"
|
||||
|
||||
@@ -1643,7 +1658,7 @@ msgstr "시작 시간"
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "상태"
|
||||
@@ -1671,7 +1686,7 @@ msgstr "스왑 사용량"
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Switch theme"
|
||||
msgstr ""
|
||||
msgstr "테마 전환"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
@@ -1690,7 +1705,7 @@ msgstr "시스템"
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
msgid "System fan speeds (RPM)"
|
||||
msgstr ""
|
||||
msgstr "시스템 팬 속도 (RPM)"
|
||||
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "System load averages over time"
|
||||
@@ -1772,9 +1787,9 @@ msgstr "선택한 모든 레코드를 데이터베이스에서 영구적으로
|
||||
msgid "Throughput of {extraFsName}"
|
||||
msgstr "{extraFsName}의 처리량"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Throughput of ZFS pool {poolName}"
|
||||
msgstr "ZFS 풀 {poolName} 처리량"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Throughput of storage pool {displayName}"
|
||||
msgstr "스토리지 풀 {displayName}의 처리량"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Time format"
|
||||
@@ -1819,7 +1834,7 @@ msgstr "각 인터페이스별 총합 업로드 데이터량"
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
msgctxt "Disk I/O"
|
||||
msgid "Total time spent on read/write (can exceed 100%)"
|
||||
msgstr ""
|
||||
msgstr "읽기/쓰기에 소요된 총 시간 (100%를 초과할 수 있음)"
|
||||
|
||||
#. placeholder {0}: data.length
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
@@ -1897,6 +1912,7 @@ msgstr "디스크 사용량이 임계값을 초과할 때 트리거됩니다."
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Type"
|
||||
msgstr "유형"
|
||||
|
||||
@@ -1919,7 +1935,7 @@ msgid "Universal token"
|
||||
msgstr "범용 토큰"
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Unknown"
|
||||
msgstr "알 수 없음"
|
||||
@@ -1945,7 +1961,7 @@ msgstr "업데이트"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Updated"
|
||||
msgstr "업데이트됨"
|
||||
@@ -1968,20 +1984,20 @@ msgstr "가동시간"
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Usage"
|
||||
msgstr "사용량"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Usage of ZFS pool {poolName}"
|
||||
msgstr "ZFS 풀 {poolName} 사용량"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Usage of storage pool {displayName}"
|
||||
msgstr "스토리지 풀 {displayName}의 사용량"
|
||||
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Used"
|
||||
msgstr "사용됨"
|
||||
|
||||
@@ -2058,7 +2074,7 @@ msgstr "Windows 명령어"
|
||||
#. Disk write
|
||||
#. Disk write
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -2066,7 +2082,7 @@ msgstr "Windows 명령어"
|
||||
msgid "Write"
|
||||
msgstr "쓰기"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Write errors"
|
||||
msgstr "쓰기 오류"
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: nl\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-09-02 21:43\n"
|
||||
"PO-Revision-Date: 2026-09-10 00:33\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Dutch\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -52,7 +52,7 @@ msgstr "I/O van {diskName}"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "{threads, plural, one {# thread} other {# threads}}"
|
||||
msgstr "{threads, plural, one {# thread} other {# threads}}"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 hour"
|
||||
@@ -69,7 +69,7 @@ msgstr "1 minuut"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 week"
|
||||
msgstr "1 week"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "12 hours"
|
||||
@@ -97,7 +97,7 @@ msgstr "5 minuten"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Actions"
|
||||
msgstr "Acties"
|
||||
@@ -154,7 +154,7 @@ msgstr "Start na het instellen van de omgevingsvariabelen je Beszel-hub opnieuw
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Agent"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
@@ -196,7 +196,7 @@ msgstr "Weet je het zeker?"
|
||||
msgid "Automatic copy requires a secure context."
|
||||
msgstr "Automatisch kopiëren vereist een veilige context."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Disk space available"
|
||||
msgid "Available"
|
||||
msgstr "Beschikbaar"
|
||||
@@ -258,7 +258,7 @@ msgstr "Bandbreedte"
|
||||
#. Battery label in systems table header
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Bat"
|
||||
msgstr "Bat"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
@@ -300,7 +300,7 @@ msgstr "Binair"
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bits (Kbps, Mbps, Gbps)"
|
||||
msgstr "Bits (Kbps, Mbps, Gbps)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Boot state"
|
||||
@@ -309,11 +309,11 @@ msgstr "Opstartstatus"
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bytes (KB/s, MB/s, GB/s)"
|
||||
msgstr "Bytes (KB/s, MB/s, GB/s)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
msgid "Cache / Buffers"
|
||||
msgstr "Cache / Buffers"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Can reload"
|
||||
@@ -338,7 +338,7 @@ msgid "Capabilities"
|
||||
msgstr "Mogelijkheden"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Capacity"
|
||||
msgstr "Capaciteit"
|
||||
|
||||
@@ -348,7 +348,7 @@ msgstr "Opgelet - potentieel gegevensverlies"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Celsius (°C)"
|
||||
msgstr "Celsius (°C)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change display units for metrics."
|
||||
@@ -391,14 +391,14 @@ msgstr "Controleer je monitoringservice"
|
||||
msgid "Check your notification service"
|
||||
msgstr "Controleer je meldingsservice"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Checksum errors"
|
||||
msgstr "Checksumfouten"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Clear"
|
||||
msgstr "Wissen"
|
||||
@@ -411,7 +411,7 @@ msgstr "Klik op een container om meer informatie te zien."
|
||||
msgid "Click on a device to view more information."
|
||||
msgstr "Klik op een apparaat om meer informatie te bekijken."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Click on a pool to view vdev and dataset details."
|
||||
msgstr "Klik op een pool om details over vdevs en datasets te bekijken."
|
||||
|
||||
@@ -447,7 +447,7 @@ msgstr "Verbinding is niet actief"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Container"
|
||||
msgstr "Container"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Container Health"
|
||||
@@ -455,7 +455,7 @@ msgstr "Containergezondheid"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Containers"
|
||||
msgstr "Containers"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
@@ -530,7 +530,7 @@ msgstr "Kern"
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "CPU"
|
||||
msgstr "CPU"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "CPU Cores"
|
||||
@@ -826,7 +826,7 @@ msgstr "Exporteer je huidige systeemconfiguratie."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Fahrenheit (°F)"
|
||||
msgstr "Fahrenheit (°F)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Failed"
|
||||
@@ -875,8 +875,8 @@ msgstr "Ventilatoren"
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system/chart-card.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Filter..."
|
||||
@@ -888,7 +888,7 @@ msgstr "Vingerafdruk"
|
||||
|
||||
#: 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}}"
|
||||
@@ -898,8 +898,8 @@ msgstr "Voor <0>{min}</0> {min, plural, one {minuut} other {minuten}}"
|
||||
msgid "Forgot password?"
|
||||
msgstr "Wachtwoord vergeten?"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Free space"
|
||||
msgid "Free"
|
||||
msgstr "Vrij"
|
||||
@@ -928,7 +928,7 @@ msgstr "Globaal"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "GPU"
|
||||
msgstr "GPU"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
msgid "GPU Engines"
|
||||
@@ -948,13 +948,13 @@ msgid "Grid"
|
||||
msgstr "Raster"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Health"
|
||||
msgstr "Gezondheid"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Heartbeat"
|
||||
msgstr "Heartbeat"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat Monitoring"
|
||||
@@ -1009,7 +1009,7 @@ msgstr "Als je het wachtwoord voor je beheerdersaccount bent kwijtgeraakt, kan j
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
msgctxt "Docker image"
|
||||
msgid "Image"
|
||||
msgstr "Image"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Inactive"
|
||||
@@ -1017,7 +1017,7 @@ msgstr "Inactief"
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Interval"
|
||||
msgstr "Interval"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Invalid email address."
|
||||
@@ -1113,7 +1113,7 @@ msgstr "Handmatige installatie-instructies"
|
||||
#. Chart select field. Please try to keep this short.
|
||||
#: src/components/routes/system/chart-card.tsx
|
||||
msgid "Max 1 min"
|
||||
msgstr "Max 1 min"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
@@ -1144,9 +1144,9 @@ msgstr "Geheugengebruik van containers"
|
||||
#. Device model
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Model"
|
||||
msgstr "Model"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Mountpoint"
|
||||
msgstr "Koppelpunt"
|
||||
|
||||
@@ -1185,7 +1185,7 @@ msgstr "Netwerk eenheid"
|
||||
msgid "No"
|
||||
msgstr "Nee"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "No detail data for this pool."
|
||||
msgstr "Geen gedetailleerde gegevens beschikbaar voor deze pool."
|
||||
|
||||
@@ -1212,7 +1212,7 @@ msgstr "Geen S.M.A.R.T. kenmerken beschikbaar voor dit apparaat."
|
||||
msgid "No systems found."
|
||||
msgstr "Geen systemen gevonden."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "None"
|
||||
msgstr "Geen"
|
||||
|
||||
@@ -1255,7 +1255,7 @@ msgstr "Eenmalig wachtwoord"
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Open menu"
|
||||
msgstr "Menu openen"
|
||||
@@ -1346,6 +1346,10 @@ msgstr "Blijvend"
|
||||
msgid "Persistence"
|
||||
msgstr "Persistentie"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
msgid "Physical device space. True usable capacity is unknown. Pool disk usage alerts are disabled."
|
||||
msgstr "Fysieke apparatuurruimte. De werkelijk bruikbare capaciteit is onbekend. Waarschuwingen voor pool-schijfgebruik zijn uitgeschakeld."
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
msgstr "<0>Configureer een SMTP-server </0> om ervoor te zorgen dat waarschuwingen worden afgeleverd."
|
||||
@@ -1379,11 +1383,11 @@ msgstr "Bekijk <0>de documentatie</0> voor instructies."
|
||||
msgid "Please sign in to your account"
|
||||
msgstr "Meld je aan bij je account"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Pool Health"
|
||||
msgstr "Poolstatus"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Pool Usage"
|
||||
msgstr "Poolgebruik"
|
||||
|
||||
@@ -1432,10 +1436,21 @@ msgstr "Wachtrijdiepte"
|
||||
msgid "Quiet Hours"
|
||||
msgstr "Stille uren"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Raw"
|
||||
msgstr "Ruw"
|
||||
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Raw usage of storage pool {displayName}"
|
||||
msgstr "Ruw gebruik van opslagpool {displayName}"
|
||||
|
||||
#. Disk read
|
||||
#. Disk read
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -1443,7 +1458,7 @@ msgstr "Stille uren"
|
||||
msgid "Read"
|
||||
msgstr "Lezen"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Read errors"
|
||||
msgstr "Leesfouten"
|
||||
|
||||
@@ -1454,7 +1469,7 @@ msgstr "Ontvangen"
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Refresh"
|
||||
msgstr "Vernieuwen"
|
||||
|
||||
@@ -1501,7 +1516,7 @@ msgstr "Hervatten"
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgctxt "Root disk label"
|
||||
msgid "Root"
|
||||
msgstr "Root"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Rotate token"
|
||||
@@ -1599,7 +1614,7 @@ msgstr "Servicedetails"
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Services"
|
||||
msgstr "Services"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
@@ -1643,7 +1658,7 @@ msgstr "Starttijd"
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "Status"
|
||||
@@ -1654,7 +1669,7 @@ msgstr "Status"
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Status"
|
||||
msgstr "Status"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Sub State"
|
||||
@@ -1742,7 +1757,7 @@ msgstr "Temperatuur van systeem sensoren"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Test <0>URL</0>"
|
||||
msgstr "Test <0>URL</0>"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Test heartbeat"
|
||||
@@ -1772,9 +1787,9 @@ msgstr "Dit zal alle geselecteerde records verwijderen uit de database."
|
||||
msgid "Throughput of {extraFsName}"
|
||||
msgstr "Doorvoer van {extraFsName}"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Throughput of ZFS pool {poolName}"
|
||||
msgstr "Doorvoer van ZFS-pool {poolName}"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Throughput of storage pool {displayName}"
|
||||
msgstr "Doorvoer van opslagpool {displayName}"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Time format"
|
||||
@@ -1787,7 +1802,7 @@ msgstr "Naar e-mail(s)"
|
||||
#: 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
|
||||
@@ -1832,7 +1847,7 @@ msgstr "Geactiveerd door"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Triggers"
|
||||
msgstr "Triggers"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Triggers when 1 minute load average exceeds a threshold"
|
||||
@@ -1897,8 +1912,9 @@ msgstr "Triggert wanneer het gebruik van een schijf een drempelwaarde overschrij
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Type"
|
||||
msgstr "Type"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Unhealthy"
|
||||
@@ -1919,7 +1935,7 @@ msgid "Universal token"
|
||||
msgstr "Universele token"
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Unknown"
|
||||
msgstr "Onbekend"
|
||||
@@ -1945,7 +1961,7 @@ msgstr "Bijwerken"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Updated"
|
||||
msgstr "Bijgewerkt"
|
||||
@@ -1968,20 +1984,20 @@ msgstr "Actief"
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Usage"
|
||||
msgstr "Gebruik"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Usage of ZFS pool {poolName}"
|
||||
msgstr "Gebruik van ZFS-pool {poolName}"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Usage of storage pool {displayName}"
|
||||
msgstr "Gebruik van opslagpool {displayName}"
|
||||
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Used"
|
||||
msgstr "Gebruikt"
|
||||
|
||||
@@ -2058,7 +2074,7 @@ msgstr "Windows-commando"
|
||||
#. Disk write
|
||||
#. Disk write
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -2066,7 +2082,7 @@ msgstr "Windows-commando"
|
||||
msgid "Write"
|
||||
msgstr "Schrijven"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Write errors"
|
||||
msgstr "Schrijffouten"
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: no\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-09-02 21:43\n"
|
||||
"PO-Revision-Date: 2026-09-10 00:33\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Norwegian\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -61,7 +61,7 @@ msgstr "1 time"
|
||||
#. Load average
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "1 min"
|
||||
msgstr "1 min"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 minute"
|
||||
@@ -78,7 +78,7 @@ msgstr "12 timer"
|
||||
#. Load average
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "15 min"
|
||||
msgstr "15 min"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "24 hours"
|
||||
@@ -91,13 +91,13 @@ msgstr "30 dager"
|
||||
#. Load average
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "5 min"
|
||||
msgstr "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/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Actions"
|
||||
msgstr "Handlinger"
|
||||
@@ -142,7 +142,7 @@ msgstr "Juster bredden på hovedlayouten"
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Admin"
|
||||
msgstr "Admin"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "After"
|
||||
@@ -154,7 +154,7 @@ msgstr "Etter å ha angitt miljøvariablene, start Beszel-huben på nytt for at
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Agent"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
@@ -196,7 +196,7 @@ msgstr "Er du sikker?"
|
||||
msgid "Automatic copy requires a secure context."
|
||||
msgstr "Automatisk kopiering krever en sikker kontekst."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Disk space available"
|
||||
msgid "Available"
|
||||
msgstr "Tilgjengelig"
|
||||
@@ -300,7 +300,7 @@ msgstr "Binær"
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bits (Kbps, Mbps, Gbps)"
|
||||
msgstr "Bits (Kbps, Mbps, Gbps)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Boot state"
|
||||
@@ -309,7 +309,7 @@ msgstr "Oppstartstilstand"
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bytes (KB/s, MB/s, GB/s)"
|
||||
msgstr "Bytes (KB/s, MB/s, GB/s)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
msgid "Cache / Buffers"
|
||||
@@ -338,7 +338,7 @@ msgid "Capabilities"
|
||||
msgstr "Kapabiliteter"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Capacity"
|
||||
msgstr "Kapasitet"
|
||||
|
||||
@@ -348,7 +348,7 @@ msgstr "Advarsel - potensielt tap av data"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Celsius (°C)"
|
||||
msgstr "Celsius (°C)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change display units for metrics."
|
||||
@@ -391,14 +391,14 @@ msgstr "Sjekk overvåkingstjenesten din"
|
||||
msgid "Check your notification service"
|
||||
msgstr "Sjekk din meldingstjeneste"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Checksum errors"
|
||||
msgstr "Kontrollsumfeil"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Clear"
|
||||
msgstr "Tøm"
|
||||
@@ -411,7 +411,7 @@ msgstr "Klikk på en container for å se mer informasjon."
|
||||
msgid "Click on a device to view more information."
|
||||
msgstr "Klikk på en enhet for å se mer informasjon."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Click on a pool to view vdev and dataset details."
|
||||
msgstr "Klikk på en pool for å se detaljer om vdev-er og datasett."
|
||||
|
||||
@@ -447,7 +447,7 @@ msgstr "Tilkoblingen er nede"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Container"
|
||||
msgstr "Container"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Container Health"
|
||||
@@ -503,7 +503,7 @@ msgstr "Kopier navn"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Copy public key"
|
||||
msgstr ""
|
||||
msgstr "Kopier offentlig nøkkel"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
@@ -530,7 +530,7 @@ msgstr "Kjerne"
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "CPU"
|
||||
msgstr "CPU"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "CPU Cores"
|
||||
@@ -661,7 +661,7 @@ msgstr "Lader ut"
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Disk"
|
||||
msgstr "Disk"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Disk unit"
|
||||
@@ -826,7 +826,7 @@ msgstr "Eksporter din nåværende systemkonfigurasjon"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Fahrenheit (°F)"
|
||||
msgstr "Fahrenheit (°F)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Failed"
|
||||
@@ -869,18 +869,18 @@ msgstr "Mislyktes: {0}"
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
msgid "Fans"
|
||||
msgstr ""
|
||||
msgstr "Vifter"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system/chart-card.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Filter..."
|
||||
msgstr "Filter..."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Fingerprint"
|
||||
@@ -898,8 +898,8 @@ msgstr "I <0>{min}</0> {min, plural, one {minutt} other {minutter}}"
|
||||
msgid "Forgot password?"
|
||||
msgstr "Glemt passord?"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Free space"
|
||||
msgid "Free"
|
||||
msgstr "Ledig"
|
||||
@@ -924,11 +924,11 @@ msgstr "Generelt"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Global"
|
||||
msgstr "Global"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "GPU"
|
||||
msgstr "GPU"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
msgid "GPU Engines"
|
||||
@@ -948,7 +948,7 @@ msgid "Grid"
|
||||
msgstr "Rutenett"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Health"
|
||||
msgstr "Helse"
|
||||
|
||||
@@ -1009,7 +1009,7 @@ msgstr "Dersom du har mistet passordet til admin-kontoen kan du nullstille det m
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
msgctxt "Docker image"
|
||||
msgid "Image"
|
||||
msgstr "Image"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Inactive"
|
||||
@@ -1146,7 +1146,7 @@ msgstr "Minnebruk for containere"
|
||||
msgid "Model"
|
||||
msgstr "Modell"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Mountpoint"
|
||||
msgstr "Monteringspunkt"
|
||||
|
||||
@@ -1185,7 +1185,7 @@ msgstr "Nettverksenhet"
|
||||
msgid "No"
|
||||
msgstr "Nei"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "No detail data for this pool."
|
||||
msgstr "Ingen detaljerte data er tilgjengelige for denne poolen."
|
||||
|
||||
@@ -1212,7 +1212,7 @@ msgstr "Ingen S.M.A.R.T.-attributter tilgjengelig for denne enheten."
|
||||
msgid "No systems found."
|
||||
msgstr "Ingen systemer funnet."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "None"
|
||||
msgstr "Ingen"
|
||||
|
||||
@@ -1255,7 +1255,7 @@ msgstr "Engangspassord"
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Open menu"
|
||||
msgstr "Åpne meny"
|
||||
@@ -1311,7 +1311,7 @@ msgstr "Fortid"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Pause"
|
||||
msgstr "Pause"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Paused"
|
||||
@@ -1340,12 +1340,16 @@ msgstr "Prosentandel av tid brukt i hver tilstand"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Permanent"
|
||||
msgstr "Permanent"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Persistence"
|
||||
msgstr "Vedvarenhet"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
msgid "Physical device space. True usable capacity is unknown. Pool disk usage alerts are disabled."
|
||||
msgstr "Fysisk enhetsplass. Den faktiske brukbare kapasiteten er ukjent. Varsler om diskbruk for poolen er deaktivert."
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
msgstr "Vennligst <0>konfigurer en SMTP-server</0> for å forsikre deg om at varsler blir levert."
|
||||
@@ -1379,17 +1383,17 @@ msgstr "Vennligst se <0>dokumentasjonen</0> for instrukser."
|
||||
msgid "Please sign in to your account"
|
||||
msgstr "Vennligst logg inn på kontoen din"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Pool Health"
|
||||
msgstr "Pooltilstand"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Pool Usage"
|
||||
msgstr "Poolbruk"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Port"
|
||||
msgstr "Port"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
msgctxt "Container ports"
|
||||
@@ -1416,7 +1420,7 @@ msgstr "Prosess startet"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Public key"
|
||||
msgstr ""
|
||||
msgstr "Offentlig nøkkel"
|
||||
|
||||
#. Use 'Key' if your language requires many more characters
|
||||
#: src/components/add-system.tsx
|
||||
@@ -1432,10 +1436,21 @@ msgstr "Kødybde"
|
||||
msgid "Quiet Hours"
|
||||
msgstr "Stille timer"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Raw"
|
||||
msgstr "Rå"
|
||||
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Raw usage of storage pool {displayName}"
|
||||
msgstr "Rå bruk av lagringspool {displayName}"
|
||||
|
||||
#. Disk read
|
||||
#. Disk read
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -1443,7 +1458,7 @@ msgstr "Stille timer"
|
||||
msgid "Read"
|
||||
msgstr "Lesing"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Read errors"
|
||||
msgstr "Lesefeil"
|
||||
|
||||
@@ -1454,7 +1469,7 @@ msgstr "Mottatt"
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Refresh"
|
||||
msgstr "Oppdater"
|
||||
|
||||
@@ -1643,7 +1658,7 @@ msgstr "Starttid"
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "Tilstand"
|
||||
@@ -1654,7 +1669,7 @@ msgstr "Tilstand"
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Status"
|
||||
msgstr "Status"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Sub State"
|
||||
@@ -1671,7 +1686,7 @@ msgstr "Swap-bruk"
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Switch theme"
|
||||
msgstr ""
|
||||
msgstr "Bytt tema"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
@@ -1686,11 +1701,11 @@ msgstr ""
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "System"
|
||||
msgstr "System"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
msgid "System fan speeds (RPM)"
|
||||
msgstr ""
|
||||
msgstr "Systemviftehastigheter (RPM)"
|
||||
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "System load averages over time"
|
||||
@@ -1725,7 +1740,7 @@ msgstr "Oppgaver"
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Temp"
|
||||
msgstr "Temp"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
#: src/lib/alerts.ts
|
||||
@@ -1742,7 +1757,7 @@ msgstr "Temperaturer på system-sensorer"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Test <0>URL</0>"
|
||||
msgstr "Test <0>URL</0>"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Test heartbeat"
|
||||
@@ -1772,9 +1787,9 @@ msgstr "Dette vil permanent slette alle valgte oppføringer fra databasen."
|
||||
msgid "Throughput of {extraFsName}"
|
||||
msgstr "Gjennomstrømning av {extraFsName}"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Throughput of ZFS pool {poolName}"
|
||||
msgstr "Gjennomstrømning for ZFS-pool {poolName}"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Throughput of storage pool {displayName}"
|
||||
msgstr "Gjennomstrømning av lagringspool {displayName}"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Time format"
|
||||
@@ -1787,7 +1802,7 @@ msgstr "Til e-postadresse(r)"
|
||||
#: 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
|
||||
@@ -1897,8 +1912,9 @@ msgstr "Slår inn når forbruk av hvilken som helst disk overstiger en grensever
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Type"
|
||||
msgstr "Type"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Unhealthy"
|
||||
@@ -1916,10 +1932,10 @@ msgstr "Enhetspreferanser"
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Universal token"
|
||||
msgstr "Universal token"
|
||||
msgstr ""
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Unknown"
|
||||
msgstr "Ukjent"
|
||||
@@ -1945,7 +1961,7 @@ msgstr "Oppdater"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Updated"
|
||||
msgstr "Oppdatert"
|
||||
@@ -1968,20 +1984,20 @@ msgstr "Oppetid"
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Usage"
|
||||
msgstr "Forbruk"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Usage of ZFS pool {poolName}"
|
||||
msgstr "Bruk av ZFS-pool {poolName}"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Usage of storage pool {displayName}"
|
||||
msgstr "Bruk av lagringspool {displayName}"
|
||||
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Used"
|
||||
msgstr "Brukt"
|
||||
|
||||
@@ -2058,7 +2074,7 @@ msgstr "Windows-kommando"
|
||||
#. Disk write
|
||||
#. Disk write
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -2066,7 +2082,7 @@ msgstr "Windows-kommando"
|
||||
msgid "Write"
|
||||
msgstr "Skriving"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Write errors"
|
||||
msgstr "Skrivefeil"
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: pl\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-09-02 21:43\n"
|
||||
"PO-Revision-Date: 2026-09-10 00:33\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Polish\n"
|
||||
"Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n"
|
||||
@@ -61,7 +61,7 @@ msgstr "1 godzina"
|
||||
#. Load average
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "1 min"
|
||||
msgstr "1 min"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 minute"
|
||||
@@ -78,7 +78,7 @@ msgstr "12 godzin"
|
||||
#. Load average
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "15 min"
|
||||
msgstr "15 min"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "24 hours"
|
||||
@@ -91,13 +91,13 @@ msgstr "30 dni"
|
||||
#. Load average
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "5 min"
|
||||
msgstr "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/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Actions"
|
||||
msgstr "Akcje"
|
||||
@@ -142,7 +142,7 @@ msgstr "Dostosuj szerokość widoku"
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Admin"
|
||||
msgstr "Admin"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "After"
|
||||
@@ -154,7 +154,7 @@ msgstr "Po ustawieniu zmiennych środowiskowych zrestartuj Beszel hub, aby zmian
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Agent"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
@@ -196,7 +196,7 @@ msgstr "Czy jesteś pewien?"
|
||||
msgid "Automatic copy requires a secure context."
|
||||
msgstr "Automatyczne kopiowanie wymaga bezpiecznego kontekstu."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Disk space available"
|
||||
msgid "Available"
|
||||
msgstr "Dostępne"
|
||||
@@ -338,7 +338,7 @@ msgid "Capabilities"
|
||||
msgstr "Możliwości"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Capacity"
|
||||
msgstr "Pojemność"
|
||||
|
||||
@@ -391,14 +391,14 @@ msgstr "Sprawdź usługę monitorowania"
|
||||
msgid "Check your notification service"
|
||||
msgstr "Sprawdź swój serwis powiadomień"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Checksum errors"
|
||||
msgstr "Błędy sumy kontrolnej"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Clear"
|
||||
msgstr "Wyczyść"
|
||||
@@ -411,7 +411,7 @@ msgstr "Wybierz kontener, aby wyświetlić więcej informacji."
|
||||
msgid "Click on a device to view more information."
|
||||
msgstr "Wybierz urządzenie, aby wyświetlić więcej informacji."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Click on a pool to view vdev and dataset details."
|
||||
msgstr "Kliknij pulę, aby wyświetlić szczegóły vdevów i zestawów danych."
|
||||
|
||||
@@ -503,7 +503,7 @@ msgstr "Kopiuj nazwę"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Copy public key"
|
||||
msgstr ""
|
||||
msgstr "Kopiuj klucz publiczny"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
@@ -826,7 +826,7 @@ msgstr "Eksportuj aktualną konfigurację systemów."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Fahrenheit (°F)"
|
||||
msgstr "Fahrenheit (°F)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Failed"
|
||||
@@ -869,14 +869,14 @@ msgstr "Nieudane: {0}"
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
msgid "Fans"
|
||||
msgstr ""
|
||||
msgstr "Wentylatory"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system/chart-card.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Filter..."
|
||||
@@ -898,8 +898,8 @@ msgstr "Na <0>{min}</0> {min, plural, one {minutę} other {minut}}"
|
||||
msgid "Forgot password?"
|
||||
msgstr "Zapomniałeś hasła?"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Free space"
|
||||
msgid "Free"
|
||||
msgstr "Wolne"
|
||||
@@ -928,7 +928,7 @@ msgstr "Globalny"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "GPU"
|
||||
msgstr "GPU"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
msgid "GPU Engines"
|
||||
@@ -948,13 +948,13 @@ msgid "Grid"
|
||||
msgstr "Siatka"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Health"
|
||||
msgstr "Kondycja"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Heartbeat"
|
||||
msgstr "Heartbeat"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat Monitoring"
|
||||
@@ -1042,7 +1042,7 @@ msgstr "Cykl życia"
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "limit"
|
||||
msgstr "limit"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "Load Average"
|
||||
@@ -1144,9 +1144,9 @@ msgstr "Zużycie pamięci przez kontenery"
|
||||
#. Device model
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Model"
|
||||
msgstr "Model"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Mountpoint"
|
||||
msgstr "Punkt montowania"
|
||||
|
||||
@@ -1185,7 +1185,7 @@ msgstr "Jednostka sieciowa"
|
||||
msgid "No"
|
||||
msgstr "Nie"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "No detail data for this pool."
|
||||
msgstr "Brak szczegółowych danych dla tej puli."
|
||||
|
||||
@@ -1212,7 +1212,7 @@ msgstr "Brak dostępnych atrybutów S.M.A.R.T. dla tego urządzenia."
|
||||
msgid "No systems found."
|
||||
msgstr "Nie znaleziono systemów."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "None"
|
||||
msgstr "Brak"
|
||||
|
||||
@@ -1255,7 +1255,7 @@ msgstr "Hasło jednorazowe"
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Open menu"
|
||||
msgstr "Otwórz menu"
|
||||
@@ -1346,6 +1346,10 @@ msgstr "Stały"
|
||||
msgid "Persistence"
|
||||
msgstr "Trwałość"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
msgid "Physical device space. True usable capacity is unknown. Pool disk usage alerts are disabled."
|
||||
msgstr "Fizyczne miejsce na urządzeniu. Rzeczywista pojemność użytkowa jest nieznana. Alerty użycia dysków puli są wyłączone."
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
msgstr "Proszę <0>skonfigurować serwer SMTP</0>, aby zapewnić dostarczanie powiadomień."
|
||||
@@ -1379,17 +1383,17 @@ msgstr "Proszę zapoznać się z <0>dokumentacją</0>."
|
||||
msgid "Please sign in to your account"
|
||||
msgstr "Zaloguj się na swoje konto"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Pool Health"
|
||||
msgstr "Stan puli"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Pool Usage"
|
||||
msgstr "Użycie puli"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Port"
|
||||
msgstr "Port"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
msgctxt "Container ports"
|
||||
@@ -1416,7 +1420,7 @@ msgstr "Proces uruchomiony"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Public key"
|
||||
msgstr ""
|
||||
msgstr "Klucz publiczny"
|
||||
|
||||
#. Use 'Key' if your language requires many more characters
|
||||
#: src/components/add-system.tsx
|
||||
@@ -1432,10 +1436,21 @@ msgstr "Głębokość kolejki"
|
||||
msgid "Quiet Hours"
|
||||
msgstr "Godziny ciszy"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Raw"
|
||||
msgstr "Surowe"
|
||||
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Raw usage of storage pool {displayName}"
|
||||
msgstr "Surowe użycie puli magazynu {displayName}"
|
||||
|
||||
#. Disk read
|
||||
#. Disk read
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -1443,7 +1458,7 @@ msgstr "Godziny ciszy"
|
||||
msgid "Read"
|
||||
msgstr "Odczyt"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Read errors"
|
||||
msgstr "Błędy odczytu"
|
||||
|
||||
@@ -1454,7 +1469,7 @@ msgstr "Otrzymane"
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Refresh"
|
||||
msgstr "Odśwież"
|
||||
|
||||
@@ -1501,7 +1516,7 @@ msgstr "Wznów"
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgctxt "Root disk label"
|
||||
msgid "Root"
|
||||
msgstr "Root"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Rotate token"
|
||||
@@ -1643,7 +1658,7 @@ msgstr "Czas rozpoczęcia"
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "Stan"
|
||||
@@ -1654,7 +1669,7 @@ msgstr "Stan"
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Status"
|
||||
msgstr "Status"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Sub State"
|
||||
@@ -1671,7 +1686,7 @@ msgstr "Użycie pamięci wymiany"
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Switch theme"
|
||||
msgstr ""
|
||||
msgstr "Zmień motyw"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
@@ -1686,11 +1701,11 @@ msgstr ""
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "System"
|
||||
msgstr "System"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
msgid "System fan speeds (RPM)"
|
||||
msgstr ""
|
||||
msgstr "Prędkości wentylatorów systemu (RPM)"
|
||||
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "System load averages over time"
|
||||
@@ -1742,7 +1757,7 @@ msgstr "Temperatury czujników systemowych."
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Test <0>URL</0>"
|
||||
msgstr "Test <0>URL</0>"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Test heartbeat"
|
||||
@@ -1772,9 +1787,9 @@ msgstr "Spowoduje to trwałe usunięcie wszystkich zaznaczonych rekordów z bazy
|
||||
msgid "Throughput of {extraFsName}"
|
||||
msgstr "Przepustowość {extraFsName}"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Throughput of ZFS pool {poolName}"
|
||||
msgstr "Przepustowość puli ZFS {poolName}"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Throughput of storage pool {displayName}"
|
||||
msgstr "Przepustowość puli magazynu {displayName}"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Time format"
|
||||
@@ -1787,7 +1802,7 @@ msgstr "Do e-mail(ów)"
|
||||
#: 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
|
||||
@@ -1897,6 +1912,7 @@ msgstr "Wyzwalane, gdy wykorzystanie któregokolwiek dysku przekroczy ustalony p
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Type"
|
||||
msgstr "Typ"
|
||||
|
||||
@@ -1919,7 +1935,7 @@ msgid "Universal token"
|
||||
msgstr "Uniwersalny token"
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Unknown"
|
||||
msgstr "Nieznana"
|
||||
@@ -1945,7 +1961,7 @@ msgstr "Aktualizuj"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Updated"
|
||||
msgstr "Zaktualizowano"
|
||||
@@ -1968,20 +1984,20 @@ msgstr "Czas pracy"
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Usage"
|
||||
msgstr "Wykorzystanie"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Usage of ZFS pool {poolName}"
|
||||
msgstr "Użycie puli ZFS {poolName}"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Usage of storage pool {displayName}"
|
||||
msgstr "Użycie puli magazynu {displayName}"
|
||||
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Used"
|
||||
msgstr "Używane"
|
||||
|
||||
@@ -2058,7 +2074,7 @@ msgstr "Polecenie Windows"
|
||||
#. Disk write
|
||||
#. Disk write
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -2066,7 +2082,7 @@ msgstr "Polecenie Windows"
|
||||
msgid "Write"
|
||||
msgstr "Zapis"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Write errors"
|
||||
msgstr "Błędy zapisu"
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: pt\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-09-02 21:43\n"
|
||||
"PO-Revision-Date: 2026-09-10 00:33\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Portuguese\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -52,7 +52,7 @@ msgstr "E/S de {diskName}"
|
||||
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
msgid "{threads, plural, one {# thread} other {# threads}}"
|
||||
msgstr "{threads, plural, one {# thread} other {# threads}}"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 hour"
|
||||
@@ -61,7 +61,7 @@ msgstr "1 hora"
|
||||
#. Load average
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "1 min"
|
||||
msgstr "1 min"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 minute"
|
||||
@@ -78,7 +78,7 @@ msgstr "12 horas"
|
||||
#. Load average
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "15 min"
|
||||
msgstr "15 min"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "24 hours"
|
||||
@@ -91,13 +91,13 @@ msgstr "30 dias"
|
||||
#. Load average
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "5 min"
|
||||
msgstr "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/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Actions"
|
||||
msgstr "Ações"
|
||||
@@ -142,7 +142,7 @@ msgstr "Ajustar a largura do layout principal"
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Admin"
|
||||
msgstr "Admin"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "After"
|
||||
@@ -196,7 +196,7 @@ msgstr "Tem certeza?"
|
||||
msgid "Automatic copy requires a secure context."
|
||||
msgstr "A cópia automática requer um contexto seguro."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Disk space available"
|
||||
msgid "Available"
|
||||
msgstr "Disponível"
|
||||
@@ -258,7 +258,7 @@ msgstr "Largura de Banda"
|
||||
#. Battery label in systems table header
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Bat"
|
||||
msgstr "Bat"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
@@ -300,7 +300,7 @@ msgstr "Binário"
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bits (Kbps, Mbps, Gbps)"
|
||||
msgstr "Bits (Kbps, Mbps, Gbps)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Boot state"
|
||||
@@ -309,11 +309,11 @@ msgstr "Estado de inicialização"
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bytes (KB/s, MB/s, GB/s)"
|
||||
msgstr "Bytes (KB/s, MB/s, GB/s)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
msgid "Cache / Buffers"
|
||||
msgstr "Cache / Buffers"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Can reload"
|
||||
@@ -338,7 +338,7 @@ msgid "Capabilities"
|
||||
msgstr "Capacidades"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Capacity"
|
||||
msgstr "Capacidade"
|
||||
|
||||
@@ -348,7 +348,7 @@ msgstr "Cuidado - possível perda de dados"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Celsius (°C)"
|
||||
msgstr "Celsius (°C)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change display units for metrics."
|
||||
@@ -391,14 +391,14 @@ msgstr "Verifique o seu serviço de monitorização"
|
||||
msgid "Check your notification service"
|
||||
msgstr "Verifique seu serviço de notificação"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Checksum errors"
|
||||
msgstr "Erros de checksum"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Clear"
|
||||
msgstr "Limpar"
|
||||
@@ -411,7 +411,7 @@ msgstr "Clique num contentor para ver mais informações."
|
||||
msgid "Click on a device to view more information."
|
||||
msgstr "Clique em um dispositivo para ver mais informações."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Click on a pool to view vdev and dataset details."
|
||||
msgstr "Clique num pool para ver os detalhes de vdev e dataset."
|
||||
|
||||
@@ -503,7 +503,7 @@ msgstr "Copiar nome"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Copy public key"
|
||||
msgstr ""
|
||||
msgstr "Copiar chave pública"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
@@ -530,7 +530,7 @@ msgstr "Núcleos"
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "CPU"
|
||||
msgstr "CPU"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "CPU Cores"
|
||||
@@ -732,7 +732,7 @@ msgstr "Editar {foo}"
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
#: src/components/login/otp-forms.tsx
|
||||
msgid "Email"
|
||||
msgstr "Email"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Email notifications"
|
||||
@@ -826,7 +826,7 @@ msgstr "Exporte a configuração atual dos seus sistemas."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Fahrenheit (°F)"
|
||||
msgstr "Fahrenheit (°F)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Failed"
|
||||
@@ -869,14 +869,14 @@ msgstr "Falhou: {0}"
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
msgid "Fans"
|
||||
msgstr ""
|
||||
msgstr "Ventoinhas"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system/chart-card.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Filter..."
|
||||
@@ -888,7 +888,7 @@ msgstr "Impressão digital"
|
||||
|
||||
#: 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}}"
|
||||
@@ -898,8 +898,8 @@ msgstr "Por <0>{min}</0> {min, plural, one {minuto} other {minutos}}"
|
||||
msgid "Forgot password?"
|
||||
msgstr "Esqueceu a senha?"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Free space"
|
||||
msgid "Free"
|
||||
msgstr "Livre"
|
||||
@@ -924,11 +924,11 @@ msgstr "Geral"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Global"
|
||||
msgstr "Global"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "GPU"
|
||||
msgstr "GPU"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
msgid "GPU Engines"
|
||||
@@ -948,7 +948,7 @@ msgid "Grid"
|
||||
msgstr "Grade"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Health"
|
||||
msgstr "Saúde"
|
||||
|
||||
@@ -972,7 +972,7 @@ msgstr "Comando Homebrew"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Host / IP"
|
||||
msgstr "Host / IP"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "HTTP Method"
|
||||
@@ -1091,7 +1091,7 @@ msgstr "Tentativa de login falhou"
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Logs"
|
||||
msgstr "Logs"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Looking instead for where to create alerts? Click the bell <0/> icons in the systems table."
|
||||
@@ -1146,7 +1146,7 @@ msgstr "Utilização de memória dos contentores"
|
||||
msgid "Model"
|
||||
msgstr "Modelo"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Mountpoint"
|
||||
msgstr "Ponto de montagem"
|
||||
|
||||
@@ -1185,7 +1185,7 @@ msgstr "Unidade de rede"
|
||||
msgid "No"
|
||||
msgstr "Não"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "No detail data for this pool."
|
||||
msgstr "Não existem dados detalhados para este pool."
|
||||
|
||||
@@ -1212,7 +1212,7 @@ msgstr "Nenhum atributo S.M.A.R.T. disponível para este dispositivo."
|
||||
msgid "No systems found."
|
||||
msgstr "Nenhum sistema encontrado."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "None"
|
||||
msgstr "Nenhum"
|
||||
|
||||
@@ -1255,7 +1255,7 @@ msgstr "Senha de uso único"
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Open menu"
|
||||
msgstr "Abrir menu"
|
||||
@@ -1346,6 +1346,10 @@ msgstr "Permanente"
|
||||
msgid "Persistence"
|
||||
msgstr "Persistência"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
msgid "Physical device space. True usable capacity is unknown. Pool disk usage alerts are disabled."
|
||||
msgstr "Espaço físico do dispositivo. A capacidade útil real é desconhecida. Os alertas de utilização do disco do pool estão desativados."
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
msgstr "Por favor, <0>configure um servidor SMTP</0> para garantir que os alertas sejam entregues."
|
||||
@@ -1379,11 +1383,11 @@ msgstr "Por favor, veja <0>a documentação</0> para instruções."
|
||||
msgid "Please sign in to your account"
|
||||
msgstr "Por favor, entre na sua conta"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Pool Health"
|
||||
msgstr "Estado do pool"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Pool Usage"
|
||||
msgstr "Utilização do pool"
|
||||
|
||||
@@ -1416,7 +1420,7 @@ msgstr "Processo iniciado"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Public key"
|
||||
msgstr ""
|
||||
msgstr "Chave pública"
|
||||
|
||||
#. Use 'Key' if your language requires many more characters
|
||||
#: src/components/add-system.tsx
|
||||
@@ -1432,10 +1436,21 @@ msgstr "Profundidade da fila"
|
||||
msgid "Quiet Hours"
|
||||
msgstr "Horas Silenciosas"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Raw"
|
||||
msgstr "Bruto"
|
||||
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Raw usage of storage pool {displayName}"
|
||||
msgstr "Utilização bruta do pool de armazenamento {displayName}"
|
||||
|
||||
#. Disk read
|
||||
#. Disk read
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -1443,7 +1458,7 @@ msgstr "Horas Silenciosas"
|
||||
msgid "Read"
|
||||
msgstr "Ler"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Read errors"
|
||||
msgstr "Erros de leitura"
|
||||
|
||||
@@ -1454,7 +1469,7 @@ msgstr "Recebido"
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Refresh"
|
||||
msgstr "Atualizar"
|
||||
|
||||
@@ -1643,7 +1658,7 @@ msgstr "Hora de Início"
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "Estado"
|
||||
@@ -1671,7 +1686,7 @@ msgstr "Uso de Swap"
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Switch theme"
|
||||
msgstr ""
|
||||
msgstr "Mudar tema"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
@@ -1690,7 +1705,7 @@ msgstr "Sistema"
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
msgid "System fan speeds (RPM)"
|
||||
msgstr ""
|
||||
msgstr "Velocidades das ventoinhas do sistema (RPM)"
|
||||
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "System load averages over time"
|
||||
@@ -1725,7 +1740,7 @@ msgstr "Tarefas"
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Temp"
|
||||
msgstr "Temp"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
#: src/lib/alerts.ts
|
||||
@@ -1772,9 +1787,9 @@ msgstr "Isso excluirá permanentemente todos os registros selecionados do banco
|
||||
msgid "Throughput of {extraFsName}"
|
||||
msgstr "Taxa de transferência de {extraFsName}"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Throughput of ZFS pool {poolName}"
|
||||
msgstr "Débito do pool ZFS {poolName}"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Throughput of storage pool {displayName}"
|
||||
msgstr "Taxa de transferência do pool de armazenamento {displayName}"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Time format"
|
||||
@@ -1787,7 +1802,7 @@ msgstr "Para email(s)"
|
||||
#: 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
|
||||
@@ -1806,7 +1821,7 @@ msgstr "Tokens e impressões digitais são usados para autenticar conexões WebS
|
||||
#: src/components/ui/chart.tsx
|
||||
#: src/components/ui/chart.tsx
|
||||
msgid "Total"
|
||||
msgstr "Total"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/network-sheet.tsx
|
||||
msgid "Total data received for each interface"
|
||||
@@ -1824,7 +1839,7 @@ msgstr "Tempo total gasto em leitura/escrita (pode exceder 100%)"
|
||||
#. placeholder {0}: data.length
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Total: {0}"
|
||||
msgstr "Total: {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Triggered by"
|
||||
@@ -1897,6 +1912,7 @@ msgstr "Dispara quando o uso de qualquer disco excede um limite"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Type"
|
||||
msgstr "Tipo"
|
||||
|
||||
@@ -1919,7 +1935,7 @@ msgid "Universal token"
|
||||
msgstr "Token universal"
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Unknown"
|
||||
msgstr "Desconhecida"
|
||||
@@ -1945,7 +1961,7 @@ msgstr "Atualizar"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Updated"
|
||||
msgstr "Atualizado"
|
||||
@@ -1968,20 +1984,20 @@ msgstr "Tempo de Atividade"
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Usage"
|
||||
msgstr "Uso"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Usage of ZFS pool {poolName}"
|
||||
msgstr "Utilização do pool ZFS {poolName}"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Usage of storage pool {displayName}"
|
||||
msgstr "Utilização do pool de armazenamento {displayName}"
|
||||
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Used"
|
||||
msgstr "Usado"
|
||||
|
||||
@@ -2058,7 +2074,7 @@ msgstr "Comando Windows"
|
||||
#. Disk write
|
||||
#. Disk write
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -2066,7 +2082,7 @@ msgstr "Comando Windows"
|
||||
msgid "Write"
|
||||
msgstr "Escrever"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Write errors"
|
||||
msgstr "Erros de escrita"
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: ro\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-09-02 19:06\n"
|
||||
"PO-Revision-Date: 2026-09-10 00:33\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"
|
||||
@@ -61,7 +61,7 @@ msgstr "1 oră"
|
||||
#. Load average
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "1 min"
|
||||
msgstr "1 min"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 minute"
|
||||
@@ -78,7 +78,7 @@ msgstr "12 ore"
|
||||
#. Load average
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "15 min"
|
||||
msgstr "15 min"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "24 hours"
|
||||
@@ -91,13 +91,13 @@ msgstr "30 zile"
|
||||
#. Load average
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "5 min"
|
||||
msgstr "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/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Actions"
|
||||
msgstr "Acțiuni"
|
||||
@@ -142,7 +142,7 @@ msgstr "Reglaţi lăţimea aspectului principal"
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Admin"
|
||||
msgstr "Admin"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "After"
|
||||
@@ -154,7 +154,7 @@ msgstr "După setarea variabilelor de mediu, reporniţi centrul Beszel pentru ca
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Agent"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
@@ -196,7 +196,7 @@ msgstr "Ești sigur?"
|
||||
msgid "Automatic copy requires a secure context."
|
||||
msgstr "Copierea automată necesită un context securizat."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Disk space available"
|
||||
msgid "Available"
|
||||
msgstr ""
|
||||
@@ -338,7 +338,7 @@ msgid "Capabilities"
|
||||
msgstr "Capabilități"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Capacity"
|
||||
msgstr "Capacitate"
|
||||
|
||||
@@ -348,7 +348,7 @@ msgstr "Atenție - posibilă pierdere de date"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Celsius (°C)"
|
||||
msgstr "Celsius (°C)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change display units for metrics."
|
||||
@@ -391,14 +391,14 @@ msgstr ""
|
||||
msgid "Check your notification service"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Checksum errors"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Clear"
|
||||
msgstr ""
|
||||
@@ -411,7 +411,7 @@ msgstr ""
|
||||
msgid "Click on a device to view more information."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Click on a pool to view vdev and dataset details."
|
||||
msgstr ""
|
||||
|
||||
@@ -530,7 +530,7 @@ msgstr ""
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "CPU"
|
||||
msgstr "CPU"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "CPU Cores"
|
||||
@@ -732,7 +732,7 @@ msgstr "Editează {foo}"
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
#: src/components/login/otp-forms.tsx
|
||||
msgid "Email"
|
||||
msgstr "Email"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Email notifications"
|
||||
@@ -875,8 +875,8 @@ msgstr ""
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system/chart-card.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Filter..."
|
||||
@@ -898,8 +898,8 @@ msgstr ""
|
||||
msgid "Forgot password?"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Free space"
|
||||
msgid "Free"
|
||||
msgstr ""
|
||||
@@ -924,7 +924,7 @@ msgstr ""
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Global"
|
||||
msgstr "Global"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "GPU"
|
||||
@@ -948,7 +948,7 @@ msgid "Grid"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Health"
|
||||
msgstr ""
|
||||
|
||||
@@ -1144,9 +1144,9 @@ msgstr "Utilizarea memoriei de către containere"
|
||||
#. Device model
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
msgid "Model"
|
||||
msgstr "Model"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Mountpoint"
|
||||
msgstr ""
|
||||
|
||||
@@ -1185,7 +1185,7 @@ msgstr ""
|
||||
msgid "No"
|
||||
msgstr "Nu"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "No detail data for this pool."
|
||||
msgstr ""
|
||||
|
||||
@@ -1212,7 +1212,7 @@ msgstr ""
|
||||
msgid "No systems found."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "None"
|
||||
msgstr ""
|
||||
|
||||
@@ -1255,7 +1255,7 @@ msgstr ""
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Open menu"
|
||||
msgstr ""
|
||||
@@ -1346,6 +1346,10 @@ msgstr ""
|
||||
msgid "Persistence"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
msgid "Physical device space. True usable capacity is unknown. Pool disk usage alerts are disabled."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
msgstr ""
|
||||
@@ -1379,17 +1383,17 @@ msgstr ""
|
||||
msgid "Please sign in to your account"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Pool Health"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Pool Usage"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Port"
|
||||
msgstr "Port"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
msgctxt "Container ports"
|
||||
@@ -1432,10 +1436,21 @@ msgstr ""
|
||||
msgid "Quiet Hours"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Raw"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Raw usage of storage pool {displayName}"
|
||||
msgstr ""
|
||||
|
||||
#. Disk read
|
||||
#. Disk read
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -1443,7 +1458,7 @@ msgstr ""
|
||||
msgid "Read"
|
||||
msgstr "Citit"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Read errors"
|
||||
msgstr ""
|
||||
|
||||
@@ -1454,7 +1469,7 @@ msgstr "Primit"
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Refresh"
|
||||
msgstr "Actualizează"
|
||||
|
||||
@@ -1643,7 +1658,7 @@ msgstr ""
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "State"
|
||||
msgstr ""
|
||||
@@ -1772,8 +1787,8 @@ msgstr ""
|
||||
msgid "Throughput of {extraFsName}"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Throughput of ZFS pool {poolName}"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Throughput of storage pool {displayName}"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
@@ -1806,7 +1821,7 @@ msgstr ""
|
||||
#: src/components/ui/chart.tsx
|
||||
#: src/components/ui/chart.tsx
|
||||
msgid "Total"
|
||||
msgstr "Total"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/network-sheet.tsx
|
||||
msgid "Total data received for each interface"
|
||||
@@ -1824,7 +1839,7 @@ msgstr ""
|
||||
#. placeholder {0}: data.length
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Total: {0}"
|
||||
msgstr "Total: {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Triggered by"
|
||||
@@ -1897,6 +1912,7 @@ msgstr ""
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Type"
|
||||
msgstr "Tip"
|
||||
|
||||
@@ -1919,7 +1935,7 @@ msgid "Universal token"
|
||||
msgstr ""
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Unknown"
|
||||
msgstr ""
|
||||
@@ -1945,7 +1961,7 @@ msgstr ""
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Updated"
|
||||
msgstr ""
|
||||
@@ -1968,20 +1984,20 @@ msgstr ""
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Usage"
|
||||
msgstr "Utilizare"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Usage of ZFS pool {poolName}"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Usage of storage pool {displayName}"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Used"
|
||||
msgstr "Utilizat"
|
||||
|
||||
@@ -2058,7 +2074,7 @@ msgstr ""
|
||||
#. Disk write
|
||||
#. Disk write
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -2066,7 +2082,7 @@ msgstr ""
|
||||
msgid "Write"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Write errors"
|
||||
msgstr ""
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: ru\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-09-02 21:43\n"
|
||||
"PO-Revision-Date: 2026-09-10 00:33\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Russian\n"
|
||||
"Plural-Forms: nplurals=4; plural=((n%10==1 && n%100!=11) ? 0 : ((n%10 >= 2 && n%10 <=4 && (n%100 < 12 || n%100 > 14)) ? 1 : ((n%10 == 0 || (n%10 >= 5 && n%10 <=9)) || (n%100 >= 11 && n%100 <= 14)) ? 2 : 3));\n"
|
||||
@@ -97,7 +97,7 @@ msgstr "5 мин"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Actions"
|
||||
msgstr "Действия"
|
||||
@@ -196,7 +196,7 @@ msgstr "Вы уверены?"
|
||||
msgid "Automatic copy requires a secure context."
|
||||
msgstr "Автоматическое копирование требует безопасного контекста."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Disk space available"
|
||||
msgid "Available"
|
||||
msgstr "Доступно"
|
||||
@@ -234,7 +234,7 @@ msgstr "Среднее время от очереди до завершения
|
||||
|
||||
#: src/components/routes/system/charts/cpu-charts.tsx
|
||||
msgid "Average system-wide CPU utilization"
|
||||
msgstr "Среднее использование CPU по всей системе"
|
||||
msgstr "Среднее использование CPU в системе"
|
||||
|
||||
#. placeholder {0}: gpu.n
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
@@ -338,7 +338,7 @@ msgid "Capabilities"
|
||||
msgstr "Возможности"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Capacity"
|
||||
msgstr "Емкость"
|
||||
|
||||
@@ -391,14 +391,14 @@ msgstr "Проверьте ваш сервис мониторинга"
|
||||
msgid "Check your notification service"
|
||||
msgstr "Проверьте ваш сервис уведомлений"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Checksum errors"
|
||||
msgstr "Ошибки контрольной суммы"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Clear"
|
||||
msgstr "Очистить"
|
||||
@@ -411,7 +411,7 @@ msgstr "Нажмите на контейнер для просмотра доп
|
||||
msgid "Click on a device to view more information."
|
||||
msgstr "Нажмите на устройство для просмотра дополнительной информации."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Click on a pool to view vdev and dataset details."
|
||||
msgstr "Нажмите на пул, чтобы просмотреть сведения о vdev и dataset."
|
||||
|
||||
@@ -875,8 +875,8 @@ msgstr "Вентиляторы"
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system/chart-card.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Filter..."
|
||||
@@ -898,8 +898,8 @@ msgstr "На <0>{min}</0> {min, plural, one {минуту} other {минут}}"
|
||||
msgid "Forgot password?"
|
||||
msgstr "Забыли пароль?"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Free space"
|
||||
msgid "Free"
|
||||
msgstr "Свободно"
|
||||
@@ -928,11 +928,11 @@ msgstr "Глобально"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "GPU"
|
||||
msgstr "GPU"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
msgid "GPU Engines"
|
||||
msgstr "GPU движки"
|
||||
msgstr "Ядра GPU"
|
||||
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
msgid "GPU Power Draw"
|
||||
@@ -948,13 +948,13 @@ msgid "Grid"
|
||||
msgstr "Сетка"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Health"
|
||||
msgstr "Здоровье"
|
||||
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Heartbeat"
|
||||
msgstr "Heartbeat"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/heartbeat.tsx
|
||||
msgid "Heartbeat Monitoring"
|
||||
@@ -1146,7 +1146,7 @@ msgstr "Использование памяти контейнерами"
|
||||
msgid "Model"
|
||||
msgstr "Модель"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Mountpoint"
|
||||
msgstr "Точка монтирования"
|
||||
|
||||
@@ -1185,7 +1185,7 @@ msgstr "Единицы измерения скорости сети"
|
||||
msgid "No"
|
||||
msgstr "Нет"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "No detail data for this pool."
|
||||
msgstr "Подробные данные для этого пула отсутствуют."
|
||||
|
||||
@@ -1212,7 +1212,7 @@ msgstr "Для этого устройства нет доступных атр
|
||||
msgid "No systems found."
|
||||
msgstr "Системы не найдены."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "None"
|
||||
msgstr "Нет"
|
||||
|
||||
@@ -1255,7 +1255,7 @@ msgstr "Одноразовый пароль"
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Open menu"
|
||||
msgstr "Открыть меню"
|
||||
@@ -1346,6 +1346,10 @@ msgstr "Постоянный"
|
||||
msgid "Persistence"
|
||||
msgstr "Устойчивость"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
msgid "Physical device space. True usable capacity is unknown. Pool disk usage alerts are disabled."
|
||||
msgstr "Физическое пространство устройства. Реальная полезная ёмкость неизвестна. Оповещения об использовании дисков пула отключены."
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
msgstr "Пожалуйста, <0>настройте SMTP-сервер</0>, чтобы гарантировать доставку оповещений."
|
||||
@@ -1379,11 +1383,11 @@ msgstr "Пожалуйста, смотрите <0>документацию</0>
|
||||
msgid "Please sign in to your account"
|
||||
msgstr "Пожалуйста, войдите в свою учетную запись"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Pool Health"
|
||||
msgstr "Состояние пула"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Pool Usage"
|
||||
msgstr "Использование пула"
|
||||
|
||||
@@ -1404,7 +1408,7 @@ msgstr "Включение питания"
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
msgid "Precise utilization at the recorded time"
|
||||
msgstr "Точное использование в записанное время"
|
||||
msgstr "Использование по времени"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Preferred Language"
|
||||
@@ -1432,10 +1436,21 @@ msgstr "Глубина очереди"
|
||||
msgid "Quiet Hours"
|
||||
msgstr "Тихие часы"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Raw"
|
||||
msgstr "Сырое"
|
||||
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Raw usage of storage pool {displayName}"
|
||||
msgstr "Сырое использование пула хранения {displayName}"
|
||||
|
||||
#. Disk read
|
||||
#. Disk read
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -1443,7 +1458,7 @@ msgstr "Тихие часы"
|
||||
msgid "Read"
|
||||
msgstr "Чтение"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Read errors"
|
||||
msgstr "Ошибки чтения"
|
||||
|
||||
@@ -1454,7 +1469,7 @@ msgstr "Получено"
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Refresh"
|
||||
msgstr "Обновить"
|
||||
|
||||
@@ -1501,7 +1516,7 @@ msgstr "Возобновить"
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgctxt "Root disk label"
|
||||
msgid "Root"
|
||||
msgstr "Корневой"
|
||||
msgstr "Системный"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Rotate token"
|
||||
@@ -1643,7 +1658,7 @@ msgstr "Время начала"
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "Состояние"
|
||||
@@ -1662,7 +1677,7 @@ msgstr "Подсостояние"
|
||||
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
msgid "Swap space used by the system"
|
||||
msgstr "Используемое системой пространство подкачки"
|
||||
msgstr "Размер файла подкачки системы"
|
||||
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
msgid "Swap Usage"
|
||||
@@ -1694,7 +1709,7 @@ msgstr "Скорость вращения системных вентилято
|
||||
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "System load averages over time"
|
||||
msgstr "Средняя загрузка системы за время"
|
||||
msgstr "Средняя загрузка системы по времени"
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "Systemd Services"
|
||||
@@ -1738,7 +1753,7 @@ msgstr "Единицы измерения температуры"
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
msgid "Temperatures of system sensors"
|
||||
msgstr "Температуры датчиков системы"
|
||||
msgstr "Температура датчиков системы"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Test <0>URL</0>"
|
||||
@@ -1772,9 +1787,9 @@ msgstr "Это навсегда удалит все выбранные запи
|
||||
msgid "Throughput of {extraFsName}"
|
||||
msgstr "Пропускная способность {extraFsName}"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Throughput of ZFS pool {poolName}"
|
||||
msgstr "Пропускная способность ZFS-пула {poolName}"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Throughput of storage pool {displayName}"
|
||||
msgstr "Пропускная способность пула хранения {displayName}"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Time format"
|
||||
@@ -1897,6 +1912,7 @@ msgstr "Срабатывает, когда нагрузка на любой из
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Type"
|
||||
msgstr "Тип"
|
||||
|
||||
@@ -1919,7 +1935,7 @@ msgid "Universal token"
|
||||
msgstr "Универсальный токен"
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Unknown"
|
||||
msgstr "Неизвестно"
|
||||
@@ -1945,7 +1961,7 @@ msgstr "Обновить"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Updated"
|
||||
msgstr "Обновлено"
|
||||
@@ -1968,20 +1984,20 @@ msgstr "Время работы"
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Usage"
|
||||
msgstr "Использование"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Usage of ZFS pool {poolName}"
|
||||
msgstr "Использование ZFS-пула {poolName}"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Usage of storage pool {displayName}"
|
||||
msgstr "Использование пула хранения {displayName}"
|
||||
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Used"
|
||||
msgstr "Использовано"
|
||||
|
||||
@@ -2058,7 +2074,7 @@ msgstr "Команда Windows"
|
||||
#. Disk write
|
||||
#. Disk write
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -2066,7 +2082,7 @@ msgstr "Команда Windows"
|
||||
msgid "Write"
|
||||
msgstr "Запись"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Write errors"
|
||||
msgstr "Ошибки записи"
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: sl\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-09-02 21:43\n"
|
||||
"PO-Revision-Date: 2026-09-10 00:33\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Slovenian\n"
|
||||
"Plural-Forms: nplurals=4; plural=n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3;\n"
|
||||
@@ -61,7 +61,7 @@ msgstr "1 ura"
|
||||
#. Load average
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "1 min"
|
||||
msgstr "1 min"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 minute"
|
||||
@@ -78,7 +78,7 @@ msgstr "12 ur"
|
||||
#. Load average
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "15 min"
|
||||
msgstr "15 min"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "24 hours"
|
||||
@@ -91,13 +91,13 @@ msgstr "30 dni"
|
||||
#. Load average
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "5 min"
|
||||
msgstr "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/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Actions"
|
||||
msgstr "Dejanja"
|
||||
@@ -196,7 +196,7 @@ msgstr "Ali ste prepričani?"
|
||||
msgid "Automatic copy requires a secure context."
|
||||
msgstr "Za samodejno kopiranje je potreben varen kontekst."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Disk space available"
|
||||
msgid "Available"
|
||||
msgstr "Na voljo"
|
||||
@@ -338,7 +338,7 @@ msgid "Capabilities"
|
||||
msgstr "Zmožnosti"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Capacity"
|
||||
msgstr "Kapaciteta"
|
||||
|
||||
@@ -391,14 +391,14 @@ msgstr "Preverite svojo storitev za spremljanje"
|
||||
msgid "Check your notification service"
|
||||
msgstr "Preverite storitev obveščanja"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Checksum errors"
|
||||
msgstr "Napake kontrolne vsote"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Clear"
|
||||
msgstr "Počisti"
|
||||
@@ -411,7 +411,7 @@ msgstr "Kliknite na kontejner za več informacij."
|
||||
msgid "Click on a device to view more information."
|
||||
msgstr "Kliknite na napravo za več informacij."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Click on a pool to view vdev and dataset details."
|
||||
msgstr "Kliknite pool za ogled podrobnosti o vdev in dataset."
|
||||
|
||||
@@ -503,7 +503,7 @@ msgstr "Kopiraj ime"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Copy public key"
|
||||
msgstr ""
|
||||
msgstr "Kopiraj javni ključ"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
@@ -869,14 +869,14 @@ msgstr "Neuspešno: {0}"
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
msgid "Fans"
|
||||
msgstr ""
|
||||
msgstr "Ventilatorji"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system/chart-card.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Filter..."
|
||||
@@ -898,8 +898,8 @@ msgstr "Za <0>{min}</0> {min, plural, one {minuto} other {minut}}"
|
||||
msgid "Forgot password?"
|
||||
msgstr "Pozabljeno geslo?"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Free space"
|
||||
msgid "Free"
|
||||
msgstr "Prosto"
|
||||
@@ -948,7 +948,7 @@ msgid "Grid"
|
||||
msgstr "Mreža"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Health"
|
||||
msgstr "Zdravje"
|
||||
|
||||
@@ -1146,7 +1146,7 @@ msgstr "Poraba pomnilnika vsebnikov"
|
||||
msgid "Model"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Mountpoint"
|
||||
msgstr "Točka priklopa"
|
||||
|
||||
@@ -1185,7 +1185,7 @@ msgstr "Enota omrežja"
|
||||
msgid "No"
|
||||
msgstr "Ne"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "No detail data for this pool."
|
||||
msgstr "Za ta pool ni podrobnih podatkov."
|
||||
|
||||
@@ -1212,7 +1212,7 @@ msgstr "Za to napravo ni na voljo atributov S.M.A.R.T."
|
||||
msgid "No systems found."
|
||||
msgstr "Ne najdem sistema."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "None"
|
||||
msgstr "Brez"
|
||||
|
||||
@@ -1255,7 +1255,7 @@ msgstr "Enkratno geslo"
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Open menu"
|
||||
msgstr "Odpri menu"
|
||||
@@ -1346,6 +1346,10 @@ msgstr "Trajen"
|
||||
msgid "Persistence"
|
||||
msgstr "Vztrajnost"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
msgid "Physical device space. True usable capacity is unknown. Pool disk usage alerts are disabled."
|
||||
msgstr "Fizični prostor naprave. Dejanska uporabna zmogljivost ni znana. Opozorila o uporabi diskov poola so onemogočena."
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
msgstr "<0>Nastavite strežnik SMTP</0>, da zagotovite dostavo opozoril."
|
||||
@@ -1379,11 +1383,11 @@ msgstr "Za navodila glejte <0>dokumentacijo</0>."
|
||||
msgid "Please sign in to your account"
|
||||
msgstr "Prijavite se v svoj račun"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Pool Health"
|
||||
msgstr "Stanje poola"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Pool Usage"
|
||||
msgstr "Uporaba poola"
|
||||
|
||||
@@ -1416,7 +1420,7 @@ msgstr "Proces začet"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Public key"
|
||||
msgstr ""
|
||||
msgstr "Javni ključ"
|
||||
|
||||
#. Use 'Key' if your language requires many more characters
|
||||
#: src/components/add-system.tsx
|
||||
@@ -1432,10 +1436,21 @@ msgstr "Globina čakalne vrste"
|
||||
msgid "Quiet Hours"
|
||||
msgstr "Tihi čas"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Raw"
|
||||
msgstr "Surovo"
|
||||
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Raw usage of storage pool {displayName}"
|
||||
msgstr "Surova uporaba shranjevalnega poola {displayName}"
|
||||
|
||||
#. Disk read
|
||||
#. Disk read
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -1443,7 +1458,7 @@ msgstr "Tihi čas"
|
||||
msgid "Read"
|
||||
msgstr "Preberano"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Read errors"
|
||||
msgstr "Napake pri branju"
|
||||
|
||||
@@ -1454,7 +1469,7 @@ msgstr "Prejeto"
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Refresh"
|
||||
msgstr "Osveži"
|
||||
|
||||
@@ -1643,7 +1658,7 @@ msgstr "Čas začetka"
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "Stanje"
|
||||
@@ -1671,7 +1686,7 @@ msgstr "Swap uporaba"
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Switch theme"
|
||||
msgstr ""
|
||||
msgstr "Zamenjaj temo"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
@@ -1690,7 +1705,7 @@ msgstr "Sistemsko"
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
msgid "System fan speeds (RPM)"
|
||||
msgstr ""
|
||||
msgstr "Hitrosti ventilatorjev sistema (RPM)"
|
||||
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "System load averages over time"
|
||||
@@ -1725,7 +1740,7 @@ msgstr "Naloge"
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Temp"
|
||||
msgstr "Temp"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
#: src/lib/alerts.ts
|
||||
@@ -1772,9 +1787,9 @@ msgstr "To bo trajno izbrisalo vse izbrane zapise iz zbirke podatkov."
|
||||
msgid "Throughput of {extraFsName}"
|
||||
msgstr "Prepustnost {extraFsName}"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Throughput of ZFS pool {poolName}"
|
||||
msgstr "Prepustnost ZFS poola {poolName}"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Throughput of storage pool {displayName}"
|
||||
msgstr "Prepustnost shranjevalnega poola {displayName}"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Time format"
|
||||
@@ -1819,7 +1834,7 @@ msgstr "Skupni poslani podatki za vsak vmesnik"
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
msgctxt "Disk I/O"
|
||||
msgid "Total time spent on read/write (can exceed 100%)"
|
||||
msgstr ""
|
||||
msgstr "Skupni čas, porabljen za branje/pisanje (lahko preseže 100 %)"
|
||||
|
||||
#. placeholder {0}: data.length
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
@@ -1897,6 +1912,7 @@ msgstr "Sproži se, ko uporaba katerega koli diska preseže prag"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Type"
|
||||
msgstr "Vrsta"
|
||||
|
||||
@@ -1919,7 +1935,7 @@ msgid "Universal token"
|
||||
msgstr "Univerzalni žeton"
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Unknown"
|
||||
msgstr "Neznana"
|
||||
@@ -1945,7 +1961,7 @@ msgstr "Posodobi"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Updated"
|
||||
msgstr "Posodobljeno"
|
||||
@@ -1968,20 +1984,20 @@ msgstr "Čas delovanja"
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Usage"
|
||||
msgstr "Uporaba"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Usage of ZFS pool {poolName}"
|
||||
msgstr "Uporaba ZFS poola {poolName}"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Usage of storage pool {displayName}"
|
||||
msgstr "Uporaba shranjevalnega poola {displayName}"
|
||||
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Used"
|
||||
msgstr "Uporabljeno"
|
||||
|
||||
@@ -2058,7 +2074,7 @@ msgstr "Ukaz Windows"
|
||||
#. Disk write
|
||||
#. Disk write
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -2066,7 +2082,7 @@ msgstr "Ukaz Windows"
|
||||
msgid "Write"
|
||||
msgstr "Pisanje"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Write errors"
|
||||
msgstr "Napake pri zapisovanju"
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: sr\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-09-02 21:43\n"
|
||||
"PO-Revision-Date: 2026-09-10 00:33\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Serbian (Cyrillic)\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"
|
||||
@@ -97,7 +97,7 @@ msgstr "5 мин"
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Actions"
|
||||
msgstr "Акције"
|
||||
@@ -196,7 +196,7 @@ msgstr "Да ли сте сигурни?"
|
||||
msgid "Automatic copy requires a secure context."
|
||||
msgstr "Аутоматско копирање захтева безбедан контекст."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Disk space available"
|
||||
msgid "Available"
|
||||
msgstr "Доступно"
|
||||
@@ -338,7 +338,7 @@ msgid "Capabilities"
|
||||
msgstr "Могућности"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Capacity"
|
||||
msgstr "Капацитет"
|
||||
|
||||
@@ -391,14 +391,14 @@ msgstr "Proverite svoju uslugu monitoringa"
|
||||
msgid "Check your notification service"
|
||||
msgstr "Проверите ваш сервис за обавештавања"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Checksum errors"
|
||||
msgstr "Грешке контролне суме"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Clear"
|
||||
msgstr "Обриши"
|
||||
@@ -411,7 +411,7 @@ msgstr "Кликните на контејнер да видите више ин
|
||||
msgid "Click on a device to view more information."
|
||||
msgstr "Кликните на уређај да видите више информација."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Click on a pool to view vdev and dataset details."
|
||||
msgstr "Кликните на pool да бисте видели детаље о vdev и dataset."
|
||||
|
||||
@@ -875,8 +875,8 @@ msgstr "Вентилатори"
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system/chart-card.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Filter..."
|
||||
@@ -898,8 +898,8 @@ msgstr "За <0>{min}</0> {min, plural, one {минуту} few {минута} ot
|
||||
msgid "Forgot password?"
|
||||
msgstr "Заборављена лозинка?"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Free space"
|
||||
msgid "Free"
|
||||
msgstr "Слободно"
|
||||
@@ -948,7 +948,7 @@ msgid "Grid"
|
||||
msgstr "Мрежа"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Health"
|
||||
msgstr "Здравље"
|
||||
|
||||
@@ -1146,7 +1146,7 @@ msgstr "Коришћење меморије контејнера"
|
||||
msgid "Model"
|
||||
msgstr "Модел"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Mountpoint"
|
||||
msgstr "Тачка монтирања"
|
||||
|
||||
@@ -1185,7 +1185,7 @@ msgstr "Мрежна јединица"
|
||||
msgid "No"
|
||||
msgstr "Не"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "No detail data for this pool."
|
||||
msgstr "Нема детаљних података за овај pool."
|
||||
|
||||
@@ -1212,7 +1212,7 @@ msgstr "Нема доступних S.M.A.R.T. атрибута за овај у
|
||||
msgid "No systems found."
|
||||
msgstr "Нису пронађени системи."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "None"
|
||||
msgstr "Нема"
|
||||
|
||||
@@ -1255,7 +1255,7 @@ msgstr "Једнократна лозинка"
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Open menu"
|
||||
msgstr "Отвори мени"
|
||||
@@ -1346,6 +1346,10 @@ msgstr "Трајан"
|
||||
msgid "Persistence"
|
||||
msgstr "Упорност"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
msgid "Physical device space. True usable capacity is unknown. Pool disk usage alerts are disabled."
|
||||
msgstr "Физички простор уређаја. Стварни искористиви капацитет није познат. Упозорења о искоришћености дискова pool-а су онемогућена."
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
msgstr "Молимо вас <0>конфигуришите SMTP сервер</0> да бисте осигурали да се упозорења испоручују."
|
||||
@@ -1379,11 +1383,11 @@ msgstr "Молимо вас погледајте <0>документацију</
|
||||
msgid "Please sign in to your account"
|
||||
msgstr "Молимо вас да се пријавите на ваш налог"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Pool Health"
|
||||
msgstr "Стање pool-а"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Pool Usage"
|
||||
msgstr "Коришћење pool-а"
|
||||
|
||||
@@ -1432,10 +1436,21 @@ msgstr "Дубина реда"
|
||||
msgid "Quiet Hours"
|
||||
msgstr "Тихи сати"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Raw"
|
||||
msgstr "Сирово"
|
||||
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Raw usage of storage pool {displayName}"
|
||||
msgstr "Сирова искоришћеност pool-а за складиштење {displayName}"
|
||||
|
||||
#. Disk read
|
||||
#. Disk read
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -1443,7 +1458,7 @@ msgstr "Тихи сати"
|
||||
msgid "Read"
|
||||
msgstr "Читање"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Read errors"
|
||||
msgstr "Грешке при читању"
|
||||
|
||||
@@ -1454,7 +1469,7 @@ msgstr "Примљено"
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Refresh"
|
||||
msgstr "Освежи"
|
||||
|
||||
@@ -1501,7 +1516,7 @@ msgstr "Настави"
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgctxt "Root disk label"
|
||||
msgid "Root"
|
||||
msgstr "Root"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Rotate token"
|
||||
@@ -1643,7 +1658,7 @@ msgstr "Време почетка"
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "Стање"
|
||||
@@ -1690,7 +1705,7 @@ msgstr "Систем"
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
msgid "System fan speeds (RPM)"
|
||||
msgstr ""
|
||||
msgstr "Брзине вентилатора система (RPM)"
|
||||
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "System load averages over time"
|
||||
@@ -1772,9 +1787,9 @@ msgstr "Ово ће трајно избрисати све изабране за
|
||||
msgid "Throughput of {extraFsName}"
|
||||
msgstr "Проток {extraFsName}"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Throughput of ZFS pool {poolName}"
|
||||
msgstr "Пропусност ZFS pool-а {poolName}"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Throughput of storage pool {displayName}"
|
||||
msgstr "Проток pool-а за складиштење {displayName}"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Time format"
|
||||
@@ -1897,6 +1912,7 @@ msgstr "Окида се када употреба било ког диска п
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Type"
|
||||
msgstr "Тип"
|
||||
|
||||
@@ -1919,7 +1935,7 @@ msgid "Universal token"
|
||||
msgstr "Универзални токен"
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Unknown"
|
||||
msgstr "Непознато"
|
||||
@@ -1945,7 +1961,7 @@ msgstr "Ажурирај"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Updated"
|
||||
msgstr "Ажурирано"
|
||||
@@ -1968,20 +1984,20 @@ msgstr "Време рада"
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Usage"
|
||||
msgstr "Употреба"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Usage of ZFS pool {poolName}"
|
||||
msgstr "Коришћење ZFS pool-а {poolName}"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Usage of storage pool {displayName}"
|
||||
msgstr "Искоришћеност pool-а за складиштење {displayName}"
|
||||
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Used"
|
||||
msgstr "Коришћено"
|
||||
|
||||
@@ -2058,7 +2074,7 @@ msgstr "Windows команда"
|
||||
#. Disk write
|
||||
#. Disk write
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -2066,7 +2082,7 @@ msgstr "Windows команда"
|
||||
msgid "Write"
|
||||
msgstr "Писање"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Write errors"
|
||||
msgstr "Грешке при писању"
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: sv\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2026-09-02 21:43\n"
|
||||
"PO-Revision-Date: 2026-09-10 00:33\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Swedish\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -61,7 +61,7 @@ msgstr "1 timme"
|
||||
#. Load average
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "1 min"
|
||||
msgstr "1 min"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 minute"
|
||||
@@ -78,7 +78,7 @@ msgstr "12 timmar"
|
||||
#. Load average
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "15 min"
|
||||
msgstr "15 min"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "24 hours"
|
||||
@@ -91,13 +91,13 @@ msgstr "30 dagar"
|
||||
#. Load average
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "5 min"
|
||||
msgstr "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/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Actions"
|
||||
msgstr "Åtgärder"
|
||||
@@ -142,7 +142,7 @@ msgstr "Justera bredden på huvudlayouten"
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Admin"
|
||||
msgstr "Admin"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
msgid "After"
|
||||
@@ -154,7 +154,7 @@ msgstr "Efter att du har ställt in miljövariablerna, starta om din Beszel-hubb
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Agent"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
@@ -196,7 +196,7 @@ msgstr "Är du säker?"
|
||||
msgid "Automatic copy requires a secure context."
|
||||
msgstr "Automatisk kopiering kräver en säker kontext."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Disk space available"
|
||||
msgid "Available"
|
||||
msgstr "Tillgängligt"
|
||||
@@ -338,7 +338,7 @@ msgid "Capabilities"
|
||||
msgstr "Förmågor"
|
||||
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Capacity"
|
||||
msgstr "Kapacitet"
|
||||
|
||||
@@ -348,7 +348,7 @@ msgstr "Varning - potentiell dataförlust"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Celsius (°C)"
|
||||
msgstr "Celsius (°C)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change display units for metrics."
|
||||
@@ -391,14 +391,14 @@ msgstr "Kontrollera din övervakningstjänst"
|
||||
msgid "Check your notification service"
|
||||
msgstr "Kontrollera din aviseringstjänst"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Checksum errors"
|
||||
msgstr "Kontrollsummefel"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Clear"
|
||||
msgstr "Rensa"
|
||||
@@ -411,7 +411,7 @@ msgstr "Klicka på en behållare för att visa mer information."
|
||||
msgid "Click on a device to view more information."
|
||||
msgstr "Klicka på en enhet för att visa mer information."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Click on a pool to view vdev and dataset details."
|
||||
msgstr "Klicka på en pool för att visa detaljer om vdev och dataset."
|
||||
|
||||
@@ -447,7 +447,7 @@ msgstr "Ej ansluten"
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Container"
|
||||
msgstr "Container"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Container Health"
|
||||
@@ -503,7 +503,7 @@ msgstr "Kopiera namn"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Copy public key"
|
||||
msgstr ""
|
||||
msgstr "Kopiera publik nyckel"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
@@ -530,7 +530,7 @@ msgstr "Kärna"
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "CPU"
|
||||
msgstr "CPU"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "CPU Cores"
|
||||
@@ -661,7 +661,7 @@ msgstr "Urladdar"
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Disk"
|
||||
msgstr "Disk"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Disk unit"
|
||||
@@ -826,7 +826,7 @@ msgstr "Exportera din nuvarande systemkonfiguration."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Fahrenheit (°F)"
|
||||
msgstr "Fahrenheit (°F)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Failed"
|
||||
@@ -869,14 +869,14 @@ msgstr "Misslyckades: {0}"
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
msgid "Fans"
|
||||
msgstr ""
|
||||
msgstr "Fläktar"
|
||||
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/routes/system/chart-card.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Filter..."
|
||||
@@ -898,8 +898,8 @@ msgstr "Under <0>{min}</0> {min, plural, one {minut} other {minuter}}"
|
||||
msgid "Forgot password?"
|
||||
msgstr "Glömt lösenordet?"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgctxt "Free space"
|
||||
msgid "Free"
|
||||
msgstr "Ledigt"
|
||||
@@ -914,7 +914,7 @@ msgstr "FreeBSD kommando"
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Full"
|
||||
msgstr "Full"
|
||||
msgstr ""
|
||||
|
||||
#. Context: General settings
|
||||
#: src/components/routes/settings/general.tsx
|
||||
@@ -924,7 +924,7 @@ msgstr "Allmänt"
|
||||
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
msgid "Global"
|
||||
msgstr "Global"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "GPU"
|
||||
@@ -948,7 +948,7 @@ msgid "Grid"
|
||||
msgstr "Rutnät"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Health"
|
||||
msgstr "Hälsa"
|
||||
|
||||
@@ -1029,7 +1029,7 @@ msgstr "Språk"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Layout"
|
||||
msgstr "Layout"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Layout width"
|
||||
@@ -1113,7 +1113,7 @@ msgstr "Manuella installationsinstruktioner"
|
||||
#. Chart select field. Please try to keep this short.
|
||||
#: src/components/routes/system/chart-card.tsx
|
||||
msgid "Max 1 min"
|
||||
msgstr "Max 1 min"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/info-bar.tsx
|
||||
@@ -1146,7 +1146,7 @@ msgstr "Minnesanvändning för containrar"
|
||||
msgid "Model"
|
||||
msgstr "Modell"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Mountpoint"
|
||||
msgstr "Monteringspunkt"
|
||||
|
||||
@@ -1185,7 +1185,7 @@ msgstr "Nätverksenhet"
|
||||
msgid "No"
|
||||
msgstr "Nej"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "No detail data for this pool."
|
||||
msgstr "Det finns inga detaljerade data för denna pool."
|
||||
|
||||
@@ -1212,7 +1212,7 @@ msgstr "Inga S.M.A.R.T.-attribut tillgängliga för den här enheten."
|
||||
msgid "No systems found."
|
||||
msgstr "Inga system hittades."
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "None"
|
||||
msgstr "Ingen"
|
||||
|
||||
@@ -1255,7 +1255,7 @@ msgstr "Engångslösenord"
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Open menu"
|
||||
msgstr "Öppna menyn"
|
||||
@@ -1346,6 +1346,10 @@ msgstr ""
|
||||
msgid "Persistence"
|
||||
msgstr "Beständighet"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
msgid "Physical device space. True usable capacity is unknown. Pool disk usage alerts are disabled."
|
||||
msgstr "Fysiskt enhetsutrymme. Den verkliga användbara kapaciteten är okänd. Varningar om diskanvändning för poolen är inaktiverade."
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
msgstr "Vänligen <0>konfigurera en SMTP-server</0> för att säkerställa att larm levereras."
|
||||
@@ -1379,17 +1383,17 @@ msgstr "Vänligen se <0>dokumentationen</0> för instruktioner."
|
||||
msgid "Please sign in to your account"
|
||||
msgstr "Vänligen logga in på ditt konto"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Pool Health"
|
||||
msgstr "Poolstatus"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Pool Usage"
|
||||
msgstr "Poolanvändning"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Port"
|
||||
msgstr "Port"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
msgctxt "Container ports"
|
||||
@@ -1416,7 +1420,7 @@ msgstr "Process startad"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Public key"
|
||||
msgstr ""
|
||||
msgstr "Publik nyckel"
|
||||
|
||||
#. Use 'Key' if your language requires many more characters
|
||||
#: src/components/add-system.tsx
|
||||
@@ -1432,10 +1436,21 @@ msgstr "Kö-djup"
|
||||
msgid "Quiet Hours"
|
||||
msgstr "Tysta timmar"
|
||||
|
||||
#: src/components/routes/system/raw-capacity-label.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Raw"
|
||||
msgstr "Rå"
|
||||
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Raw usage of storage pool {displayName}"
|
||||
msgstr "Rå användning av lagringspool {displayName}"
|
||||
|
||||
#. Disk read
|
||||
#. Disk read
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -1443,7 +1458,7 @@ msgstr "Tysta timmar"
|
||||
msgid "Read"
|
||||
msgstr "Läs"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Read errors"
|
||||
msgstr "Läsfel"
|
||||
|
||||
@@ -1454,7 +1469,7 @@ msgstr "Mottaget"
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/containers-table/containers-table.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Refresh"
|
||||
msgstr "Uppdatera"
|
||||
|
||||
@@ -1643,7 +1658,7 @@ msgstr "Starttid"
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "Tillstånd"
|
||||
@@ -1654,7 +1669,7 @@ msgstr "Tillstånd"
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "Status"
|
||||
msgstr "Status"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Sub State"
|
||||
@@ -1671,7 +1686,7 @@ msgstr "Swap-användning"
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Switch theme"
|
||||
msgstr ""
|
||||
msgstr "Byt tema"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
@@ -1686,11 +1701,11 @@ msgstr ""
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/lib/alerts.ts
|
||||
msgid "System"
|
||||
msgstr "System"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
msgid "System fan speeds (RPM)"
|
||||
msgstr ""
|
||||
msgstr "Systemfläkthastigheter (RPM)"
|
||||
|
||||
#: src/components/routes/system/charts/load-average-chart.tsx
|
||||
msgid "System load averages over time"
|
||||
@@ -1725,7 +1740,7 @@ msgstr "Uppgifter"
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Temp"
|
||||
msgstr "Temp"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/charts/sensor-charts.tsx
|
||||
#: src/lib/alerts.ts
|
||||
@@ -1772,9 +1787,9 @@ msgstr "Detta kommer permanent att ta bort alla valda poster från databasen."
|
||||
msgid "Throughput of {extraFsName}"
|
||||
msgstr "Genomströmning av {extraFsName}"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Throughput of ZFS pool {poolName}"
|
||||
msgstr "Genomströmning för ZFS-pool {poolName}"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Throughput of storage pool {displayName}"
|
||||
msgstr "Genomströmning av lagringspool {displayName}"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Time format"
|
||||
@@ -1806,7 +1821,7 @@ msgstr "Tokens och fingeravtryck används för att autentisera WebSocket-anslutn
|
||||
#: src/components/ui/chart.tsx
|
||||
#: src/components/ui/chart.tsx
|
||||
msgid "Total"
|
||||
msgstr "Total"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system/network-sheet.tsx
|
||||
msgid "Total data received for each interface"
|
||||
@@ -1819,7 +1834,7 @@ msgstr "Totalt skickad data för varje gränssnitt"
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
msgctxt "Disk I/O"
|
||||
msgid "Total time spent on read/write (can exceed 100%)"
|
||||
msgstr ""
|
||||
msgstr "Total tid för läsning/skrivning (kan överstiga 100 %)"
|
||||
|
||||
#. placeholder {0}: data.length
|
||||
#: src/components/systemd-table/systemd-table.tsx
|
||||
@@ -1897,6 +1912,7 @@ msgstr "Utlöses när användningen av någon disk överskrider ett tröskelvär
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/settings/quiet-hours.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Type"
|
||||
msgstr "Typ"
|
||||
|
||||
@@ -1919,7 +1935,7 @@ msgid "Universal token"
|
||||
msgstr "Universell nyckel"
|
||||
|
||||
#. Context: Battery state
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/lib/i18n.ts
|
||||
msgid "Unknown"
|
||||
msgstr "Okänd"
|
||||
@@ -1945,7 +1961,7 @@ msgstr "Uppdatera"
|
||||
|
||||
#: src/components/containers-table/containers-table-columns.tsx
|
||||
#: src/components/routes/system/smart-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/systemd-table/systemd-table-columns.tsx
|
||||
msgid "Updated"
|
||||
msgstr "Uppdaterad"
|
||||
@@ -1968,20 +1984,20 @@ msgstr "Drifttid"
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/gpu-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/cpu-sheet.tsx
|
||||
msgid "Usage"
|
||||
msgstr "Användning"
|
||||
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
msgid "Usage of ZFS pool {poolName}"
|
||||
msgstr "Användning av ZFS-pool {poolName}"
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
msgid "Usage of storage pool {displayName}"
|
||||
msgstr "Användning av lagringspool {displayName}"
|
||||
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/charts/memory-charts.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Used"
|
||||
msgstr "Använt"
|
||||
|
||||
@@ -2058,7 +2074,7 @@ msgstr "Windows-kommando"
|
||||
#. Disk write
|
||||
#. Disk write
|
||||
#: src/components/routes/system/charts/disk-charts.tsx
|
||||
#: src/components/routes/system/charts/zfs-charts.tsx
|
||||
#: src/components/routes/system/charts/storage-pool-charts.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
#: src/components/routes/system/disk-io-sheet.tsx
|
||||
@@ -2066,7 +2082,7 @@ msgstr "Windows-kommando"
|
||||
msgid "Write"
|
||||
msgstr "Skriv"
|
||||
|
||||
#: src/components/routes/system/zfs-table.tsx
|
||||
#: src/components/routes/system/storage-pools-table.tsx
|
||||
msgid "Write errors"
|
||||
msgstr "Skrivfel"
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user