mirror of
https://github.com/henrygd/beszel.git
synced 2026-03-22 05:36:15 +01:00
Compare commits
67 Commits
svenvg93-f
...
encoding/j
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
47360c5bf1 | ||
|
|
7ba1f366ba | ||
|
|
37c6b920f9 | ||
|
|
49db81dac8 | ||
|
|
a9e90ec19c | ||
|
|
2ad60507b7 | ||
|
|
12059ee3db | ||
|
|
de56544ca3 | ||
|
|
065c7facb6 | ||
|
|
630c92c139 | ||
|
|
e11d452d91 | ||
|
|
99c7f7bd8a | ||
|
|
8af3a0eb5b | ||
|
|
5f7950b474 | ||
|
|
df9e2dec28 | ||
|
|
a0f271545a | ||
|
|
aa2bc9f118 | ||
|
|
b22ae87022 | ||
|
|
79e79079bc | ||
|
|
1811ebdee4 | ||
|
|
137f3f3e24 | ||
|
|
ed1d1e77c0 | ||
|
|
8c36dd1caa | ||
|
|
57bfe72486 | ||
|
|
75f66b0246 | ||
|
|
ce93d54aa7 | ||
|
|
39dbe0eac5 | ||
|
|
7282044f80 | ||
|
|
d77c37c0b0 | ||
|
|
e362cbbca5 | ||
|
|
118544926b | ||
|
|
d4bb0a0a30 | ||
|
|
fe5e35d1a9 | ||
|
|
60a6ae2caa | ||
|
|
80338d36aa | ||
|
|
f0d2c242e8 | ||
|
|
559f83d99c | ||
|
|
d3a751ee6c | ||
|
|
fb70a166fa | ||
|
|
c12457b707 | ||
|
|
3e53d73d56 | ||
|
|
80338c5e98 | ||
|
|
249cd8ad19 | ||
|
|
ccdff46370 | ||
|
|
91679b5cc0 | ||
|
|
6953edf59e | ||
|
|
b91c77ec92 | ||
|
|
3ac0b185d1 | ||
|
|
1e675cabb5 | ||
|
|
5f44965c2c | ||
|
|
f080929296 | ||
|
|
f055658eba | ||
|
|
e430c747fe | ||
|
|
ca62b1db36 | ||
|
|
38569b7057 | ||
|
|
203244090f | ||
|
|
2bed722045 | ||
|
|
13f3a52760 | ||
|
|
16b9827c70 | ||
|
|
0fc352d7fc | ||
|
|
8a2bee11d4 | ||
|
|
485f7d16ff | ||
|
|
46fdc94cb8 | ||
|
|
261f7fb76c | ||
|
|
18d9258907 | ||
|
|
9d7fb8ab80 | ||
|
|
3730a78e5a |
30
.github/workflows/docker-images.yml
vendored
30
.github/workflows/docker-images.yml
vendored
@@ -14,28 +14,48 @@ jobs:
|
||||
include:
|
||||
- image: henrygd/beszel
|
||||
context: ./beszel
|
||||
dockerfile: ./beszel/dockerfile_Hub
|
||||
dockerfile: ./beszel/dockerfile_hub
|
||||
registry: docker.io
|
||||
username_secret: DOCKERHUB_USERNAME
|
||||
password_secret: DOCKERHUB_TOKEN
|
||||
|
||||
- image: henrygd/beszel-agent
|
||||
context: ./beszel
|
||||
dockerfile: ./beszel/dockerfile_Agent
|
||||
dockerfile: ./beszel/dockerfile_agent
|
||||
registry: docker.io
|
||||
username_secret: DOCKERHUB_USERNAME
|
||||
password_secret: DOCKERHUB_TOKEN
|
||||
|
||||
- image: henrygd/beszel-agent-nvidia
|
||||
context: ./beszel
|
||||
dockerfile: ./beszel/dockerfile_agent_nvidia
|
||||
platforms: linux/amd64
|
||||
registry: docker.io
|
||||
username_secret: DOCKERHUB_USERNAME
|
||||
password_secret: DOCKERHUB_TOKEN
|
||||
|
||||
- image: ghcr.io/${{ github.repository }}/beszel
|
||||
context: ./beszel
|
||||
dockerfile: ./beszel/dockerfile_Hub
|
||||
dockerfile: ./beszel/dockerfile_hub
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password_secret: GITHUB_TOKEN
|
||||
|
||||
- image: ghcr.io/${{ github.repository }}/beszel-agent
|
||||
context: ./beszel
|
||||
dockerfile: ./beszel/dockerfile_Agent
|
||||
dockerfile: ./beszel/dockerfile_agent
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password_secret: GITHUB_TOKEN
|
||||
|
||||
- image: ghcr.io/${{ github.repository }}/beszel-agent-nvidia
|
||||
context: ./beszel
|
||||
dockerfile: ./beszel/dockerfile_agent_nvidia
|
||||
platforms: linux/amd64
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password_secret: GITHUB_TOKEN
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
@@ -87,7 +107,7 @@ jobs:
|
||||
with:
|
||||
context: "${{ matrix.context }}"
|
||||
file: ${{ matrix.dockerfile }}
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||
platforms: ${{ matrix.platforms || 'linux/amd64,linux/arm64,linux/arm/v7' }}
|
||||
push: ${{ github.ref_type == 'tag' }}
|
||||
tags: ${{ steps.metadata.outputs.tags }}
|
||||
labels: ${{ steps.metadata.outputs.labels }}
|
||||
|
||||
15
.github/workflows/release.yml
vendored
15
.github/workflows/release.yml
vendored
@@ -3,7 +3,7 @@ name: Make release and binaries
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
- "v*"
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
@@ -29,7 +29,17 @@ jobs:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '^1.22.1'
|
||||
go-version: "^1.22.1"
|
||||
|
||||
- name: Set up .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: "9.0.x"
|
||||
|
||||
- name: Build .NET LHM executable for Windows sensors
|
||||
run: |
|
||||
dotnet build -c Release ./beszel/internal/agent/lhm/beszel_lhm.csproj
|
||||
shell: bash
|
||||
|
||||
- name: GoReleaser beszel
|
||||
uses: goreleaser/goreleaser-action@v6
|
||||
@@ -40,3 +50,4 @@ jobs:
|
||||
args: release --clean
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.TOKEN || secrets.GITHUB_TOKEN }}
|
||||
WINGET_TOKEN: ${{ secrets.WINGET_TOKEN }}
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -17,3 +17,6 @@ beszel/build
|
||||
beszel/site/src/locales/**/*.ts
|
||||
*.bak
|
||||
__debug_*
|
||||
beszel/internal/agent/lhm/obj
|
||||
beszel/internal/agent/lhm/bin
|
||||
dockerfile_agent_dev
|
||||
|
||||
@@ -173,7 +173,6 @@ brews:
|
||||
error_log_path "#{Dir.home}/.cache/beszel/beszel-agent.log"
|
||||
keep_alive true
|
||||
restart_delay 5
|
||||
name beszel-agent
|
||||
process_type :background
|
||||
|
||||
winget:
|
||||
@@ -203,13 +202,14 @@ winget:
|
||||
owner: henrygd
|
||||
name: beszel-winget
|
||||
branch: henrygd.beszel-agent-{{ .Version }}
|
||||
pull_request:
|
||||
enabled: false
|
||||
draft: false
|
||||
base:
|
||||
owner: microsoft
|
||||
name: winget-pkgs
|
||||
branch: master
|
||||
token: "{{ .Env.WINGET_TOKEN }}"
|
||||
# pull_request:
|
||||
# enabled: true
|
||||
# draft: false
|
||||
# base:
|
||||
# owner: microsoft
|
||||
# name: winget-pkgs
|
||||
# branch: master
|
||||
|
||||
release:
|
||||
draft: true
|
||||
|
||||
@@ -4,6 +4,9 @@ ARCH ?= $(shell go env GOARCH)
|
||||
# Skip building the web UI if true
|
||||
SKIP_WEB ?= false
|
||||
|
||||
# Set executable extension based on target OS
|
||||
EXE_EXT := $(if $(filter windows,$(OS)),.exe,)
|
||||
|
||||
.PHONY: tidy build-agent build-hub build clean lint dev-server dev-agent dev-hub dev generate-locales
|
||||
.DEFAULT_GOAL := build
|
||||
|
||||
@@ -14,7 +17,7 @@ clean:
|
||||
lint:
|
||||
golangci-lint run
|
||||
|
||||
test: export GOEXPERIMENT=synctest
|
||||
test: export GOEXPERIMENT=synctest,jsonv2
|
||||
test:
|
||||
go test -tags=testing ./...
|
||||
|
||||
@@ -30,11 +33,25 @@ build-web-ui:
|
||||
npm run --prefix ./site build; \
|
||||
fi
|
||||
|
||||
build-agent: tidy
|
||||
GOOS=$(OS) GOARCH=$(ARCH) go build -o ./build/beszel-agent_$(OS)_$(ARCH) -ldflags "-w -s" beszel/cmd/agent
|
||||
# Conditional .NET build - only for Windows
|
||||
build-dotnet-conditional:
|
||||
@if [ "$(OS)" = "windows" ]; then \
|
||||
echo "Building .NET executable for Windows..."; \
|
||||
if command -v dotnet >/dev/null 2>&1; then \
|
||||
rm -rf ./internal/agent/lhm/bin; \
|
||||
dotnet build -c Release ./internal/agent/lhm/beszel_lhm.csproj; \
|
||||
else \
|
||||
echo "Error: dotnet not found. Install .NET SDK to build Windows agent."; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
fi
|
||||
|
||||
# Update build-agent to include conditional .NET build
|
||||
build-agent: tidy build-dotnet-conditional
|
||||
GOOS=$(OS) GOARCH=$(ARCH) go build -o ./build/beszel-agent_$(OS)_$(ARCH)$(EXE_EXT) -ldflags "-w -s" beszel/cmd/agent
|
||||
|
||||
build-hub: tidy $(if $(filter false,$(SKIP_WEB)),build-web-ui)
|
||||
GOOS=$(OS) GOARCH=$(ARCH) go build -o ./build/beszel_$(OS)_$(ARCH) -ldflags "-w -s" beszel/cmd/hub
|
||||
GOOS=$(OS) GOARCH=$(ARCH) go build -o ./build/beszel_$(OS)_$(ARCH)$(EXE_EXT) -ldflags "-w -s" beszel/cmd/hub
|
||||
|
||||
build: build-agent build-hub
|
||||
|
||||
@@ -53,6 +70,7 @@ dev-server: generate-locales
|
||||
fi
|
||||
|
||||
dev-hub: export ENV=dev
|
||||
dev-hub: export GOEXPERIMENT=jsonv2
|
||||
dev-hub:
|
||||
mkdir -p ./site/dist && touch ./site/dist/index.html
|
||||
@if command -v entr >/dev/null 2>&1; then \
|
||||
@@ -61,12 +79,22 @@ dev-hub:
|
||||
cd ./cmd/hub && go run . serve --http 0.0.0.0:8090; \
|
||||
fi
|
||||
|
||||
dev-agent: export GOEXPERIMENT=jsonv2
|
||||
dev-agent:
|
||||
@if command -v entr >/dev/null 2>&1; then \
|
||||
find ./cmd/agent/*.go ./internal/agent/*.go | entr -r go run beszel/cmd/agent; \
|
||||
else \
|
||||
go run beszel/cmd/agent; \
|
||||
fi
|
||||
|
||||
build-dotnet:
|
||||
@if command -v dotnet >/dev/null 2>&1; then \
|
||||
rm -rf ./internal/agent/lhm/bin; \
|
||||
dotnet build -c Release ./internal/agent/lhm/beszel_lhm.csproj; \
|
||||
else \
|
||||
echo "dotnet not found"; \
|
||||
fi
|
||||
|
||||
|
||||
# KEY="..." make -j dev
|
||||
dev: dev-server dev-hub dev-agent
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
@@ -25,13 +26,16 @@ func (opts *cmdOptions) parse() bool {
|
||||
flag.StringVar(&opts.listen, "listen", "", "Address or port to listen on")
|
||||
|
||||
flag.Usage = func() {
|
||||
fmt.Printf("Usage: %s [command] [flags]\n", os.Args[0])
|
||||
fmt.Println("\nCommands:")
|
||||
fmt.Println(" health Check if the agent is running")
|
||||
fmt.Println(" help Display this help message")
|
||||
fmt.Println(" update Update to the latest version")
|
||||
fmt.Println(" version Display the version")
|
||||
fmt.Println("\nFlags:")
|
||||
builder := strings.Builder{}
|
||||
builder.WriteString("Usage: ")
|
||||
builder.WriteString(os.Args[0])
|
||||
builder.WriteString(" [command] [flags]\n")
|
||||
builder.WriteString("\nCommands:\n")
|
||||
builder.WriteString(" health Check if the agent is running\n")
|
||||
builder.WriteString(" help Display this help message\n")
|
||||
builder.WriteString(" update Update to the latest version\n")
|
||||
builder.WriteString("\nFlags:\n")
|
||||
fmt.Print(builder.String())
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
|
||||
@@ -111,12 +115,12 @@ func main() {
|
||||
serverConfig.Addr = addr
|
||||
serverConfig.Network = agent.GetNetwork(addr)
|
||||
|
||||
agent, err := agent.NewAgent("")
|
||||
a, err := agent.NewAgent()
|
||||
if err != nil {
|
||||
log.Fatal("Failed to create agent: ", err)
|
||||
}
|
||||
|
||||
if err := agent.Start(serverConfig); err != nil {
|
||||
if err := a.Start(serverConfig); err != nil {
|
||||
log.Fatal("Failed to start server: ", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
FROM --platform=$BUILDPLATFORM golang:alpine AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY go.mod go.sum ./
|
||||
# RUN go mod download
|
||||
COPY *.go ./
|
||||
COPY cmd ./cmd
|
||||
COPY internal ./internal
|
||||
|
||||
# Build
|
||||
ARG TARGETOS TARGETARCH
|
||||
RUN CGO_ENABLED=0 GOGC=75 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -ldflags "-w -s" -o /agent ./cmd/agent
|
||||
|
||||
RUN rm -rf /tmp/*
|
||||
|
||||
# ? -------------------------
|
||||
FROM scratch
|
||||
|
||||
COPY --from=builder /agent /agent
|
||||
|
||||
# this is so we don't need to create the
|
||||
# /tmp directory in the scratch container
|
||||
COPY --from=builder /tmp /tmp
|
||||
|
||||
ENTRYPOINT ["/agent"]
|
||||
26
beszel/dockerfile_agent
Normal file
26
beszel/dockerfile_agent
Normal file
@@ -0,0 +1,26 @@
|
||||
FROM --platform=$BUILDPLATFORM golang:alpine AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY go.mod go.sum ./
|
||||
# RUN go mod download
|
||||
COPY *.go ./
|
||||
COPY cmd ./cmd
|
||||
COPY internal ./internal
|
||||
|
||||
# Build
|
||||
ARG TARGETOS TARGETARCH
|
||||
RUN CGO_ENABLED=0 GOGC=75 GOEXPERIMENT=jsonv2 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -ldflags "-w -s" -o /agent ./cmd/agent
|
||||
|
||||
RUN rm -rf /tmp/*
|
||||
|
||||
# --------------------------
|
||||
# Final image: default scratch-based agent
|
||||
# --------------------------
|
||||
FROM scratch
|
||||
COPY --from=builder /agent /agent
|
||||
|
||||
# this is so we don't need to create the /tmp directory in the scratch container
|
||||
COPY --from=builder /tmp /tmp
|
||||
|
||||
ENTRYPOINT ["/agent"]
|
||||
21
beszel/dockerfile_agent_nvidia
Normal file
21
beszel/dockerfile_agent_nvidia
Normal file
@@ -0,0 +1,21 @@
|
||||
FROM --platform=$BUILDPLATFORM golang:alpine AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY go.mod go.sum ./
|
||||
# RUN go mod download
|
||||
COPY *.go ./
|
||||
COPY cmd ./cmd
|
||||
COPY internal ./internal
|
||||
|
||||
# Build
|
||||
ARG TARGETOS TARGETARCH
|
||||
RUN CGO_ENABLED=0 GOGC=75 GOEXPERIMENT=jsonv2 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -ldflags "-w -s" -o /agent ./cmd/agent
|
||||
|
||||
# --------------------------
|
||||
# Final image: GPU-enabled agent with nvidia-smi
|
||||
# --------------------------
|
||||
FROM nvidia/cuda:12.9.1-base-ubuntu22.04
|
||||
COPY --from=builder /agent /agent
|
||||
|
||||
ENTRYPOINT ["/agent"]
|
||||
@@ -22,7 +22,7 @@ RUN update-ca-certificates
|
||||
|
||||
# Build
|
||||
ARG TARGETOS TARGETARCH
|
||||
RUN CGO_ENABLED=0 GOGC=75 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -ldflags "-w -s" -o /beszel ./cmd/hub
|
||||
RUN CGO_ENABLED=0 GOEXPERIMENT=jsonv2 GOGC=75 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -ldflags "-w -s" -o /beszel ./cmd/hub
|
||||
|
||||
# ? -------------------------
|
||||
FROM scratch
|
||||
@@ -7,20 +7,20 @@ replace github.com/nicholas-fedor/shoutrrr => github.com/nicholas-fedor/shoutrrr
|
||||
|
||||
require (
|
||||
github.com/blang/semver v3.5.1+incompatible
|
||||
github.com/fxamacker/cbor/v2 v2.8.0
|
||||
github.com/fxamacker/cbor/v2 v2.9.0
|
||||
github.com/gliderlabs/ssh v0.3.8
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/lxzan/gws v1.8.9
|
||||
github.com/nicholas-fedor/shoutrrr v0.8.15
|
||||
github.com/nicholas-fedor/shoutrrr v0.8.17
|
||||
github.com/pocketbase/dbx v1.11.0
|
||||
github.com/pocketbase/pocketbase v0.28.4
|
||||
github.com/pocketbase/pocketbase v0.29.2
|
||||
github.com/rhysd/go-github-selfupdate v1.2.3
|
||||
github.com/shirou/gopsutil/v4 v4.25.6
|
||||
github.com/shirou/gopsutil/v4 v4.25.7
|
||||
github.com/spf13/cast v1.9.2
|
||||
github.com/spf13/cobra v1.9.1
|
||||
github.com/stretchr/testify v1.10.0
|
||||
golang.org/x/crypto v0.39.0
|
||||
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b
|
||||
golang.org/x/crypto v0.41.0
|
||||
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
@@ -39,7 +39,7 @@ require (
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/go-ozzo/ozzo-validation/v4 v4.3.0 // indirect
|
||||
github.com/go-sql-driver/mysql v1.9.1 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
|
||||
github.com/google/go-github/v30 v30.1.0 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf // indirect
|
||||
@@ -52,21 +52,21 @@ require (
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/spf13/pflag v1.0.6 // indirect
|
||||
github.com/spf13/pflag v1.0.7 // indirect
|
||||
github.com/tcnksm/go-gitconfig v0.1.2 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.15 // indirect
|
||||
github.com/tklauser/numcpus v0.10.0 // indirect
|
||||
github.com/ulikunitz/xz v0.5.12 // indirect
|
||||
github.com/ulikunitz/xz v0.5.13 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
golang.org/x/image v0.28.0 // indirect
|
||||
golang.org/x/net v0.41.0 // indirect
|
||||
golang.org/x/image v0.30.0 // indirect
|
||||
golang.org/x/net v0.43.0 // indirect
|
||||
golang.org/x/oauth2 v0.30.0 // indirect
|
||||
golang.org/x/sync v0.15.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/text v0.26.0 // indirect
|
||||
modernc.org/libc v1.65.10 // indirect
|
||||
golang.org/x/sync v0.16.0 // indirect
|
||||
golang.org/x/sys v0.35.0 // indirect
|
||||
golang.org/x/text v0.28.0 // indirect
|
||||
modernc.org/libc v1.66.3 // indirect
|
||||
modernc.org/mathutil v1.7.1 // indirect
|
||||
modernc.org/memory v1.11.0 // indirect
|
||||
modernc.org/sqlite v1.38.0 // indirect
|
||||
modernc.org/sqlite v1.38.2 // indirect
|
||||
)
|
||||
|
||||
@@ -26,8 +26,8 @@ github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fxamacker/cbor/v2 v2.8.0 h1:fFtUGXUzXPHTIUdne5+zzMPTfffl3RD5qYnkY40vtxU=
|
||||
github.com/fxamacker/cbor/v2 v2.8.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
|
||||
github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
|
||||
github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
|
||||
github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
|
||||
github.com/ganigeorgiev/fexpr v0.5.0 h1:XA9JxtTE/Xm+g/JFI6RfZEHSiQlk+1glLvRK1Lpv/Tk=
|
||||
@@ -46,8 +46,8 @@ github.com/go-sql-driver/mysql v1.9.1 h1:FrjNGn/BsJQjVRuSa8CBrM5BWA9BWoXXat3KrtS
|
||||
github.com/go-sql-driver/mysql v1.9.1/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
@@ -103,8 +103,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pocketbase/dbx v1.11.0 h1:LpZezioMfT3K4tLrqA55wWFw1EtH1pM4tzSVa7kgszU=
|
||||
github.com/pocketbase/dbx v1.11.0/go.mod h1:xXRCIAKTHMgUCyCKZm55pUOdvFziJjQfXaWKhu2vhMs=
|
||||
github.com/pocketbase/pocketbase v0.28.4 h1:RmhWXDcfKrFM9/W0G0Zrlv4eKBM8/s/v4SQKytjgD20=
|
||||
github.com/pocketbase/pocketbase v0.28.4/go.mod h1:jSuN93vE/oeJVOz2D2ZxcYyr2bYNmDOMCUkM+JhyJQ0=
|
||||
github.com/pocketbase/pocketbase v0.29.2 h1:MghVgLYy/xh9lBwHtteNSYjYOvHKYD+dS9pzUzOP79Q=
|
||||
github.com/pocketbase/pocketbase v0.29.2/go.mod h1:QZPKtMCWfiDJb0aLhwgj7ZOr6O8tusbui2EhTFAHThU=
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
@@ -114,14 +114,15 @@ github.com/rhysd/go-github-selfupdate v1.2.3/go.mod h1:mp/N8zj6jFfBQy/XMYoWsmfzx
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/shirou/gopsutil/v4 v4.25.6 h1:kLysI2JsKorfaFPcYmcJqbzROzsBWEOAtw6A7dIfqXs=
|
||||
github.com/shirou/gopsutil/v4 v4.25.6/go.mod h1:PfybzyydfZcN+JMMjkF6Zb8Mq1A/VcogFFg7hj50W9c=
|
||||
github.com/shirou/gopsutil/v4 v4.25.7 h1:bNb2JuqKuAu3tRlPv5piSmBZyMfecwQ+t/ILq+1JqVM=
|
||||
github.com/shirou/gopsutil/v4 v4.25.7/go.mod h1:XV/egmwJtd3ZQjBpJVY5kndsiOO4IRqy9TQnmm6VP7U=
|
||||
github.com/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE=
|
||||
github.com/spf13/cast v1.9.2/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
|
||||
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
||||
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
|
||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M=
|
||||
github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
@@ -133,8 +134,8 @@ github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nE
|
||||
github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso=
|
||||
github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
|
||||
github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
|
||||
github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/ulikunitz/xz v0.5.13 h1:ar98gWrjf4H1ev05fYP/o29PDZw9DrI3niHtnEqyuXA=
|
||||
github.com/ulikunitz/xz v0.5.13/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||
@@ -143,29 +144,29 @@ go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
|
||||
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
|
||||
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
|
||||
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o=
|
||||
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8=
|
||||
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
|
||||
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
|
||||
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b h1:DXr+pvt3nC887026GRP39Ej11UATqWDmWuS99x26cD0=
|
||||
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b/go.mod h1:4QTo5u+SEIbbKW1RacMZq1YEfOBqeXa19JeshGi+zc4=
|
||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.28.0 h1:gdem5JW1OLS4FbkWgLO+7ZeFzYtL3xClb97GaUzYMFE=
|
||||
golang.org/x/image v0.28.0/go.mod h1:GUJYXtnGKEUgggyzh+Vxt+AviiCcyiwpsl8iQ8MvwGY=
|
||||
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
|
||||
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/image v0.30.0 h1:jD5RhkmVAnjqaCUXfbGBrn3lpxbknfN9w2UhHHU+5B4=
|
||||
golang.org/x/image v0.30.0/go.mod h1:SAEUTxCCMWSrJcCy/4HwavEsfZZJlYxeHLc6tTiAe/c=
|
||||
golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
|
||||
golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
|
||||
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
|
||||
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
|
||||
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
|
||||
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
|
||||
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -173,19 +174,19 @@ golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
||||
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
|
||||
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
|
||||
golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
|
||||
golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
||||
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
||||
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
|
||||
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
|
||||
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
|
||||
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
|
||||
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
@@ -201,16 +202,22 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
modernc.org/cc/v4 v4.26.1 h1:+X5NtzVBn0KgsBCBe+xkDC7twLb/jNVj9FPgiwSQO3s=
|
||||
modernc.org/cc/v4 v4.26.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
||||
modernc.org/cc/v4 v4.26.2 h1:991HMkLjJzYBIfha6ECZdjrIYz2/1ayr+FL8GN+CNzM=
|
||||
modernc.org/cc/v4 v4.26.3 h1:yEN8dzrkRFnn4PUUKXLYIqVf2PJYAEjMTFjO3BDGc3I=
|
||||
modernc.org/cc/v4 v4.26.3/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
||||
modernc.org/ccgo/v4 v4.28.0 h1:rjznn6WWehKq7dG4JtLRKxb52Ecv8OUGah8+Z/SfpNU=
|
||||
modernc.org/ccgo/v4 v4.28.0/go.mod h1:JygV3+9AV6SmPhDasu4JgquwU81XAKLd3OKTUDNOiKE=
|
||||
modernc.org/fileutil v1.3.3 h1:3qaU+7f7xxTUmvU1pJTZiDLAIoJVdUSSauJNHg9yXoA=
|
||||
modernc.org/fileutil v1.3.3/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
|
||||
modernc.org/fileutil v1.3.8 h1:qtzNm7ED75pd1C7WgAGcK4edm4fvhtBsEiI/0NQ54YM=
|
||||
modernc.org/fileutil v1.3.15 h1:rJAXTP6ilMW/1+kzDiqmBlHLWszheUFXIyGQIAvjJpY=
|
||||
modernc.org/fileutil v1.3.15/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
|
||||
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
|
||||
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
|
||||
modernc.org/libc v1.65.10 h1:ZwEk8+jhW7qBjHIT+wd0d9VjitRyQef9BnzlzGwMODc=
|
||||
modernc.org/libc v1.65.10/go.mod h1:StFvYpx7i/mXtBAfVOjaU0PWZOvIRoZSgXhrwXzr8Po=
|
||||
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
|
||||
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
|
||||
modernc.org/libc v1.66.3 h1:cfCbjTUcdsKyyZZfEUKfoHcP3S0Wkvz3jgSzByEWVCQ=
|
||||
modernc.org/libc v1.66.3/go.mod h1:XD9zO8kt59cANKvHPXpx7yS2ELPheAey0vjIuZOhOU8=
|
||||
modernc.org/libc v1.66.7 h1:rjhZ8OSCybKWxS1CJr0hikpEi6Vg+944Ouyrd+bQsoY=
|
||||
modernc.org/libc v1.66.7/go.mod h1:ln6tbWX0NH+mzApEoDRvilBvAWFt1HX7AUA4VDdVDPM=
|
||||
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
|
||||
@@ -219,8 +226,8 @@ modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
|
||||
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
||||
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
|
||||
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
|
||||
modernc.org/sqlite v1.38.0 h1:+4OrfPQ8pxHKuWG4md1JpR/EYAh3Md7TdejuuzE7EUI=
|
||||
modernc.org/sqlite v1.38.0/go.mod h1:1Bj+yES4SVvBZ4cBOpVZ6QgesMCKpJZDq0nxYzOpmNE=
|
||||
modernc.org/sqlite v1.38.2 h1:Aclu7+tgjgcQVShZqim41Bbw9Cho0y/7WzYptXqkEek=
|
||||
modernc.org/sqlite v1.38.2/go.mod h1:cPTJYSlgg3Sfg046yBShXENNtPrWrDX8bsbAQBzgQ5E=
|
||||
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
|
||||
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
|
||||
@@ -40,13 +40,13 @@ type Agent struct {
|
||||
|
||||
// NewAgent creates a new agent with the given data directory for persisting data.
|
||||
// If the data directory is not set, it will attempt to find the optimal directory.
|
||||
func NewAgent(dataDir string) (agent *Agent, err error) {
|
||||
func NewAgent(dataDir ...string) (agent *Agent, err error) {
|
||||
agent = &Agent{
|
||||
fsStats: make(map[string]*system.FsStats),
|
||||
cache: NewSessionCache(69 * time.Second),
|
||||
}
|
||||
|
||||
agent.dataDir, err = getDataDir(dataDir)
|
||||
agent.dataDir, err = getDataDir(dataDir...)
|
||||
if err != nil {
|
||||
slog.Warn("Data directory not found")
|
||||
} else {
|
||||
@@ -113,37 +113,37 @@ func (a *Agent) gatherStats(sessionID string) *system.CombinedData {
|
||||
a.Lock()
|
||||
defer a.Unlock()
|
||||
|
||||
cachedData, ok := a.cache.Get(sessionID)
|
||||
if ok {
|
||||
slog.Debug("Cached stats", "session", sessionID)
|
||||
return cachedData
|
||||
data, isCached := a.cache.Get(sessionID)
|
||||
if isCached {
|
||||
slog.Debug("Cached data", "session", sessionID)
|
||||
return data
|
||||
}
|
||||
|
||||
*cachedData = system.CombinedData{
|
||||
*data = system.CombinedData{
|
||||
Stats: a.getSystemStats(),
|
||||
Info: a.systemInfo,
|
||||
}
|
||||
slog.Debug("System stats", "data", cachedData)
|
||||
slog.Debug("System data", "data", data)
|
||||
|
||||
if a.dockerManager != nil {
|
||||
if containerStats, err := a.dockerManager.getDockerStats(); err == nil {
|
||||
cachedData.Containers = containerStats
|
||||
slog.Debug("Docker stats", "data", cachedData.Containers)
|
||||
data.Containers = containerStats
|
||||
slog.Debug("Containers", "data", data.Containers)
|
||||
} else {
|
||||
slog.Debug("Docker stats", "err", err)
|
||||
slog.Debug("Containers", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
cachedData.Stats.ExtraFs = make(map[string]*system.FsStats)
|
||||
data.Stats.ExtraFs = make(map[string]*system.FsStats)
|
||||
for name, stats := range a.fsStats {
|
||||
if !stats.Root && stats.DiskTotal > 0 {
|
||||
cachedData.Stats.ExtraFs[name] = stats
|
||||
data.Stats.ExtraFs[name] = stats
|
||||
}
|
||||
}
|
||||
slog.Debug("Extra filesystems", "data", cachedData.Stats.ExtraFs)
|
||||
slog.Debug("Extra FS", "data", data.Stats.ExtraFs)
|
||||
|
||||
a.cache.Set(sessionID, cachedData)
|
||||
return cachedData
|
||||
a.cache.Set(sessionID, data)
|
||||
return data
|
||||
}
|
||||
|
||||
// StartAgent initializes and starts the agent with optional WebSocket connection
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -53,9 +54,9 @@ func newWebSocketClient(agent *Agent) (client *WebSocketClient, err error) {
|
||||
return nil, errors.New("invalid hub URL")
|
||||
}
|
||||
// get registration token
|
||||
client.token, _ = GetEnv("TOKEN")
|
||||
if client.token == "" {
|
||||
return nil, errors.New("TOKEN environment variable not set")
|
||||
client.token, err = getToken()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client.agent = agent
|
||||
@@ -65,6 +66,27 @@ func newWebSocketClient(agent *Agent) (client *WebSocketClient, err error) {
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// getToken returns the token for the WebSocket client.
|
||||
// It first checks the TOKEN environment variable, then the TOKEN_FILE environment variable.
|
||||
// If neither is set, it returns an error.
|
||||
func getToken() (string, error) {
|
||||
// get token from env var
|
||||
token, _ := GetEnv("TOKEN")
|
||||
if token != "" {
|
||||
return token, nil
|
||||
}
|
||||
// get token from file
|
||||
tokenFile, _ := GetEnv("TOKEN_FILE")
|
||||
if tokenFile == "" {
|
||||
return "", errors.New("must set TOKEN or TOKEN_FILE")
|
||||
}
|
||||
tokenBytes, err := os.ReadFile(tokenFile)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(tokenBytes), nil
|
||||
}
|
||||
|
||||
// getOptions returns the WebSocket client options, creating them if necessary.
|
||||
// It configures the connection URL, TLS settings, and authentication headers.
|
||||
func (client *WebSocketClient) getOptions() *gws.ClientOption {
|
||||
|
||||
538
beszel/internal/agent/client_test.go
Normal file
538
beszel/internal/agent/client_test.go
Normal file
@@ -0,0 +1,538 @@
|
||||
//go:build testing
|
||||
// +build testing
|
||||
|
||||
package agent
|
||||
|
||||
import (
|
||||
"beszel"
|
||||
"beszel/internal/common"
|
||||
"crypto/ed25519"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/fxamacker/cbor/v2"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
// TestNewWebSocketClient tests WebSocket client creation
|
||||
func TestNewWebSocketClient(t *testing.T) {
|
||||
agent := createTestAgent(t)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
hubURL string
|
||||
token string
|
||||
expectError bool
|
||||
errorMsg string
|
||||
}{
|
||||
{
|
||||
name: "valid configuration",
|
||||
hubURL: "http://localhost:8080",
|
||||
token: "test-token-123",
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "valid https URL",
|
||||
hubURL: "https://hub.example.com",
|
||||
token: "secure-token",
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "missing hub URL",
|
||||
hubURL: "",
|
||||
token: "test-token",
|
||||
expectError: true,
|
||||
errorMsg: "HUB_URL environment variable not set",
|
||||
},
|
||||
{
|
||||
name: "invalid URL",
|
||||
hubURL: "ht\ttp://invalid",
|
||||
token: "test-token",
|
||||
expectError: true,
|
||||
errorMsg: "invalid hub URL",
|
||||
},
|
||||
{
|
||||
name: "missing token",
|
||||
hubURL: "http://localhost:8080",
|
||||
token: "",
|
||||
expectError: true,
|
||||
errorMsg: "must set TOKEN or TOKEN_FILE",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// Set up environment
|
||||
if tc.hubURL != "" {
|
||||
os.Setenv("BESZEL_AGENT_HUB_URL", tc.hubURL)
|
||||
} else {
|
||||
os.Unsetenv("BESZEL_AGENT_HUB_URL")
|
||||
}
|
||||
if tc.token != "" {
|
||||
os.Setenv("BESZEL_AGENT_TOKEN", tc.token)
|
||||
} else {
|
||||
os.Unsetenv("BESZEL_AGENT_TOKEN")
|
||||
}
|
||||
defer func() {
|
||||
os.Unsetenv("BESZEL_AGENT_HUB_URL")
|
||||
os.Unsetenv("BESZEL_AGENT_TOKEN")
|
||||
}()
|
||||
|
||||
client, err := newWebSocketClient(agent)
|
||||
|
||||
if tc.expectError {
|
||||
assert.Error(t, err)
|
||||
if err != nil && tc.errorMsg != "" {
|
||||
assert.Contains(t, err.Error(), tc.errorMsg)
|
||||
}
|
||||
assert.Nil(t, client)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, client)
|
||||
assert.Equal(t, agent, client.agent)
|
||||
assert.Equal(t, tc.token, client.token)
|
||||
assert.Equal(t, tc.hubURL, client.hubURL.String())
|
||||
assert.NotEmpty(t, client.fingerprint)
|
||||
assert.NotNil(t, client.hubRequest)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestWebSocketClient_GetOptions tests WebSocket client options configuration
|
||||
func TestWebSocketClient_GetOptions(t *testing.T) {
|
||||
agent := createTestAgent(t)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
inputURL string
|
||||
expectedScheme string
|
||||
expectedPath string
|
||||
}{
|
||||
{
|
||||
name: "http to ws conversion",
|
||||
inputURL: "http://localhost:8080",
|
||||
expectedScheme: "ws",
|
||||
expectedPath: "/api/beszel/agent-connect",
|
||||
},
|
||||
{
|
||||
name: "https to wss conversion",
|
||||
inputURL: "https://hub.example.com",
|
||||
expectedScheme: "wss",
|
||||
expectedPath: "/api/beszel/agent-connect",
|
||||
},
|
||||
{
|
||||
name: "existing path preservation",
|
||||
inputURL: "http://localhost:8080/custom/path",
|
||||
expectedScheme: "ws",
|
||||
expectedPath: "/custom/path/api/beszel/agent-connect",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// Set up environment
|
||||
os.Setenv("BESZEL_AGENT_HUB_URL", tc.inputURL)
|
||||
os.Setenv("BESZEL_AGENT_TOKEN", "test-token")
|
||||
defer func() {
|
||||
os.Unsetenv("BESZEL_AGENT_HUB_URL")
|
||||
os.Unsetenv("BESZEL_AGENT_TOKEN")
|
||||
}()
|
||||
|
||||
client, err := newWebSocketClient(agent)
|
||||
require.NoError(t, err)
|
||||
|
||||
options := client.getOptions()
|
||||
|
||||
// Parse the WebSocket URL
|
||||
wsURL, err := url.Parse(options.Addr)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, tc.expectedScheme, wsURL.Scheme)
|
||||
assert.Equal(t, tc.expectedPath, wsURL.Path)
|
||||
|
||||
// Check headers
|
||||
assert.Equal(t, "test-token", options.RequestHeader.Get("X-Token"))
|
||||
assert.Equal(t, beszel.Version, options.RequestHeader.Get("X-Beszel"))
|
||||
assert.Contains(t, options.RequestHeader.Get("User-Agent"), "Mozilla/5.0")
|
||||
|
||||
// Test options caching
|
||||
options2 := client.getOptions()
|
||||
assert.Same(t, options, options2, "Options should be cached")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestWebSocketClient_VerifySignature tests signature verification
|
||||
func TestWebSocketClient_VerifySignature(t *testing.T) {
|
||||
agent := createTestAgent(t)
|
||||
|
||||
// Generate test key pairs
|
||||
_, goodPrivKey, err := ed25519.GenerateKey(nil)
|
||||
require.NoError(t, err)
|
||||
goodPubKey, err := ssh.NewPublicKey(goodPrivKey.Public().(ed25519.PublicKey))
|
||||
require.NoError(t, err)
|
||||
|
||||
_, badPrivKey, err := ed25519.GenerateKey(nil)
|
||||
require.NoError(t, err)
|
||||
badPubKey, err := ssh.NewPublicKey(badPrivKey.Public().(ed25519.PublicKey))
|
||||
require.NoError(t, err)
|
||||
|
||||
// Set up environment
|
||||
os.Setenv("BESZEL_AGENT_HUB_URL", "http://localhost:8080")
|
||||
os.Setenv("BESZEL_AGENT_TOKEN", "test-token")
|
||||
defer func() {
|
||||
os.Unsetenv("BESZEL_AGENT_HUB_URL")
|
||||
os.Unsetenv("BESZEL_AGENT_TOKEN")
|
||||
}()
|
||||
|
||||
client, err := newWebSocketClient(agent)
|
||||
require.NoError(t, err)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
keys []ssh.PublicKey
|
||||
token string
|
||||
signWith ed25519.PrivateKey
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "valid signature with correct key",
|
||||
keys: []ssh.PublicKey{goodPubKey},
|
||||
token: "test-token",
|
||||
signWith: goodPrivKey,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "invalid signature with wrong key",
|
||||
keys: []ssh.PublicKey{goodPubKey},
|
||||
token: "test-token",
|
||||
signWith: badPrivKey,
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "valid signature with multiple keys",
|
||||
keys: []ssh.PublicKey{badPubKey, goodPubKey},
|
||||
token: "test-token",
|
||||
signWith: goodPrivKey,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "no valid keys",
|
||||
keys: []ssh.PublicKey{badPubKey},
|
||||
token: "test-token",
|
||||
signWith: goodPrivKey,
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// Set up agent with test keys
|
||||
agent.keys = tc.keys
|
||||
client.token = tc.token
|
||||
|
||||
// Create signature
|
||||
signature := ed25519.Sign(tc.signWith, []byte(tc.token))
|
||||
|
||||
err := client.verifySignature(signature)
|
||||
|
||||
if tc.expectError {
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "invalid signature")
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestWebSocketClient_HandleHubRequest tests hub request routing (basic verification logic)
|
||||
func TestWebSocketClient_HandleHubRequest(t *testing.T) {
|
||||
agent := createTestAgent(t)
|
||||
|
||||
// Set up environment
|
||||
os.Setenv("BESZEL_AGENT_HUB_URL", "http://localhost:8080")
|
||||
os.Setenv("BESZEL_AGENT_TOKEN", "test-token")
|
||||
defer func() {
|
||||
os.Unsetenv("BESZEL_AGENT_HUB_URL")
|
||||
os.Unsetenv("BESZEL_AGENT_TOKEN")
|
||||
}()
|
||||
|
||||
client, err := newWebSocketClient(agent)
|
||||
require.NoError(t, err)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
action common.WebSocketAction
|
||||
hubVerified bool
|
||||
expectError bool
|
||||
errorMsg string
|
||||
}{
|
||||
{
|
||||
name: "CheckFingerprint without verification",
|
||||
action: common.CheckFingerprint,
|
||||
hubVerified: false,
|
||||
expectError: false, // CheckFingerprint is allowed without verification
|
||||
},
|
||||
{
|
||||
name: "GetData without verification",
|
||||
action: common.GetData,
|
||||
hubVerified: false,
|
||||
expectError: true,
|
||||
errorMsg: "hub not verified",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
client.hubVerified = tc.hubVerified
|
||||
|
||||
// Create minimal request
|
||||
hubRequest := &common.HubRequest[cbor.RawMessage]{
|
||||
Action: tc.action,
|
||||
Data: cbor.RawMessage{},
|
||||
}
|
||||
|
||||
err := client.handleHubRequest(hubRequest)
|
||||
|
||||
if tc.expectError {
|
||||
assert.Error(t, err)
|
||||
if tc.errorMsg != "" {
|
||||
assert.Contains(t, err.Error(), tc.errorMsg)
|
||||
}
|
||||
} else {
|
||||
// For CheckFingerprint, we expect a decode error since we're not providing valid data,
|
||||
// but it shouldn't be the "hub not verified" error
|
||||
if err != nil && tc.errorMsg != "" {
|
||||
assert.NotContains(t, err.Error(), tc.errorMsg)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestWebSocketClient_GetUserAgent tests user agent generation
|
||||
func TestGetUserAgent(t *testing.T) {
|
||||
// Run multiple times to check both variants
|
||||
userAgents := make(map[string]bool)
|
||||
|
||||
for range 20 {
|
||||
ua := getUserAgent()
|
||||
userAgents[ua] = true
|
||||
|
||||
// Check that it's a valid Mozilla user agent
|
||||
assert.Contains(t, ua, "Mozilla/5.0")
|
||||
assert.Contains(t, ua, "AppleWebKit/537.36")
|
||||
assert.Contains(t, ua, "Chrome/124.0.0.0")
|
||||
assert.Contains(t, ua, "Safari/537.36")
|
||||
|
||||
// Should contain either Windows or Mac
|
||||
isWindows := strings.Contains(ua, "Windows NT 11.0")
|
||||
isMac := strings.Contains(ua, "Macintosh; Intel Mac OS X 14_0_0")
|
||||
assert.True(t, isWindows || isMac, "User agent should contain either Windows or Mac identifier")
|
||||
}
|
||||
|
||||
// With enough iterations, we should see both variants
|
||||
// though this might occasionally fail
|
||||
if len(userAgents) == 1 {
|
||||
t.Log("Note: Only one user agent variant was generated in this test run")
|
||||
}
|
||||
}
|
||||
|
||||
// TestWebSocketClient_Close tests connection closing
|
||||
func TestWebSocketClient_Close(t *testing.T) {
|
||||
agent := createTestAgent(t)
|
||||
|
||||
// Set up environment
|
||||
os.Setenv("BESZEL_AGENT_HUB_URL", "http://localhost:8080")
|
||||
os.Setenv("BESZEL_AGENT_TOKEN", "test-token")
|
||||
defer func() {
|
||||
os.Unsetenv("BESZEL_AGENT_HUB_URL")
|
||||
os.Unsetenv("BESZEL_AGENT_TOKEN")
|
||||
}()
|
||||
|
||||
client, err := newWebSocketClient(agent)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Test closing with nil connection (should not panic)
|
||||
assert.NotPanics(t, func() {
|
||||
client.Close()
|
||||
})
|
||||
}
|
||||
|
||||
// TestWebSocketClient_ConnectRateLimit tests connection rate limiting
|
||||
func TestWebSocketClient_ConnectRateLimit(t *testing.T) {
|
||||
agent := createTestAgent(t)
|
||||
|
||||
// Set up environment
|
||||
os.Setenv("BESZEL_AGENT_HUB_URL", "http://localhost:8080")
|
||||
os.Setenv("BESZEL_AGENT_TOKEN", "test-token")
|
||||
defer func() {
|
||||
os.Unsetenv("BESZEL_AGENT_HUB_URL")
|
||||
os.Unsetenv("BESZEL_AGENT_TOKEN")
|
||||
}()
|
||||
|
||||
client, err := newWebSocketClient(agent)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Set recent connection attempt
|
||||
client.lastConnectAttempt = time.Now()
|
||||
|
||||
// Test that connection fails quickly due to rate limiting
|
||||
// This won't actually connect but should fail fast
|
||||
err = client.Connect()
|
||||
assert.Error(t, err, "Connection should fail but not hang")
|
||||
}
|
||||
|
||||
// TestGetToken tests the getToken function with various scenarios
|
||||
func TestGetToken(t *testing.T) {
|
||||
unsetEnvVars := func() {
|
||||
os.Unsetenv("BESZEL_AGENT_TOKEN")
|
||||
os.Unsetenv("TOKEN")
|
||||
os.Unsetenv("BESZEL_AGENT_TOKEN_FILE")
|
||||
os.Unsetenv("TOKEN_FILE")
|
||||
}
|
||||
|
||||
t.Run("token from TOKEN environment variable", func(t *testing.T) {
|
||||
unsetEnvVars()
|
||||
|
||||
// Set TOKEN env var
|
||||
expectedToken := "test-token-from-env"
|
||||
os.Setenv("TOKEN", expectedToken)
|
||||
defer os.Unsetenv("TOKEN")
|
||||
|
||||
token, err := getToken()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expectedToken, token)
|
||||
})
|
||||
|
||||
t.Run("token from BESZEL_AGENT_TOKEN environment variable", func(t *testing.T) {
|
||||
unsetEnvVars()
|
||||
|
||||
// Set BESZEL_AGENT_TOKEN env var (should take precedence)
|
||||
expectedToken := "test-token-from-beszel-env"
|
||||
os.Setenv("BESZEL_AGENT_TOKEN", expectedToken)
|
||||
defer os.Unsetenv("BESZEL_AGENT_TOKEN")
|
||||
|
||||
token, err := getToken()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expectedToken, token)
|
||||
})
|
||||
|
||||
t.Run("token from TOKEN_FILE", func(t *testing.T) {
|
||||
unsetEnvVars()
|
||||
|
||||
// Create a temporary token file
|
||||
expectedToken := "test-token-from-file"
|
||||
tokenFile, err := os.CreateTemp("", "token-test-*.txt")
|
||||
require.NoError(t, err)
|
||||
defer os.Remove(tokenFile.Name())
|
||||
|
||||
_, err = tokenFile.WriteString(expectedToken)
|
||||
require.NoError(t, err)
|
||||
tokenFile.Close()
|
||||
|
||||
// Set TOKEN_FILE env var
|
||||
os.Setenv("TOKEN_FILE", tokenFile.Name())
|
||||
defer os.Unsetenv("TOKEN_FILE")
|
||||
|
||||
token, err := getToken()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expectedToken, token)
|
||||
})
|
||||
|
||||
t.Run("token from BESZEL_AGENT_TOKEN_FILE", func(t *testing.T) {
|
||||
unsetEnvVars()
|
||||
|
||||
// Create a temporary token file
|
||||
expectedToken := "test-token-from-beszel-file"
|
||||
tokenFile, err := os.CreateTemp("", "token-test-*.txt")
|
||||
require.NoError(t, err)
|
||||
defer os.Remove(tokenFile.Name())
|
||||
|
||||
_, err = tokenFile.WriteString(expectedToken)
|
||||
require.NoError(t, err)
|
||||
tokenFile.Close()
|
||||
|
||||
// Set BESZEL_AGENT_TOKEN_FILE env var (should take precedence)
|
||||
os.Setenv("BESZEL_AGENT_TOKEN_FILE", tokenFile.Name())
|
||||
defer os.Unsetenv("BESZEL_AGENT_TOKEN_FILE")
|
||||
|
||||
token, err := getToken()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expectedToken, token)
|
||||
})
|
||||
|
||||
t.Run("TOKEN takes precedence over TOKEN_FILE", func(t *testing.T) {
|
||||
unsetEnvVars()
|
||||
|
||||
// Create a temporary token file
|
||||
fileToken := "token-from-file"
|
||||
tokenFile, err := os.CreateTemp("", "token-test-*.txt")
|
||||
require.NoError(t, err)
|
||||
defer os.Remove(tokenFile.Name())
|
||||
|
||||
_, err = tokenFile.WriteString(fileToken)
|
||||
require.NoError(t, err)
|
||||
tokenFile.Close()
|
||||
|
||||
// Set both TOKEN and TOKEN_FILE
|
||||
envToken := "token-from-env"
|
||||
os.Setenv("TOKEN", envToken)
|
||||
os.Setenv("TOKEN_FILE", tokenFile.Name())
|
||||
defer func() {
|
||||
os.Unsetenv("TOKEN")
|
||||
os.Unsetenv("TOKEN_FILE")
|
||||
}()
|
||||
|
||||
token, err := getToken()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, envToken, token, "TOKEN should take precedence over TOKEN_FILE")
|
||||
})
|
||||
|
||||
t.Run("error when neither TOKEN nor TOKEN_FILE is set", func(t *testing.T) {
|
||||
unsetEnvVars()
|
||||
|
||||
token, err := getToken()
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, "", token)
|
||||
assert.Contains(t, err.Error(), "must set TOKEN or TOKEN_FILE")
|
||||
})
|
||||
|
||||
t.Run("error when TOKEN_FILE points to non-existent file", func(t *testing.T) {
|
||||
unsetEnvVars()
|
||||
|
||||
// Set TOKEN_FILE to a non-existent file
|
||||
os.Setenv("TOKEN_FILE", "/non/existent/file.txt")
|
||||
defer os.Unsetenv("TOKEN_FILE")
|
||||
|
||||
token, err := getToken()
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, "", token)
|
||||
assert.Contains(t, err.Error(), "no such file or directory")
|
||||
})
|
||||
|
||||
t.Run("handles empty token file", func(t *testing.T) {
|
||||
unsetEnvVars()
|
||||
|
||||
// Create an empty token file
|
||||
tokenFile, err := os.CreateTemp("", "token-test-*.txt")
|
||||
require.NoError(t, err)
|
||||
defer os.Remove(tokenFile.Name())
|
||||
tokenFile.Close()
|
||||
|
||||
// Set TOKEN_FILE env var
|
||||
os.Setenv("TOKEN_FILE", tokenFile.Name())
|
||||
defer os.Unsetenv("TOKEN_FILE")
|
||||
|
||||
token, err := getToken()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "", token, "Empty file should return empty string")
|
||||
})
|
||||
}
|
||||
@@ -9,35 +9,30 @@ import (
|
||||
)
|
||||
|
||||
// getDataDir returns the path to the data directory for the agent and an error
|
||||
// if the directory is not valid. Pass an empty string to attempt to find the
|
||||
// optimal data directory.
|
||||
func getDataDir(dataDir string) (string, error) {
|
||||
if dataDir == "" {
|
||||
dataDir, _ = GetEnv("DATA_DIR")
|
||||
// if the directory is not valid. Attempts to find the optimal data directory if
|
||||
// no data directories are provided.
|
||||
func getDataDir(dataDirs ...string) (string, error) {
|
||||
if len(dataDirs) > 0 {
|
||||
return testDataDirs(dataDirs)
|
||||
}
|
||||
|
||||
dataDir, _ := GetEnv("DATA_DIR")
|
||||
if dataDir != "" {
|
||||
return testDataDirs([]string{dataDir})
|
||||
dataDirs = append(dataDirs, dataDir)
|
||||
}
|
||||
|
||||
var dirsToTry []string
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
dirsToTry = []string{
|
||||
dataDirs = append(dataDirs,
|
||||
filepath.Join(os.Getenv("APPDATA"), "beszel-agent"),
|
||||
filepath.Join(os.Getenv("LOCALAPPDATA"), "beszel-agent"),
|
||||
}
|
||||
)
|
||||
} else {
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
dirsToTry = []string{
|
||||
"/var/lib/beszel-agent",
|
||||
filepath.Join(homeDir, ".config", "beszel"),
|
||||
dataDirs = append(dataDirs, "/var/lib/beszel-agent")
|
||||
if homeDir, err := os.UserHomeDir(); err == nil {
|
||||
dataDirs = append(dataDirs, filepath.Join(homeDir, ".config", "beszel"))
|
||||
}
|
||||
}
|
||||
return testDataDirs(dirsToTry)
|
||||
return testDataDirs(dataDirs)
|
||||
}
|
||||
|
||||
func testDataDirs(paths []string) (string, error) {
|
||||
|
||||
@@ -44,15 +44,15 @@ func TestGetDataDir(t *testing.T) {
|
||||
oldValue := os.Getenv("DATA_DIR")
|
||||
defer func() {
|
||||
if oldValue == "" {
|
||||
os.Unsetenv("DATA_DIR")
|
||||
os.Unsetenv("BESZEL_AGENT_DATA_DIR")
|
||||
} else {
|
||||
os.Setenv("DATA_DIR", oldValue)
|
||||
os.Setenv("BESZEL_AGENT_DATA_DIR", oldValue)
|
||||
}
|
||||
}()
|
||||
|
||||
os.Setenv("DATA_DIR", tempDir)
|
||||
os.Setenv("BESZEL_AGENT_DATA_DIR", tempDir)
|
||||
|
||||
result, err := getDataDir("")
|
||||
result, err := getDataDir()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tempDir, result)
|
||||
})
|
||||
@@ -79,7 +79,7 @@ func TestGetDataDir(t *testing.T) {
|
||||
|
||||
// This will try platform-specific defaults, which may or may not work
|
||||
// We're mainly testing that it doesn't panic and returns some result
|
||||
result, err := getDataDir("")
|
||||
result, err := getDataDir()
|
||||
// We don't assert success/failure here since it depends on system permissions
|
||||
// Just verify we get a string result if no error
|
||||
if err == nil {
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"beszel/internal/entities/container"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"encoding/json/v2"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net"
|
||||
@@ -29,7 +29,6 @@ type dockerManager struct {
|
||||
goodDockerVersion bool // Whether docker version is at least 25.0.0 (one-shot works correctly)
|
||||
isWindows bool // Whether the Docker Engine API is running on Windows
|
||||
buf *bytes.Buffer // Buffer to store and read response bodies
|
||||
decoder *json.Decoder // Reusable JSON decoder that reads from buf
|
||||
apiStats *container.ApiStats // Reusable API stats object
|
||||
}
|
||||
|
||||
@@ -343,17 +342,16 @@ func newDockerManager(a *Agent) *dockerManager {
|
||||
// Decodes Docker API JSON response using a reusable buffer and decoder. Not thread safe.
|
||||
func (dm *dockerManager) decode(resp *http.Response, d any) error {
|
||||
if dm.buf == nil {
|
||||
// initialize buffer with 256kb starting size
|
||||
dm.buf = bytes.NewBuffer(make([]byte, 0, 1024*256))
|
||||
dm.decoder = json.NewDecoder(dm.buf)
|
||||
// initialize buffer with 128kb starting size
|
||||
dm.buf = bytes.NewBuffer(make([]byte, 0, 1024*128))
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
defer dm.buf.Reset()
|
||||
dm.buf.Reset()
|
||||
_, err := dm.buf.ReadFrom(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return dm.decoder.Decode(d)
|
||||
return json.Unmarshal(dm.buf.Bytes(), d)
|
||||
}
|
||||
|
||||
// Test docker / podman sockets and return if one exists
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"beszel/internal/entities/system"
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"encoding/json/v2"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
@@ -50,7 +50,7 @@ type GPUManager struct {
|
||||
// RocmSmiJson represents the JSON structure of rocm-smi output
|
||||
type RocmSmiJson struct {
|
||||
ID string `json:"GUID"`
|
||||
Name string `json:"Card series"`
|
||||
Name string `json:"Card Series"`
|
||||
Temperature string `json:"Temperature (Sensor edge) (C)"`
|
||||
MemoryUsed string `json:"VRAM Total Used Memory (B)"`
|
||||
MemoryTotal string `json:"VRAM Total Memory (B)"`
|
||||
|
||||
80
beszel/internal/agent/lhm/beszel_lhm.cs
Normal file
80
beszel/internal/agent/lhm/beszel_lhm.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using LibreHardwareMonitor.Hardware;
|
||||
|
||||
class Program
|
||||
{
|
||||
static void Main()
|
||||
{
|
||||
var computer = new Computer
|
||||
{
|
||||
IsCpuEnabled = true,
|
||||
IsGpuEnabled = true,
|
||||
IsMemoryEnabled = true,
|
||||
IsMotherboardEnabled = true,
|
||||
IsStorageEnabled = true,
|
||||
// IsPsuEnabled = true,
|
||||
// IsNetworkEnabled = true,
|
||||
};
|
||||
computer.Open();
|
||||
|
||||
var reader = Console.In;
|
||||
var writer = Console.Out;
|
||||
|
||||
string line;
|
||||
while ((line = reader.ReadLine()) != null)
|
||||
{
|
||||
if (line.Trim().Equals("getTemps", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
foreach (var hw in computer.Hardware)
|
||||
{
|
||||
// process main hardware sensors
|
||||
ProcessSensors(hw, writer);
|
||||
|
||||
// process subhardware sensors
|
||||
foreach (var subhardware in hw.SubHardware)
|
||||
{
|
||||
ProcessSensors(subhardware, writer);
|
||||
}
|
||||
}
|
||||
// send empty line to signal end of sensor data
|
||||
writer.WriteLine();
|
||||
writer.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
computer.Close();
|
||||
}
|
||||
|
||||
static void ProcessSensors(IHardware hardware, System.IO.TextWriter writer)
|
||||
{
|
||||
var updated = false;
|
||||
foreach (var sensor in hardware.Sensors)
|
||||
{
|
||||
var validTemp = sensor.SensorType == SensorType.Temperature && sensor.Value.HasValue;
|
||||
if (!validTemp || sensor.Name.Contains("Distance"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!updated)
|
||||
{
|
||||
hardware.Update();
|
||||
updated = true;
|
||||
}
|
||||
|
||||
var name = sensor.Name;
|
||||
// if sensor.Name starts with "Temperature" replace with hardware.Identifier but retain the rest of the name.
|
||||
// usually this is a number like Temperature 3
|
||||
if (sensor.Name.StartsWith("Temperature"))
|
||||
{
|
||||
name = hardware.Identifier.ToString().Replace("/", "_").TrimStart('_') + sensor.Name.Substring(11);
|
||||
}
|
||||
|
||||
// invariant culture assures the value is parsable as a float
|
||||
var value = sensor.Value.Value.ToString("0.##", CultureInfo.InvariantCulture);
|
||||
// write the name and value to the writer
|
||||
writer.WriteLine($"{name}|{value}");
|
||||
}
|
||||
}
|
||||
}
|
||||
11
beszel/internal/agent/lhm/beszel_lhm.csproj
Normal file
11
beszel/internal/agent/lhm/beszel_lhm.csproj
Normal file
@@ -0,0 +1,11 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net48</TargetFramework>
|
||||
<Platforms>x64</Platforms>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="LibreHardwareMonitorLib" Version="0.9.4" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -84,10 +84,10 @@ func (a *Agent) updateTemperatures(systemStats *system.Stats) {
|
||||
// reset high temp
|
||||
a.systemInfo.DashboardTemp = 0
|
||||
|
||||
temps, err := a.getTempsWithPanicRecovery(sensors.TemperaturesWithContext)
|
||||
temps, err := a.getTempsWithPanicRecovery(getSensorTemps)
|
||||
if err != nil {
|
||||
// retry once on panic (gopsutil/issues/1832)
|
||||
temps, err = a.getTempsWithPanicRecovery(sensors.TemperaturesWithContext)
|
||||
temps, err = a.getTempsWithPanicRecovery(getSensorTemps)
|
||||
if err != nil {
|
||||
slog.Warn("Error updating temperatures", "err", err)
|
||||
if len(systemStats.Temperatures) > 0 {
|
||||
|
||||
9
beszel/internal/agent/sensors_default.go
Normal file
9
beszel/internal/agent/sensors_default.go
Normal file
@@ -0,0 +1,9 @@
|
||||
//go:build !windows
|
||||
|
||||
package agent
|
||||
|
||||
import (
|
||||
"github.com/shirou/gopsutil/v4/sensors"
|
||||
)
|
||||
|
||||
var getSensorTemps = sensors.TemperaturesWithContext
|
||||
281
beszel/internal/agent/sensors_windows.go
Normal file
281
beszel/internal/agent/sensors_windows.go
Normal file
@@ -0,0 +1,281 @@
|
||||
//go:build windows
|
||||
|
||||
//go:generate dotnet build -c Release lhm/beszel_lhm.csproj
|
||||
|
||||
package agent
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"embed"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/shirou/gopsutil/v4/sensors"
|
||||
)
|
||||
|
||||
// Note: This is always called from Agent.gatherStats() which holds Agent.Lock(),
|
||||
// so no internal concurrency protection is needed.
|
||||
|
||||
// lhmProcess is a wrapper around the LHM .NET process.
|
||||
type lhmProcess struct {
|
||||
cmd *exec.Cmd
|
||||
stdin io.WriteCloser
|
||||
stdout io.ReadCloser
|
||||
scanner *bufio.Scanner
|
||||
isRunning bool
|
||||
stoppedNoSensors bool
|
||||
consecutiveNoSensors uint8
|
||||
execPath string
|
||||
tempDir string
|
||||
}
|
||||
|
||||
//go:embed all:lhm/bin/Release/net48
|
||||
var lhmFs embed.FS
|
||||
|
||||
var (
|
||||
beszelLhm *lhmProcess
|
||||
beszelLhmOnce sync.Once
|
||||
)
|
||||
|
||||
var errNoSensors = errors.New("no sensors found (try running as admin)")
|
||||
|
||||
// newlhmProcess copies the embedded LHM executable to a temporary directory and starts it.
|
||||
func newlhmProcess() (*lhmProcess, error) {
|
||||
destDir := filepath.Join(os.TempDir(), "beszel")
|
||||
execPath := filepath.Join(destDir, "beszel_lhm.exe")
|
||||
|
||||
if err := os.MkdirAll(destDir, 0755); err != nil {
|
||||
return nil, fmt.Errorf("failed to create temp directory: %w", err)
|
||||
}
|
||||
|
||||
// Only copy if executable doesn't exist
|
||||
if _, err := os.Stat(execPath); os.IsNotExist(err) {
|
||||
if err := copyEmbeddedDir(lhmFs, "lhm/bin/Release/net48", destDir); err != nil {
|
||||
return nil, fmt.Errorf("failed to copy embedded directory: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
lhm := &lhmProcess{
|
||||
execPath: execPath,
|
||||
tempDir: destDir,
|
||||
}
|
||||
|
||||
if err := lhm.startProcess(); err != nil {
|
||||
return nil, fmt.Errorf("failed to start process: %w", err)
|
||||
}
|
||||
|
||||
return lhm, nil
|
||||
}
|
||||
|
||||
// startProcess starts the external LHM process
|
||||
func (lhm *lhmProcess) startProcess() error {
|
||||
// Clean up any existing process
|
||||
lhm.cleanupProcess()
|
||||
|
||||
cmd := exec.Command(lhm.execPath)
|
||||
stdin, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
stdin.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
stdin.Close()
|
||||
stdout.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
// Update process state
|
||||
lhm.cmd = cmd
|
||||
lhm.stdin = stdin
|
||||
lhm.stdout = stdout
|
||||
lhm.scanner = bufio.NewScanner(stdout)
|
||||
lhm.isRunning = true
|
||||
|
||||
// Give process a moment to initialize
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// cleanupProcess terminates the process and closes resources but preserves files
|
||||
func (lhm *lhmProcess) cleanupProcess() {
|
||||
lhm.isRunning = false
|
||||
|
||||
if lhm.cmd != nil && lhm.cmd.Process != nil {
|
||||
lhm.cmd.Process.Kill()
|
||||
lhm.cmd.Wait()
|
||||
}
|
||||
|
||||
if lhm.stdin != nil {
|
||||
lhm.stdin.Close()
|
||||
lhm.stdin = nil
|
||||
}
|
||||
if lhm.stdout != nil {
|
||||
lhm.stdout.Close()
|
||||
lhm.stdout = nil
|
||||
}
|
||||
|
||||
lhm.cmd = nil
|
||||
lhm.scanner = nil
|
||||
lhm.stoppedNoSensors = false
|
||||
lhm.consecutiveNoSensors = 0
|
||||
}
|
||||
|
||||
func (lhm *lhmProcess) getTemps(ctx context.Context) (temps []sensors.TemperatureStat, err error) {
|
||||
if lhm.stoppedNoSensors {
|
||||
// Fall back to gopsutil if we can't get sensors from LHM
|
||||
return sensors.TemperaturesWithContext(ctx)
|
||||
}
|
||||
|
||||
// Start process if it's not running
|
||||
if !lhm.isRunning || lhm.stdin == nil || lhm.scanner == nil {
|
||||
err := lhm.startProcess()
|
||||
if err != nil {
|
||||
return temps, err
|
||||
}
|
||||
}
|
||||
|
||||
// Send command to process
|
||||
_, err = fmt.Fprintln(lhm.stdin, "getTemps")
|
||||
if err != nil {
|
||||
lhm.isRunning = false
|
||||
return temps, fmt.Errorf("failed to send command: %w", err)
|
||||
}
|
||||
|
||||
// Read all sensor lines until we hit an empty line or EOF
|
||||
for lhm.scanner.Scan() {
|
||||
line := strings.TrimSpace(lhm.scanner.Text())
|
||||
if line == "" {
|
||||
break
|
||||
}
|
||||
|
||||
parts := strings.Split(line, "|")
|
||||
if len(parts) != 2 {
|
||||
slog.Debug("Invalid sensor format", "line", line)
|
||||
continue
|
||||
}
|
||||
|
||||
name := strings.TrimSpace(parts[0])
|
||||
valueStr := strings.TrimSpace(parts[1])
|
||||
|
||||
value, err := strconv.ParseFloat(valueStr, 64)
|
||||
if err != nil {
|
||||
slog.Debug("Failed to parse sensor", "err", err, "line", line)
|
||||
continue
|
||||
}
|
||||
|
||||
if name == "" || value <= 0 || value > 150 {
|
||||
slog.Debug("Invalid sensor", "name", name, "val", value, "line", line)
|
||||
continue
|
||||
}
|
||||
|
||||
temps = append(temps, sensors.TemperatureStat{
|
||||
SensorKey: name,
|
||||
Temperature: value,
|
||||
})
|
||||
}
|
||||
|
||||
if err := lhm.scanner.Err(); err != nil {
|
||||
lhm.isRunning = false
|
||||
return temps, err
|
||||
}
|
||||
|
||||
// Handle no sensors case
|
||||
if len(temps) == 0 {
|
||||
lhm.consecutiveNoSensors++
|
||||
if lhm.consecutiveNoSensors >= 3 {
|
||||
lhm.stoppedNoSensors = true
|
||||
slog.Warn(errNoSensors.Error())
|
||||
lhm.cleanup()
|
||||
}
|
||||
return sensors.TemperaturesWithContext(ctx)
|
||||
}
|
||||
|
||||
lhm.consecutiveNoSensors = 0
|
||||
|
||||
return temps, nil
|
||||
}
|
||||
|
||||
// getSensorTemps attempts to pull sensor temperatures from the embedded LHM process.
|
||||
// NB: LibreHardwareMonitorLib requires admin privileges to access all available sensors.
|
||||
func getSensorTemps(ctx context.Context) (temps []sensors.TemperatureStat, err error) {
|
||||
defer func() {
|
||||
if err != nil {
|
||||
slog.Debug("Error reading sensors", "err", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Initialize process once
|
||||
beszelLhmOnce.Do(func() {
|
||||
beszelLhm, err = newlhmProcess()
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return temps, fmt.Errorf("failed to initialize lhm: %w", err)
|
||||
}
|
||||
|
||||
if beszelLhm == nil {
|
||||
return temps, fmt.Errorf("lhm not available")
|
||||
}
|
||||
|
||||
return beszelLhm.getTemps(ctx)
|
||||
}
|
||||
|
||||
// cleanup terminates the process and closes resources
|
||||
func (lhm *lhmProcess) cleanup() {
|
||||
lhm.cleanupProcess()
|
||||
if lhm.tempDir != "" {
|
||||
os.RemoveAll(lhm.tempDir)
|
||||
}
|
||||
}
|
||||
|
||||
// copyEmbeddedDir copies the embedded directory to the destination path
|
||||
func copyEmbeddedDir(fs embed.FS, srcPath, destPath string) error {
|
||||
entries, err := fs.ReadDir(srcPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.MkdirAll(destPath, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
srcEntryPath := path.Join(srcPath, entry.Name())
|
||||
destEntryPath := filepath.Join(destPath, entry.Name())
|
||||
|
||||
if entry.IsDir() {
|
||||
if err := copyEmbeddedDir(fs, srcEntryPath, destEntryPath); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
data, err := fs.ReadFile(srcEntryPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.WriteFile(destEntryPath, data, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -4,7 +4,8 @@ import (
|
||||
"beszel"
|
||||
"beszel/internal/common"
|
||||
"beszel/internal/entities/system"
|
||||
"encoding/json"
|
||||
"encoding/json/jsontext"
|
||||
"encoding/json/v2"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -144,7 +145,7 @@ func (a *Agent) writeToSession(w io.Writer, stats *system.CombinedData, hubVersi
|
||||
if hubVersion.GTE(beszel.MinVersionCbor) {
|
||||
return cbor.NewEncoder(w).Encode(stats)
|
||||
}
|
||||
return json.NewEncoder(w).Encode(stats)
|
||||
return json.MarshalEncode(jsontext.NewEncoder(w), stats)
|
||||
}
|
||||
|
||||
// extractHubVersion extracts the beszel version from SSH client version string.
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"beszel/internal/entities/system"
|
||||
"context"
|
||||
"crypto/ed25519"
|
||||
"encoding/json"
|
||||
"encoding/json/v2"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
@@ -473,11 +473,11 @@ func TestWriteToSessionEncoding(t *testing.T) {
|
||||
hubVersion: "0.12.0-beta0",
|
||||
expectedUsesCbor: false,
|
||||
},
|
||||
{
|
||||
name: "matching beta version should use CBOR",
|
||||
hubVersion: "0.12.0-beta2",
|
||||
expectedUsesCbor: true,
|
||||
},
|
||||
// {
|
||||
// name: "matching beta version should use CBOR",
|
||||
// hubVersion: "0.12.0-beta2",
|
||||
// expectedUsesCbor: true,
|
||||
// },
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
||||
@@ -80,10 +80,11 @@ func (a *Agent) getSystemStats() system.Stats {
|
||||
|
||||
// load average
|
||||
if avgstat, err := load.Avg(); err == nil {
|
||||
systemStats.LoadAvg1 = twoDecimals(avgstat.Load1)
|
||||
systemStats.LoadAvg5 = twoDecimals(avgstat.Load5)
|
||||
systemStats.LoadAvg15 = twoDecimals(avgstat.Load15)
|
||||
slog.Debug("Load average", "5m", systemStats.LoadAvg5, "15m", systemStats.LoadAvg15)
|
||||
// TODO: remove these in future release in favor of load avg array
|
||||
systemStats.LoadAvg[0] = avgstat.Load1
|
||||
systemStats.LoadAvg[1] = avgstat.Load5
|
||||
systemStats.LoadAvg[2] = avgstat.Load15
|
||||
slog.Debug("Load average", "5m", avgstat.Load5, "15m", avgstat.Load15)
|
||||
} else {
|
||||
slog.Error("Error getting load average", "err", err)
|
||||
}
|
||||
@@ -174,24 +175,27 @@ func (a *Agent) getSystemStats() system.Stats {
|
||||
a.initializeNetIoStats()
|
||||
}
|
||||
if netIO, err := psutilNet.IOCounters(true); err == nil {
|
||||
secondsElapsed := time.Since(a.netIoStats.Time).Seconds()
|
||||
msElapsed := uint64(time.Since(a.netIoStats.Time).Milliseconds())
|
||||
a.netIoStats.Time = time.Now()
|
||||
bytesSent := uint64(0)
|
||||
bytesRecv := uint64(0)
|
||||
totalBytesSent := uint64(0)
|
||||
totalBytesRecv := uint64(0)
|
||||
// sum all bytes sent and received
|
||||
for _, v := range netIO {
|
||||
// skip if not in valid network interfaces list
|
||||
if _, exists := a.netInterfaces[v.Name]; !exists {
|
||||
continue
|
||||
}
|
||||
bytesSent += v.BytesSent
|
||||
bytesRecv += v.BytesRecv
|
||||
totalBytesSent += v.BytesSent
|
||||
totalBytesRecv += v.BytesRecv
|
||||
}
|
||||
// add to systemStats
|
||||
sentPerSecond := float64(bytesSent-a.netIoStats.BytesSent) / secondsElapsed
|
||||
recvPerSecond := float64(bytesRecv-a.netIoStats.BytesRecv) / secondsElapsed
|
||||
networkSentPs := bytesToMegabytes(sentPerSecond)
|
||||
networkRecvPs := bytesToMegabytes(recvPerSecond)
|
||||
var bytesSentPerSecond, bytesRecvPerSecond uint64
|
||||
if msElapsed > 0 {
|
||||
bytesSentPerSecond = (totalBytesSent - a.netIoStats.BytesSent) * 1000 / msElapsed
|
||||
bytesRecvPerSecond = (totalBytesRecv - a.netIoStats.BytesRecv) * 1000 / msElapsed
|
||||
}
|
||||
networkSentPs := bytesToMegabytes(float64(bytesSentPerSecond))
|
||||
networkRecvPs := bytesToMegabytes(float64(bytesRecvPerSecond))
|
||||
// add check for issue (#150) where sent is a massive number
|
||||
if networkSentPs > 10_000 || networkRecvPs > 10_000 {
|
||||
slog.Warn("Invalid net stats. Resetting.", "sent", networkSentPs, "recv", networkRecvPs)
|
||||
@@ -206,9 +210,10 @@ func (a *Agent) getSystemStats() system.Stats {
|
||||
} else {
|
||||
systemStats.NetworkSent = networkSentPs
|
||||
systemStats.NetworkRecv = networkRecvPs
|
||||
systemStats.Bandwidth[0], systemStats.Bandwidth[1] = bytesSentPerSecond, bytesRecvPerSecond
|
||||
// update netIoStats
|
||||
a.netIoStats.BytesSent = bytesSent
|
||||
a.netIoStats.BytesRecv = bytesRecv
|
||||
a.netIoStats.BytesSent = totalBytesSent
|
||||
a.netIoStats.BytesRecv = totalBytesRecv
|
||||
}
|
||||
}
|
||||
|
||||
@@ -251,13 +256,17 @@ func (a *Agent) getSystemStats() system.Stats {
|
||||
|
||||
// update base system info
|
||||
a.systemInfo.Cpu = systemStats.Cpu
|
||||
a.systemInfo.LoadAvg1 = systemStats.LoadAvg1
|
||||
a.systemInfo.LoadAvg5 = systemStats.LoadAvg5
|
||||
a.systemInfo.LoadAvg15 = systemStats.LoadAvg15
|
||||
a.systemInfo.LoadAvg = systemStats.LoadAvg
|
||||
// TODO: remove these in future release in favor of load avg array
|
||||
a.systemInfo.LoadAvg1 = systemStats.LoadAvg[0]
|
||||
a.systemInfo.LoadAvg5 = systemStats.LoadAvg[1]
|
||||
a.systemInfo.LoadAvg15 = systemStats.LoadAvg[2]
|
||||
a.systemInfo.MemPct = systemStats.MemPct
|
||||
a.systemInfo.DiskPct = systemStats.DiskPct
|
||||
a.systemInfo.Uptime, _ = host.Uptime()
|
||||
// TODO: in future release, remove MB bandwidth values in favor of bytes
|
||||
a.systemInfo.Bandwidth = twoDecimals(systemStats.NetworkSent + systemStats.NetworkRecv)
|
||||
a.systemInfo.BandwidthBytes = systemStats.Bandwidth[0] + systemStats.Bandwidth[1]
|
||||
slog.Debug("sysinfo", "data", a.systemInfo)
|
||||
|
||||
return systemStats
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
|
||||
"github.com/nicholas-fedor/shoutrrr"
|
||||
"github.com/pocketbase/dbx"
|
||||
"github.com/pocketbase/pocketbase/apis"
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
"github.com/pocketbase/pocketbase/tools/mailer"
|
||||
)
|
||||
@@ -47,9 +46,7 @@ type SystemAlertStats struct {
|
||||
NetSent float64 `json:"ns"`
|
||||
NetRecv float64 `json:"nr"`
|
||||
Temperatures map[string]float32 `json:"t"`
|
||||
LoadAvg1 float64 `json:"l1"`
|
||||
LoadAvg5 float64 `json:"l5"`
|
||||
LoadAvg15 float64 `json:"l15"`
|
||||
LoadAvg [3]float64 `json:"la"`
|
||||
}
|
||||
|
||||
type SystemAlertData struct {
|
||||
@@ -93,10 +90,18 @@ func NewAlertManager(app hubLike) *AlertManager {
|
||||
alertQueue: make(chan alertTask),
|
||||
stopChan: make(chan struct{}),
|
||||
}
|
||||
am.bindEvents()
|
||||
go am.startWorker()
|
||||
return am
|
||||
}
|
||||
|
||||
// Bind events to the alerts collection lifecycle
|
||||
func (am *AlertManager) bindEvents() {
|
||||
am.hub.OnRecordAfterUpdateSuccess("alerts").BindFunc(updateHistoryOnAlertUpdate)
|
||||
am.hub.OnRecordAfterDeleteSuccess("alerts").BindFunc(resolveHistoryOnAlertDelete)
|
||||
}
|
||||
|
||||
// SendAlert sends an alert to the user
|
||||
func (am *AlertManager) SendAlert(data AlertMessageData) error {
|
||||
// get user settings
|
||||
record, err := am.hub.FindFirstRecordByFilter(
|
||||
@@ -200,16 +205,14 @@ func (am *AlertManager) SendShoutrrrAlert(notificationUrl, title, message, link,
|
||||
}
|
||||
|
||||
func (am *AlertManager) SendTestNotification(e *core.RequestEvent) error {
|
||||
info, _ := e.RequestInfo()
|
||||
if info.Auth == nil {
|
||||
return apis.NewForbiddenError("Forbidden", nil)
|
||||
var data struct {
|
||||
URL string `json:"url"`
|
||||
}
|
||||
url := e.Request.URL.Query().Get("url")
|
||||
// log.Println("url", url)
|
||||
if url == "" {
|
||||
return e.JSON(200, map[string]string{"err": "URL is required"})
|
||||
err := e.BindBody(&data)
|
||||
if err != nil || data.URL == "" {
|
||||
return e.BadRequestError("URL is required", err)
|
||||
}
|
||||
err := am.SendShoutrrrAlert(url, "Test Alert", "This is a notification from Beszel.", am.hub.Settings().Meta.AppURL, "View Beszel")
|
||||
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()})
|
||||
}
|
||||
|
||||
119
beszel/internal/alerts/alerts_api.go
Normal file
119
beszel/internal/alerts/alerts_api.go
Normal file
@@ -0,0 +1,119 @@
|
||||
package alerts
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/pocketbase/dbx"
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
)
|
||||
|
||||
// UpsertUserAlerts handles API request to create or update alerts for a user
|
||||
// across multiple systems (POST /api/beszel/user-alerts)
|
||||
func UpsertUserAlerts(e *core.RequestEvent) error {
|
||||
userID := e.Auth.Id
|
||||
|
||||
reqData := struct {
|
||||
Min uint8 `json:"min"`
|
||||
Value float64 `json:"value"`
|
||||
Name string `json:"name"`
|
||||
Systems []string `json:"systems"`
|
||||
Overwrite bool `json:"overwrite"`
|
||||
}{}
|
||||
err := e.BindBody(&reqData)
|
||||
if err != nil || userID == "" || reqData.Name == "" || len(reqData.Systems) == 0 {
|
||||
return e.BadRequestError("Bad data", err)
|
||||
}
|
||||
|
||||
alertsCollection, err := e.App.FindCachedCollectionByNameOrId("alerts")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = e.App.RunInTransaction(func(txApp core.App) error {
|
||||
for _, systemId := range reqData.Systems {
|
||||
// find existing matching alert
|
||||
alertRecord, err := txApp.FindFirstRecordByFilter(alertsCollection,
|
||||
"system={:system} && name={:name} && user={:user}",
|
||||
dbx.Params{"system": systemId, "name": reqData.Name, "user": userID})
|
||||
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return err
|
||||
}
|
||||
|
||||
// skip if alert already exists and overwrite is not set
|
||||
if !reqData.Overwrite && alertRecord != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// create new alert if it doesn't exist
|
||||
if alertRecord == nil {
|
||||
alertRecord = core.NewRecord(alertsCollection)
|
||||
alertRecord.Set("user", userID)
|
||||
alertRecord.Set("system", systemId)
|
||||
alertRecord.Set("name", reqData.Name)
|
||||
}
|
||||
|
||||
alertRecord.Set("value", reqData.Value)
|
||||
alertRecord.Set("min", reqData.Min)
|
||||
|
||||
if err := txApp.SaveNoValidate(alertRecord); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return e.JSON(http.StatusOK, map[string]any{"success": true})
|
||||
}
|
||||
|
||||
// DeleteUserAlerts handles API request to delete alerts for a user across multiple systems
|
||||
// (DELETE /api/beszel/user-alerts)
|
||||
func DeleteUserAlerts(e *core.RequestEvent) error {
|
||||
userID := e.Auth.Id
|
||||
|
||||
reqData := struct {
|
||||
AlertName string `json:"name"`
|
||||
Systems []string `json:"systems"`
|
||||
}{}
|
||||
err := e.BindBody(&reqData)
|
||||
if err != nil || userID == "" || reqData.AlertName == "" || len(reqData.Systems) == 0 {
|
||||
return e.BadRequestError("Bad data", err)
|
||||
}
|
||||
|
||||
var numDeleted uint16
|
||||
|
||||
err = e.App.RunInTransaction(func(txApp core.App) error {
|
||||
for _, systemId := range reqData.Systems {
|
||||
// Find existing alert to delete
|
||||
alertRecord, err := txApp.FindFirstRecordByFilter("alerts",
|
||||
"system={:system} && name={:name} && user={:user}",
|
||||
dbx.Params{"system": systemId, "name": reqData.AlertName, "user": userID})
|
||||
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
// alert doesn't exist, continue to next system
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
if err := txApp.Delete(alertRecord); err != nil {
|
||||
return err
|
||||
}
|
||||
numDeleted++
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return e.JSON(http.StatusOK, map[string]any{"success": true, "count": numDeleted})
|
||||
}
|
||||
85
beszel/internal/alerts/alerts_history.go
Normal file
85
beszel/internal/alerts/alerts_history.go
Normal file
@@ -0,0 +1,85 @@
|
||||
package alerts
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/pocketbase/dbx"
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
)
|
||||
|
||||
// On triggered alert record delete, set matching alert history record to resolved
|
||||
func resolveHistoryOnAlertDelete(e *core.RecordEvent) error {
|
||||
if !e.Record.GetBool("triggered") {
|
||||
return e.Next()
|
||||
}
|
||||
_ = resolveAlertHistoryRecord(e.App, e.Record.Id)
|
||||
return e.Next()
|
||||
}
|
||||
|
||||
// On alert record update, update alert history record
|
||||
func updateHistoryOnAlertUpdate(e *core.RecordEvent) error {
|
||||
original := e.Record.Original()
|
||||
new := e.Record
|
||||
|
||||
originalTriggered := original.GetBool("triggered")
|
||||
newTriggered := new.GetBool("triggered")
|
||||
|
||||
// no need to update alert history if triggered state has not changed
|
||||
if originalTriggered == newTriggered {
|
||||
return e.Next()
|
||||
}
|
||||
|
||||
// if new state is triggered, create new alert history record
|
||||
if newTriggered {
|
||||
_, _ = createAlertHistoryRecord(e.App, new)
|
||||
return e.Next()
|
||||
}
|
||||
|
||||
// if new state is not triggered, check for matching alert history record and set it to resolved
|
||||
_ = resolveAlertHistoryRecord(e.App, new.Id)
|
||||
return e.Next()
|
||||
}
|
||||
|
||||
// resolveAlertHistoryRecord sets the resolved field to the current time
|
||||
func resolveAlertHistoryRecord(app core.App, alertRecordID string) error {
|
||||
alertHistoryRecords, err := app.FindRecordsByFilter(
|
||||
"alerts_history",
|
||||
"alert_id={:alert_id} && resolved=null",
|
||||
"-created",
|
||||
1,
|
||||
0,
|
||||
dbx.Params{"alert_id": alertRecordID},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(alertHistoryRecords) == 0 {
|
||||
return nil
|
||||
}
|
||||
alertHistoryRecord := alertHistoryRecords[0] // there should be only one record
|
||||
alertHistoryRecord.Set("resolved", time.Now().UTC())
|
||||
err = app.Save(alertHistoryRecord)
|
||||
if err != nil {
|
||||
app.Logger().Error("Failed to resolve alert history", "err", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// createAlertHistoryRecord creates a new alert history record
|
||||
func createAlertHistoryRecord(app core.App, alertRecord *core.Record) (alertHistoryRecord *core.Record, err error) {
|
||||
alertHistoryCollection, err := app.FindCachedCollectionByNameOrId("alerts_history")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
alertHistoryRecord = core.NewRecord(alertHistoryCollection)
|
||||
alertHistoryRecord.Set("alert_id", alertRecord.Id)
|
||||
alertHistoryRecord.Set("user", alertRecord.GetString("user"))
|
||||
alertHistoryRecord.Set("system", alertRecord.GetString("system"))
|
||||
alertHistoryRecord.Set("name", alertRecord.GetString("name"))
|
||||
alertHistoryRecord.Set("value", alertRecord.GetFloat("value"))
|
||||
err = app.Save(alertHistoryRecord)
|
||||
if err != nil {
|
||||
app.Logger().Error("Failed to save alert history", "err", err)
|
||||
}
|
||||
return alertHistoryRecord, err
|
||||
}
|
||||
@@ -136,6 +136,14 @@ func (am *AlertManager) handleSystemUp(systemName string, alertRecords []*core.R
|
||||
|
||||
// sendStatusAlert sends a status alert ("up" or "down") to the users associated with the alert records.
|
||||
func (am *AlertManager) sendStatusAlert(alertStatus string, systemName string, alertRecord *core.Record) error {
|
||||
switch alertStatus {
|
||||
case "up":
|
||||
alertRecord.Set("triggered", false)
|
||||
case "down":
|
||||
alertRecord.Set("triggered", true)
|
||||
}
|
||||
am.hub.Save(alertRecord)
|
||||
|
||||
var emoji string
|
||||
if alertStatus == "up" {
|
||||
emoji = "\u2705" // Green checkmark emoji
|
||||
@@ -146,16 +154,16 @@ func (am *AlertManager) sendStatusAlert(alertStatus string, systemName string, a
|
||||
title := fmt.Sprintf("Connection to %s is %s %v", systemName, alertStatus, emoji)
|
||||
message := strings.TrimSuffix(title, emoji)
|
||||
|
||||
if errs := am.hub.ExpandRecord(alertRecord, []string{"user"}, nil); len(errs) > 0 {
|
||||
return errs["user"]
|
||||
}
|
||||
user := alertRecord.ExpandedOne("user")
|
||||
if user == nil {
|
||||
return nil
|
||||
}
|
||||
// if errs := am.hub.ExpandRecord(alertRecord, []string{"user"}, nil); len(errs) > 0 {
|
||||
// return errs["user"]
|
||||
// }
|
||||
// user := alertRecord.ExpandedOne("user")
|
||||
// if user == nil {
|
||||
// return nil
|
||||
// }
|
||||
|
||||
return am.SendAlert(AlertMessageData{
|
||||
UserID: user.Id,
|
||||
UserID: alertRecord.GetString("user"),
|
||||
Title: title,
|
||||
Message: message,
|
||||
Link: am.hub.MakeLink("system", systemName),
|
||||
|
||||
@@ -2,7 +2,7 @@ package alerts
|
||||
|
||||
import (
|
||||
"beszel/internal/entities/system"
|
||||
"encoding/json"
|
||||
"encoding/json/v2"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
|
||||
func (am *AlertManager) HandleSystemAlerts(systemRecord *core.Record, data *system.CombinedData) error {
|
||||
alertRecords, err := am.hub.FindAllRecords("alerts",
|
||||
dbx.NewExp("system={:system}", dbx.Params{"system": systemRecord.Id}),
|
||||
dbx.NewExp("system={:system} AND name!='Status'", dbx.Params{"system": systemRecord.Id}),
|
||||
)
|
||||
if err != nil || len(alertRecords) == 0 {
|
||||
// log.Println("no alerts found for system")
|
||||
@@ -55,13 +55,13 @@ func (am *AlertManager) HandleSystemAlerts(systemRecord *core.Record, data *syst
|
||||
val = data.Info.DashboardTemp
|
||||
unit = "°C"
|
||||
case "LoadAvg1":
|
||||
val = data.Info.LoadAvg1
|
||||
val = data.Info.LoadAvg[0]
|
||||
unit = ""
|
||||
case "LoadAvg5":
|
||||
val = data.Info.LoadAvg5
|
||||
val = data.Info.LoadAvg[1]
|
||||
unit = ""
|
||||
case "LoadAvg15":
|
||||
val = data.Info.LoadAvg15
|
||||
val = data.Info.LoadAvg[2]
|
||||
unit = ""
|
||||
}
|
||||
|
||||
@@ -200,11 +200,11 @@ func (am *AlertManager) HandleSystemAlerts(systemRecord *core.Record, data *syst
|
||||
alert.mapSums[key] += temp
|
||||
}
|
||||
case "LoadAvg1":
|
||||
alert.val += stats.LoadAvg1
|
||||
alert.val += stats.LoadAvg[0]
|
||||
case "LoadAvg5":
|
||||
alert.val += stats.LoadAvg5
|
||||
alert.val += stats.LoadAvg[1]
|
||||
case "LoadAvg15":
|
||||
alert.val += stats.LoadAvg15
|
||||
alert.val += stats.LoadAvg[2]
|
||||
default:
|
||||
continue
|
||||
}
|
||||
@@ -293,18 +293,11 @@ func (am *AlertManager) sendSystemAlert(alert SystemAlertData) {
|
||||
// app.Logger().Error("failed to save alert record", "err", err)
|
||||
return
|
||||
}
|
||||
// expand the user relation and send the alert
|
||||
if errs := am.hub.ExpandRecord(alert.alertRecord, []string{"user"}, nil); len(errs) > 0 {
|
||||
// app.Logger().Error("failed to expand user relation", "errs", errs)
|
||||
return
|
||||
}
|
||||
if user := alert.alertRecord.ExpandedOne("user"); user != nil {
|
||||
am.SendAlert(AlertMessageData{
|
||||
UserID: user.Id,
|
||||
Title: subject,
|
||||
Message: body,
|
||||
Link: am.hub.MakeLink("system", systemName),
|
||||
LinkText: "View " + systemName,
|
||||
})
|
||||
}
|
||||
am.SendAlert(AlertMessageData{
|
||||
UserID: alert.alertRecord.GetString("user"),
|
||||
Title: subject,
|
||||
Message: body,
|
||||
Link: am.hub.MakeLink("system", systemName),
|
||||
LinkText: "View " + systemName,
|
||||
})
|
||||
}
|
||||
|
||||
368
beszel/internal/alerts/alerts_test.go
Normal file
368
beszel/internal/alerts/alerts_test.go
Normal file
@@ -0,0 +1,368 @@
|
||||
//go:build testing
|
||||
// +build testing
|
||||
|
||||
package alerts_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
beszelTests "beszel/internal/tests"
|
||||
|
||||
"github.com/pocketbase/dbx"
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
pbTests "github.com/pocketbase/pocketbase/tests"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// marshal to json and return an io.Reader (for use in ApiScenario.Body)
|
||||
func jsonReader(v any) io.Reader {
|
||||
data, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return bytes.NewReader(data)
|
||||
}
|
||||
|
||||
func TestUserAlertsApi(t *testing.T) {
|
||||
hub, _ := beszelTests.NewTestHub(t.TempDir())
|
||||
defer hub.Cleanup()
|
||||
|
||||
hub.StartHub()
|
||||
|
||||
user1, _ := beszelTests.CreateUser(hub, "alertstest@example.com", "password")
|
||||
user1Token, _ := user1.NewAuthToken()
|
||||
|
||||
user2, _ := beszelTests.CreateUser(hub, "alertstest2@example.com", "password")
|
||||
user2Token, _ := user2.NewAuthToken()
|
||||
|
||||
system1, _ := beszelTests.CreateRecord(hub, "systems", map[string]any{
|
||||
"name": "system1",
|
||||
"users": []string{user1.Id},
|
||||
"host": "127.0.0.1",
|
||||
})
|
||||
|
||||
system2, _ := beszelTests.CreateRecord(hub, "systems", map[string]any{
|
||||
"name": "system2",
|
||||
"users": []string{user1.Id, user2.Id},
|
||||
"host": "127.0.0.2",
|
||||
})
|
||||
|
||||
userRecords, _ := hub.CountRecords("users")
|
||||
assert.EqualValues(t, 2, userRecords, "all users should be created")
|
||||
|
||||
systemRecords, _ := hub.CountRecords("systems")
|
||||
assert.EqualValues(t, 2, systemRecords, "all systems should be created")
|
||||
|
||||
testAppFactory := func(t testing.TB) *pbTests.TestApp {
|
||||
return hub.TestApp
|
||||
}
|
||||
|
||||
scenarios := []beszelTests.ApiScenario{
|
||||
{
|
||||
Name: "GET not implemented - returns index",
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/beszel/user-alerts",
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{"<html ", "globalThis.BESZEL"},
|
||||
TestAppFactory: testAppFactory,
|
||||
},
|
||||
{
|
||||
Name: "POST no auth",
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/beszel/user-alerts",
|
||||
ExpectedStatus: 401,
|
||||
ExpectedContent: []string{"requires valid"},
|
||||
TestAppFactory: testAppFactory,
|
||||
},
|
||||
{
|
||||
Name: "POST no body",
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/beszel/user-alerts",
|
||||
Headers: map[string]string{
|
||||
"Authorization": user1Token,
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{"Bad data"},
|
||||
TestAppFactory: testAppFactory,
|
||||
},
|
||||
{
|
||||
Name: "POST bad data",
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/beszel/user-alerts",
|
||||
Headers: map[string]string{
|
||||
"Authorization": user1Token,
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{"Bad data"},
|
||||
TestAppFactory: testAppFactory,
|
||||
Body: jsonReader(map[string]any{
|
||||
"invalidField": "this should cause validation error",
|
||||
"threshold": "not a number",
|
||||
}),
|
||||
},
|
||||
{
|
||||
Name: "POST malformed JSON",
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/beszel/user-alerts",
|
||||
Headers: map[string]string{
|
||||
"Authorization": user1Token,
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{"Bad data"},
|
||||
TestAppFactory: testAppFactory,
|
||||
Body: strings.NewReader(`{"alertType": "cpu", "threshold": 80, "enabled": true,}`),
|
||||
},
|
||||
{
|
||||
Name: "POST valid alert data multiple systems",
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/beszel/user-alerts",
|
||||
Headers: map[string]string{
|
||||
"Authorization": user1Token,
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{"\"success\":true"},
|
||||
TestAppFactory: testAppFactory,
|
||||
Body: jsonReader(map[string]any{
|
||||
"name": "CPU",
|
||||
"value": 69,
|
||||
"min": 9,
|
||||
"systems": []string{system1.Id, system2.Id},
|
||||
"overwrite": false,
|
||||
}),
|
||||
AfterTestFunc: func(t testing.TB, app *pbTests.TestApp, res *http.Response) {
|
||||
// check total alerts
|
||||
alerts, _ := app.CountRecords("alerts")
|
||||
assert.EqualValues(t, 2, alerts, "should have 2 alerts")
|
||||
// check alert has correct values
|
||||
matchingAlerts, _ := app.CountRecords("alerts", dbx.HashExp{"name": "CPU", "user": user1.Id, "system": system1.Id, "value": 69, "min": 9})
|
||||
assert.EqualValues(t, 1, matchingAlerts, "should have 1 alert")
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "POST valid alert data single system",
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/beszel/user-alerts",
|
||||
Headers: map[string]string{
|
||||
"Authorization": user1Token,
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{"\"success\":true"},
|
||||
TestAppFactory: testAppFactory,
|
||||
Body: jsonReader(map[string]any{
|
||||
"name": "Memory",
|
||||
"systems": []string{system1.Id},
|
||||
"value": 90,
|
||||
"min": 10,
|
||||
}),
|
||||
AfterTestFunc: func(t testing.TB, app *pbTests.TestApp, res *http.Response) {
|
||||
user1Alerts, _ := app.CountRecords("alerts", dbx.HashExp{"user": user1.Id})
|
||||
assert.EqualValues(t, 3, user1Alerts, "should have 3 alerts")
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Overwrite: false, should not overwrite existing alert",
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/beszel/user-alerts",
|
||||
Headers: map[string]string{
|
||||
"Authorization": user1Token,
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{"\"success\":true"},
|
||||
TestAppFactory: testAppFactory,
|
||||
Body: jsonReader(map[string]any{
|
||||
"name": "CPU",
|
||||
"value": 45,
|
||||
"min": 5,
|
||||
"systems": []string{system1.Id},
|
||||
"overwrite": false,
|
||||
}),
|
||||
BeforeTestFunc: func(t testing.TB, app *pbTests.TestApp, e *core.ServeEvent) {
|
||||
beszelTests.ClearCollection(t, app, "alerts")
|
||||
beszelTests.CreateRecord(app, "alerts", map[string]any{
|
||||
"name": "CPU",
|
||||
"system": system1.Id,
|
||||
"user": user1.Id,
|
||||
"value": 80,
|
||||
"min": 10,
|
||||
})
|
||||
},
|
||||
AfterTestFunc: func(t testing.TB, app *pbTests.TestApp, res *http.Response) {
|
||||
alerts, _ := app.CountRecords("alerts")
|
||||
assert.EqualValues(t, 1, alerts, "should have 1 alert")
|
||||
alert, _ := app.FindFirstRecordByFilter("alerts", "name = 'CPU' && user = {:user}", dbx.Params{"user": user1.Id})
|
||||
assert.EqualValues(t, 80, alert.Get("value"), "should have 80 as value")
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Overwrite: true, should overwrite existing alert",
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/beszel/user-alerts",
|
||||
Headers: map[string]string{
|
||||
"Authorization": user2Token,
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{"\"success\":true"},
|
||||
TestAppFactory: testAppFactory,
|
||||
Body: jsonReader(map[string]any{
|
||||
"name": "CPU",
|
||||
"value": 45,
|
||||
"min": 5,
|
||||
"systems": []string{system2.Id},
|
||||
"overwrite": true,
|
||||
}),
|
||||
BeforeTestFunc: func(t testing.TB, app *pbTests.TestApp, e *core.ServeEvent) {
|
||||
beszelTests.ClearCollection(t, app, "alerts")
|
||||
beszelTests.CreateRecord(app, "alerts", map[string]any{
|
||||
"name": "CPU",
|
||||
"system": system2.Id,
|
||||
"user": user2.Id,
|
||||
"value": 80,
|
||||
"min": 10,
|
||||
})
|
||||
},
|
||||
AfterTestFunc: func(t testing.TB, app *pbTests.TestApp, res *http.Response) {
|
||||
alerts, _ := app.CountRecords("alerts")
|
||||
assert.EqualValues(t, 1, alerts, "should have 1 alert")
|
||||
alert, _ := app.FindFirstRecordByFilter("alerts", "name = 'CPU' && user = {:user}", dbx.Params{"user": user2.Id})
|
||||
assert.EqualValues(t, 45, alert.Get("value"), "should have 45 as value")
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "DELETE no auth",
|
||||
Method: http.MethodDelete,
|
||||
URL: "/api/beszel/user-alerts",
|
||||
ExpectedStatus: 401,
|
||||
ExpectedContent: []string{"requires valid"},
|
||||
TestAppFactory: testAppFactory,
|
||||
Body: jsonReader(map[string]any{
|
||||
"name": "CPU",
|
||||
"systems": []string{system1.Id},
|
||||
}),
|
||||
BeforeTestFunc: func(t testing.TB, app *pbTests.TestApp, e *core.ServeEvent) {
|
||||
beszelTests.ClearCollection(t, app, "alerts")
|
||||
beszelTests.CreateRecord(app, "alerts", map[string]any{
|
||||
"name": "CPU",
|
||||
"system": system1.Id,
|
||||
"user": user1.Id,
|
||||
"value": 80,
|
||||
"min": 10,
|
||||
})
|
||||
},
|
||||
AfterTestFunc: func(t testing.TB, app *pbTests.TestApp, res *http.Response) {
|
||||
alerts, _ := app.CountRecords("alerts")
|
||||
assert.EqualValues(t, 1, alerts, "should have 1 alert")
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "DELETE alert",
|
||||
Method: http.MethodDelete,
|
||||
URL: "/api/beszel/user-alerts",
|
||||
Headers: map[string]string{
|
||||
"Authorization": user1Token,
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{"\"count\":1", "\"success\":true"},
|
||||
TestAppFactory: testAppFactory,
|
||||
Body: jsonReader(map[string]any{
|
||||
"name": "CPU",
|
||||
"systems": []string{system1.Id},
|
||||
}),
|
||||
BeforeTestFunc: func(t testing.TB, app *pbTests.TestApp, e *core.ServeEvent) {
|
||||
beszelTests.ClearCollection(t, app, "alerts")
|
||||
beszelTests.CreateRecord(app, "alerts", map[string]any{
|
||||
"name": "CPU",
|
||||
"system": system1.Id,
|
||||
"user": user1.Id,
|
||||
"value": 80,
|
||||
"min": 10,
|
||||
})
|
||||
},
|
||||
AfterTestFunc: func(t testing.TB, app *pbTests.TestApp, res *http.Response) {
|
||||
alerts, _ := app.CountRecords("alerts")
|
||||
assert.Zero(t, alerts, "should have 0 alerts")
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "DELETE alert multiple systems",
|
||||
Method: http.MethodDelete,
|
||||
URL: "/api/beszel/user-alerts",
|
||||
Headers: map[string]string{
|
||||
"Authorization": user1Token,
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{"\"count\":2", "\"success\":true"},
|
||||
TestAppFactory: testAppFactory,
|
||||
Body: jsonReader(map[string]any{
|
||||
"name": "Memory",
|
||||
"systems": []string{system1.Id, system2.Id},
|
||||
}),
|
||||
BeforeTestFunc: func(t testing.TB, app *pbTests.TestApp, e *core.ServeEvent) {
|
||||
beszelTests.ClearCollection(t, app, "alerts")
|
||||
for _, systemId := range []string{system1.Id, system2.Id} {
|
||||
_, err := beszelTests.CreateRecord(app, "alerts", map[string]any{
|
||||
"name": "Memory",
|
||||
"system": systemId,
|
||||
"user": user1.Id,
|
||||
"value": 90,
|
||||
"min": 10,
|
||||
})
|
||||
assert.NoError(t, err, "should create alert")
|
||||
}
|
||||
alerts, _ := app.CountRecords("alerts")
|
||||
assert.EqualValues(t, 2, alerts, "should have 2 alerts")
|
||||
},
|
||||
AfterTestFunc: func(t testing.TB, app *pbTests.TestApp, res *http.Response) {
|
||||
alerts, _ := app.CountRecords("alerts")
|
||||
assert.Zero(t, alerts, "should have 0 alerts")
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "User 2 should not be able to delete alert of user 1",
|
||||
Method: http.MethodDelete,
|
||||
URL: "/api/beszel/user-alerts",
|
||||
Headers: map[string]string{
|
||||
"Authorization": user2Token,
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{"\"count\":1", "\"success\":true"},
|
||||
TestAppFactory: testAppFactory,
|
||||
Body: jsonReader(map[string]any{
|
||||
"name": "CPU",
|
||||
"systems": []string{system2.Id},
|
||||
}),
|
||||
BeforeTestFunc: func(t testing.TB, app *pbTests.TestApp, e *core.ServeEvent) {
|
||||
beszelTests.ClearCollection(t, app, "alerts")
|
||||
for _, user := range []string{user1.Id, user2.Id} {
|
||||
beszelTests.CreateRecord(app, "alerts", map[string]any{
|
||||
"name": "CPU",
|
||||
"system": system2.Id,
|
||||
"user": user,
|
||||
"value": 80,
|
||||
"min": 10,
|
||||
})
|
||||
}
|
||||
alerts, _ := app.CountRecords("alerts")
|
||||
assert.EqualValues(t, 2, alerts, "should have 2 alerts")
|
||||
user1AlertCount, _ := app.CountRecords("alerts", dbx.HashExp{"user": user1.Id})
|
||||
assert.EqualValues(t, 1, user1AlertCount, "should have 1 alert")
|
||||
user2AlertCount, _ := app.CountRecords("alerts", dbx.HashExp{"user": user2.Id})
|
||||
assert.EqualValues(t, 1, user2AlertCount, "should have 1 alert")
|
||||
},
|
||||
AfterTestFunc: func(t testing.TB, app *pbTests.TestApp, res *http.Response) {
|
||||
user1AlertCount, _ := app.CountRecords("alerts", dbx.HashExp{"user": user1.Id})
|
||||
assert.EqualValues(t, 1, user1AlertCount, "should have 1 alert")
|
||||
user2AlertCount, _ := app.CountRecords("alerts", dbx.HashExp{"user": user2.Id})
|
||||
assert.Zero(t, user2AlertCount, "should have 0 alerts")
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, scenario := range scenarios {
|
||||
scenario.Test(t)
|
||||
}
|
||||
}
|
||||
@@ -31,9 +31,13 @@ type Stats struct {
|
||||
Temperatures map[string]float64 `json:"t,omitempty" cbor:"20,keyasint,omitempty"`
|
||||
ExtraFs map[string]*FsStats `json:"efs,omitempty" cbor:"21,keyasint,omitempty"`
|
||||
GPUData map[string]GPUData `json:"g,omitempty" cbor:"22,keyasint,omitempty"`
|
||||
LoadAvg1 float64 `json:"l1,omitempty" cbor:"23,keyasint,omitempty,omitzero"`
|
||||
LoadAvg5 float64 `json:"l5,omitempty" cbor:"24,keyasint,omitempty,omitzero"`
|
||||
LoadAvg15 float64 `json:"l15,omitempty" cbor:"25,keyasint,omitempty,omitzero"`
|
||||
LoadAvg1 float64 `json:"l1,omitempty" cbor:"23,keyasint,omitempty"`
|
||||
LoadAvg5 float64 `json:"l5,omitempty" cbor:"24,keyasint,omitempty"`
|
||||
LoadAvg15 float64 `json:"l15,omitempty" cbor:"25,keyasint,omitempty"`
|
||||
Bandwidth [2]uint64 `json:"b,omitzero" cbor:"26,keyasint,omitzero"` // [sent bytes, recv bytes]
|
||||
MaxBandwidth [2]uint64 `json:"bm,omitzero" cbor:"27,keyasint,omitzero"` // [sent bytes, recv bytes]
|
||||
LoadAvg [3]float64 `json:"la,omitempty" cbor:"28,keyasint"`
|
||||
// TODO: remove other load fields in future release in favor of load avg array
|
||||
}
|
||||
|
||||
type GPUData struct {
|
||||
@@ -77,24 +81,27 @@ const (
|
||||
)
|
||||
|
||||
type Info struct {
|
||||
Hostname string `json:"h" cbor:"0,keyasint"`
|
||||
KernelVersion string `json:"k,omitempty" cbor:"1,keyasint,omitempty"`
|
||||
Cores int `json:"c" cbor:"2,keyasint"`
|
||||
Threads int `json:"t,omitempty" cbor:"3,keyasint,omitempty"`
|
||||
CpuModel string `json:"m" cbor:"4,keyasint"`
|
||||
Uptime uint64 `json:"u" cbor:"5,keyasint"`
|
||||
Cpu float64 `json:"cpu" cbor:"6,keyasint"`
|
||||
MemPct float64 `json:"mp" cbor:"7,keyasint"`
|
||||
DiskPct float64 `json:"dp" cbor:"8,keyasint"`
|
||||
Bandwidth float64 `json:"b" cbor:"9,keyasint"`
|
||||
AgentVersion string `json:"v" cbor:"10,keyasint"`
|
||||
Podman bool `json:"p,omitempty" cbor:"11,keyasint,omitempty"`
|
||||
GpuPct float64 `json:"g,omitempty" cbor:"12,keyasint,omitempty"`
|
||||
DashboardTemp float64 `json:"dt,omitempty" cbor:"13,keyasint,omitempty"`
|
||||
Os Os `json:"os" cbor:"14,keyasint"`
|
||||
LoadAvg1 float64 `json:"l1,omitempty" cbor:"15,keyasint,omitempty"`
|
||||
LoadAvg5 float64 `json:"l5,omitempty" cbor:"16,keyasint,omitempty"`
|
||||
LoadAvg15 float64 `json:"l15,omitempty" cbor:"17,keyasint,omitempty"`
|
||||
Hostname string `json:"h" cbor:"0,keyasint"`
|
||||
KernelVersion string `json:"k,omitempty" cbor:"1,keyasint,omitempty"`
|
||||
Cores int `json:"c" cbor:"2,keyasint"`
|
||||
Threads int `json:"t,omitempty" cbor:"3,keyasint,omitempty"`
|
||||
CpuModel string `json:"m" cbor:"4,keyasint"`
|
||||
Uptime uint64 `json:"u" cbor:"5,keyasint"`
|
||||
Cpu float64 `json:"cpu" cbor:"6,keyasint"`
|
||||
MemPct float64 `json:"mp" cbor:"7,keyasint"`
|
||||
DiskPct float64 `json:"dp" cbor:"8,keyasint"`
|
||||
Bandwidth float64 `json:"b" cbor:"9,keyasint"`
|
||||
AgentVersion string `json:"v" cbor:"10,keyasint"`
|
||||
Podman bool `json:"p,omitempty" cbor:"11,keyasint,omitempty"`
|
||||
GpuPct float64 `json:"g,omitempty" cbor:"12,keyasint,omitempty"`
|
||||
DashboardTemp float64 `json:"dt,omitempty" cbor:"13,keyasint,omitempty"`
|
||||
Os Os `json:"os" cbor:"14,keyasint"`
|
||||
LoadAvg1 float64 `json:"l1,omitempty" cbor:"15,keyasint,omitempty"`
|
||||
LoadAvg5 float64 `json:"l5,omitempty" cbor:"16,keyasint,omitempty"`
|
||||
LoadAvg15 float64 `json:"l15,omitempty" cbor:"17,keyasint,omitempty"`
|
||||
BandwidthBytes uint64 `json:"bb" cbor:"18,keyasint"`
|
||||
LoadAvg [3]float64 `json:"la,omitempty" cbor:"19,keyasint"`
|
||||
// TODO: remove load fields in future release in favor of load avg array
|
||||
}
|
||||
|
||||
// Final data structure to return to the hub
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/pocketbase/dbx"
|
||||
"github.com/pocketbase/pocketbase/apis"
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
"github.com/spf13/cast"
|
||||
"gopkg.in/yaml.v3"
|
||||
@@ -279,9 +278,8 @@ func createFingerprintRecord(app core.App, systemID, token string) error {
|
||||
|
||||
// Returns the current config.yml file as a JSON object
|
||||
func GetYamlConfig(e *core.RequestEvent) error {
|
||||
info, _ := e.RequestInfo()
|
||||
if info.Auth == nil || info.Auth.GetString("role") != "admin" {
|
||||
return apis.NewForbiddenError("Forbidden", nil)
|
||||
if e.Auth.GetString("role") != "admin" {
|
||||
return e.ForbiddenError("Requires admin role", nil)
|
||||
}
|
||||
configContent, err := generateYAML(e.App)
|
||||
if err != nil {
|
||||
|
||||
@@ -215,7 +215,7 @@ func (h *Hub) startServer(se *core.ServeEvent) error {
|
||||
|
||||
// registerCronJobs sets up scheduled tasks
|
||||
func (h *Hub) registerCronJobs(_ *core.ServeEvent) error {
|
||||
// delete old records once every hour
|
||||
// delete old system_stats and alerts_history records once every hour
|
||||
h.Cron().MustAdd("delete old records", "8 * * * *", h.rm.DeleteOldRecords)
|
||||
// create longer records every 10 minutes
|
||||
h.Cron().MustAdd("create longer records", "*/10 * * * *", h.rm.CreateLongerRecords)
|
||||
@@ -224,48 +224,48 @@ func (h *Hub) registerCronJobs(_ *core.ServeEvent) error {
|
||||
|
||||
// custom api routes
|
||||
func (h *Hub) registerApiRoutes(se *core.ServeEvent) error {
|
||||
// returns public key and version
|
||||
se.Router.GET("/api/beszel/getkey", func(e *core.RequestEvent) error {
|
||||
info, _ := e.RequestInfo()
|
||||
if info.Auth == nil {
|
||||
return apis.NewForbiddenError("Forbidden", nil)
|
||||
}
|
||||
return e.JSON(http.StatusOK, map[string]string{"key": h.pubKey, "v": beszel.Version})
|
||||
})
|
||||
// auth protected routes
|
||||
apiAuth := se.Router.Group("/api/beszel")
|
||||
apiAuth.Bind(apis.RequireAuth())
|
||||
// auth optional routes
|
||||
apiNoAuth := se.Router.Group("/api/beszel")
|
||||
|
||||
// create first user endpoint only needed if no users exist
|
||||
if totalUsers, _ := se.App.CountRecords("users"); totalUsers == 0 {
|
||||
apiNoAuth.POST("/create-user", h.um.CreateFirstUser)
|
||||
}
|
||||
// check if first time setup on login page
|
||||
se.Router.GET("/api/beszel/first-run", func(e *core.RequestEvent) error {
|
||||
total, err := h.CountRecords("users")
|
||||
apiNoAuth.GET("/first-run", func(e *core.RequestEvent) error {
|
||||
total, err := e.App.CountRecords("users")
|
||||
return e.JSON(http.StatusOK, map[string]bool{"firstRun": err == nil && total == 0})
|
||||
})
|
||||
// get public key and version
|
||||
apiAuth.GET("/getkey", func(e *core.RequestEvent) error {
|
||||
return e.JSON(http.StatusOK, map[string]string{"key": h.pubKey, "v": beszel.Version})
|
||||
})
|
||||
// send test notification
|
||||
se.Router.GET("/api/beszel/send-test-notification", h.SendTestNotification)
|
||||
// API endpoint to get config.yml content
|
||||
se.Router.GET("/api/beszel/config-yaml", config.GetYamlConfig)
|
||||
apiAuth.POST("/test-notification", h.SendTestNotification)
|
||||
// get config.yml content
|
||||
apiAuth.GET("/config-yaml", config.GetYamlConfig)
|
||||
// handle agent websocket connection
|
||||
se.Router.GET("/api/beszel/agent-connect", h.handleAgentConnect)
|
||||
apiNoAuth.GET("/agent-connect", h.handleAgentConnect)
|
||||
// get or create universal tokens
|
||||
se.Router.GET("/api/beszel/universal-token", h.getUniversalToken)
|
||||
// create first user endpoint only needed if no users exist
|
||||
if totalUsers, _ := h.CountRecords("users"); totalUsers == 0 {
|
||||
se.Router.POST("/api/beszel/create-user", h.um.CreateFirstUser)
|
||||
}
|
||||
apiAuth.GET("/universal-token", h.getUniversalToken)
|
||||
// update / delete user alerts
|
||||
apiAuth.POST("/user-alerts", alerts.UpsertUserAlerts)
|
||||
apiAuth.DELETE("/user-alerts", alerts.DeleteUserAlerts)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Handler for universal token API endpoint (create, read, delete)
|
||||
func (h *Hub) getUniversalToken(e *core.RequestEvent) error {
|
||||
info, err := e.RequestInfo()
|
||||
if err != nil || info.Auth == nil {
|
||||
return apis.NewForbiddenError("Forbidden", nil)
|
||||
}
|
||||
|
||||
tokenMap := universalTokenMap.GetMap()
|
||||
userID := info.Auth.Id
|
||||
userID := e.Auth.Id
|
||||
query := e.Request.URL.Query()
|
||||
token := query.Get("token")
|
||||
tokenSet := token != ""
|
||||
|
||||
if !tokenSet {
|
||||
if token == "" {
|
||||
// return existing token if it exists
|
||||
if token, _, ok := tokenMap.GetByValue(userID); ok {
|
||||
return e.JSON(http.StatusOK, map[string]any{"token": token, "active": true})
|
||||
|
||||
@@ -4,27 +4,37 @@
|
||||
package hub_test
|
||||
|
||||
import (
|
||||
"beszel/internal/tests"
|
||||
beszelTests "beszel/internal/tests"
|
||||
"testing"
|
||||
|
||||
"bytes"
|
||||
"crypto/ed25519"
|
||||
"encoding/json/v2"
|
||||
"encoding/pem"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
pbTests "github.com/pocketbase/pocketbase/tests"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
func getTestHub(t testing.TB) *tests.TestHub {
|
||||
hub, _ := tests.NewTestHub(t.TempDir())
|
||||
return hub
|
||||
// marshal to json and return an io.Reader (for use in ApiScenario.Body)
|
||||
func jsonReader(v any) io.Reader {
|
||||
data, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return bytes.NewReader(data)
|
||||
}
|
||||
|
||||
func TestMakeLink(t *testing.T) {
|
||||
hub := getTestHub(t)
|
||||
hub, _ := beszelTests.NewTestHub(t.TempDir())
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -114,7 +124,7 @@ func TestMakeLink(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetSSHKey(t *testing.T) {
|
||||
hub := getTestHub(t)
|
||||
hub, _ := beszelTests.NewTestHub(t.TempDir())
|
||||
|
||||
// Test Case 1: Key generation (no existing key)
|
||||
t.Run("KeyGeneration", func(t *testing.T) {
|
||||
@@ -254,3 +264,340 @@ func TestGetSSHKey(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestApiRoutesAuthentication(t *testing.T) {
|
||||
hub, _ := beszelTests.NewTestHub(t.TempDir())
|
||||
defer hub.Cleanup()
|
||||
|
||||
hub.StartHub()
|
||||
|
||||
// Create test user and get auth token
|
||||
user, err := beszelTests.CreateUser(hub, "testuser@example.com", "password123")
|
||||
require.NoError(t, err, "Failed to create test user")
|
||||
|
||||
adminUser, err := beszelTests.CreateRecord(hub, "users", map[string]any{
|
||||
"email": "admin@example.com",
|
||||
"password": "password123",
|
||||
"role": "admin",
|
||||
})
|
||||
require.NoError(t, err, "Failed to create admin user")
|
||||
adminUserToken, err := adminUser.NewAuthToken()
|
||||
|
||||
// superUser, err := beszelTests.CreateRecord(hub, core.CollectionNameSuperusers, map[string]any{
|
||||
// "email": "superuser@example.com",
|
||||
// "password": "password123",
|
||||
// })
|
||||
// require.NoError(t, err, "Failed to create superuser")
|
||||
|
||||
userToken, err := user.NewAuthToken()
|
||||
require.NoError(t, err, "Failed to create auth token")
|
||||
|
||||
// Create test system for user-alerts endpoints
|
||||
system, err := beszelTests.CreateRecord(hub, "systems", map[string]any{
|
||||
"name": "test-system",
|
||||
"users": []string{user.Id},
|
||||
"host": "127.0.0.1",
|
||||
})
|
||||
require.NoError(t, err, "Failed to create test system")
|
||||
|
||||
testAppFactory := func(t testing.TB) *pbTests.TestApp {
|
||||
return hub.TestApp
|
||||
}
|
||||
|
||||
scenarios := []beszelTests.ApiScenario{
|
||||
// Auth Protected Routes - Should require authentication
|
||||
{
|
||||
Name: "POST /test-notification - no auth should fail",
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/beszel/test-notification",
|
||||
ExpectedStatus: 401,
|
||||
ExpectedContent: []string{"requires valid"},
|
||||
TestAppFactory: testAppFactory,
|
||||
Body: jsonReader(map[string]any{
|
||||
"url": "generic://127.0.0.1",
|
||||
}),
|
||||
},
|
||||
{
|
||||
Name: "POST /test-notification - with auth should succeed",
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/beszel/test-notification",
|
||||
TestAppFactory: testAppFactory,
|
||||
Headers: map[string]string{
|
||||
"Authorization": userToken,
|
||||
},
|
||||
Body: jsonReader(map[string]any{
|
||||
"url": "generic://127.0.0.1",
|
||||
}),
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{"sending message"},
|
||||
},
|
||||
{
|
||||
Name: "GET /config-yaml - no auth should fail",
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/beszel/config-yaml",
|
||||
ExpectedStatus: 401,
|
||||
ExpectedContent: []string{"requires valid"},
|
||||
TestAppFactory: testAppFactory,
|
||||
},
|
||||
{
|
||||
Name: "GET /config-yaml - with user auth should fail",
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/beszel/config-yaml",
|
||||
Headers: map[string]string{
|
||||
"Authorization": userToken,
|
||||
},
|
||||
ExpectedStatus: 403,
|
||||
ExpectedContent: []string{"Requires admin"},
|
||||
TestAppFactory: testAppFactory,
|
||||
},
|
||||
{
|
||||
Name: "GET /config-yaml - with admin auth should succeed",
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/beszel/config-yaml",
|
||||
Headers: map[string]string{
|
||||
"Authorization": adminUserToken,
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{"test-system"},
|
||||
TestAppFactory: testAppFactory,
|
||||
},
|
||||
{
|
||||
Name: "GET /universal-token - no auth should fail",
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/beszel/universal-token",
|
||||
ExpectedStatus: 401,
|
||||
ExpectedContent: []string{"requires valid"},
|
||||
TestAppFactory: testAppFactory,
|
||||
},
|
||||
{
|
||||
Name: "GET /universal-token - with auth should succeed",
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/beszel/universal-token",
|
||||
Headers: map[string]string{
|
||||
"Authorization": userToken,
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{"active", "token"},
|
||||
TestAppFactory: testAppFactory,
|
||||
},
|
||||
{
|
||||
Name: "POST /user-alerts - no auth should fail",
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/beszel/user-alerts",
|
||||
ExpectedStatus: 401,
|
||||
ExpectedContent: []string{"requires valid"},
|
||||
TestAppFactory: testAppFactory,
|
||||
Body: jsonReader(map[string]any{
|
||||
"name": "CPU",
|
||||
"value": 80,
|
||||
"min": 10,
|
||||
"systems": []string{system.Id},
|
||||
}),
|
||||
},
|
||||
{
|
||||
Name: "POST /user-alerts - with auth should succeed",
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/beszel/user-alerts",
|
||||
Headers: map[string]string{
|
||||
"Authorization": userToken,
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{"\"success\":true"},
|
||||
TestAppFactory: testAppFactory,
|
||||
Body: jsonReader(map[string]any{
|
||||
"name": "CPU",
|
||||
"value": 80,
|
||||
"min": 10,
|
||||
"systems": []string{system.Id},
|
||||
}),
|
||||
},
|
||||
{
|
||||
Name: "DELETE /user-alerts - no auth should fail",
|
||||
Method: http.MethodDelete,
|
||||
URL: "/api/beszel/user-alerts",
|
||||
ExpectedStatus: 401,
|
||||
ExpectedContent: []string{"requires valid"},
|
||||
TestAppFactory: testAppFactory,
|
||||
Body: jsonReader(map[string]any{
|
||||
"name": "CPU",
|
||||
"systems": []string{system.Id},
|
||||
}),
|
||||
},
|
||||
{
|
||||
Name: "DELETE /user-alerts - with auth should succeed",
|
||||
Method: http.MethodDelete,
|
||||
URL: "/api/beszel/user-alerts",
|
||||
Headers: map[string]string{
|
||||
"Authorization": userToken,
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{"\"success\":true"},
|
||||
TestAppFactory: testAppFactory,
|
||||
Body: jsonReader(map[string]any{
|
||||
"name": "CPU",
|
||||
"systems": []string{system.Id},
|
||||
}),
|
||||
BeforeTestFunc: func(t testing.TB, app *pbTests.TestApp, e *core.ServeEvent) {
|
||||
// Create an alert to delete
|
||||
beszelTests.CreateRecord(app, "alerts", map[string]any{
|
||||
"name": "CPU",
|
||||
"system": system.Id,
|
||||
"user": user.Id,
|
||||
"value": 80,
|
||||
"min": 10,
|
||||
})
|
||||
},
|
||||
},
|
||||
|
||||
// Auth Optional Routes - Should work without authentication
|
||||
{
|
||||
Name: "GET /getkey - no auth should fail",
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/beszel/getkey",
|
||||
ExpectedStatus: 401,
|
||||
ExpectedContent: []string{"requires valid"},
|
||||
TestAppFactory: testAppFactory,
|
||||
},
|
||||
{
|
||||
Name: "GET /getkey - with auth should also succeed",
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/beszel/getkey",
|
||||
Headers: map[string]string{
|
||||
"Authorization": userToken,
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{"\"key\":", "\"v\":"},
|
||||
TestAppFactory: testAppFactory,
|
||||
},
|
||||
{
|
||||
Name: "GET /first-run - no auth should succeed",
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/beszel/first-run",
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{"\"firstRun\":false"},
|
||||
TestAppFactory: testAppFactory,
|
||||
},
|
||||
{
|
||||
Name: "GET /first-run - with auth should also succeed",
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/beszel/first-run",
|
||||
Headers: map[string]string{
|
||||
"Authorization": userToken,
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{"\"firstRun\":false"},
|
||||
TestAppFactory: testAppFactory,
|
||||
},
|
||||
{
|
||||
Name: "GET /agent-connect - no auth should succeed (websocket upgrade fails but route is accessible)",
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/beszel/agent-connect",
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{},
|
||||
TestAppFactory: testAppFactory,
|
||||
},
|
||||
{
|
||||
Name: "POST /test-notification - invalid auth token should fail",
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/beszel/test-notification",
|
||||
Body: jsonReader(map[string]any{
|
||||
"url": "generic://127.0.0.1",
|
||||
}),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "invalid-token",
|
||||
},
|
||||
ExpectedStatus: 401,
|
||||
ExpectedContent: []string{"requires valid"},
|
||||
TestAppFactory: testAppFactory,
|
||||
},
|
||||
{
|
||||
Name: "POST /user-alerts - invalid auth token should fail",
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/beszel/user-alerts",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "invalid-token",
|
||||
},
|
||||
ExpectedStatus: 401,
|
||||
ExpectedContent: []string{"requires valid"},
|
||||
TestAppFactory: testAppFactory,
|
||||
Body: jsonReader(map[string]any{
|
||||
"name": "CPU",
|
||||
"value": 80,
|
||||
"min": 10,
|
||||
"systems": []string{system.Id},
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
for _, scenario := range scenarios {
|
||||
scenario.Test(t)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateUserEndpointAvailability(t *testing.T) {
|
||||
t.Run("CreateUserEndpoint available when no users exist", func(t *testing.T) {
|
||||
hub, _ := beszelTests.NewTestHub(t.TempDir())
|
||||
defer hub.Cleanup()
|
||||
|
||||
// Ensure no users exist
|
||||
userCount, err := hub.CountRecords("users")
|
||||
require.NoError(t, err)
|
||||
require.Zero(t, userCount, "Should start with no users")
|
||||
|
||||
hub.StartHub()
|
||||
|
||||
testAppFactory := func(t testing.TB) *pbTests.TestApp {
|
||||
return hub.TestApp
|
||||
}
|
||||
|
||||
scenario := beszelTests.ApiScenario{
|
||||
Name: "POST /create-user - should be available when no users exist",
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/beszel/create-user",
|
||||
Body: jsonReader(map[string]any{
|
||||
"email": "firstuser@example.com",
|
||||
"password": "password123",
|
||||
}),
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{"User created"},
|
||||
TestAppFactory: testAppFactory,
|
||||
}
|
||||
|
||||
scenario.Test(t)
|
||||
|
||||
// Verify user was created
|
||||
userCount, err = hub.CountRecords("users")
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, 1, userCount, "Should have created one user")
|
||||
})
|
||||
|
||||
t.Run("CreateUserEndpoint not available when users exist", func(t *testing.T) {
|
||||
hub, _ := beszelTests.NewTestHub(t.TempDir())
|
||||
defer hub.Cleanup()
|
||||
|
||||
// Create a user first
|
||||
_, err := beszelTests.CreateUser(hub, "existing@example.com", "password")
|
||||
require.NoError(t, err)
|
||||
|
||||
hub.StartHub()
|
||||
|
||||
testAppFactory := func(t testing.TB) *pbTests.TestApp {
|
||||
return hub.TestApp
|
||||
}
|
||||
|
||||
scenario := beszelTests.ApiScenario{
|
||||
Name: "POST /create-user - should not be available when users exist",
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/beszel/create-user",
|
||||
Body: jsonReader(map[string]any{
|
||||
"email": "another@example.com",
|
||||
"password": "password123",
|
||||
}),
|
||||
ExpectedStatus: 404,
|
||||
ExpectedContent: []string{"wasn't found"},
|
||||
TestAppFactory: testAppFactory,
|
||||
}
|
||||
|
||||
scenario.Test(t)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -5,7 +5,8 @@ import (
|
||||
"beszel/internal/entities/system"
|
||||
"beszel/internal/hub/ws"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"encoding/json/jsontext"
|
||||
"encoding/json/v2"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
@@ -275,7 +276,7 @@ func (sys *System) fetchDataViaSSH() (*system.CombinedData, error) {
|
||||
if sys.agentVersion.GTE(beszel.MinVersionCbor) {
|
||||
err = cbor.NewDecoder(stdout).Decode(sys.data)
|
||||
} else {
|
||||
err = json.NewDecoder(stdout).Decode(sys.data)
|
||||
err = json.UnmarshalDecode(jsontext.NewDecoder(stdout), sys.data)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
||||
@@ -159,8 +159,10 @@ func (sm *SystemManager) onRecordUpdate(e *core.RecordEvent) error {
|
||||
// - down: Triggers status change alerts
|
||||
func (sm *SystemManager) onRecordAfterUpdateSuccess(e *core.RecordEvent) error {
|
||||
newStatus := e.Record.GetString("status")
|
||||
prevStatus := pending
|
||||
system, ok := sm.systems.GetOk(e.Record.Id)
|
||||
if ok {
|
||||
prevStatus = system.Status
|
||||
system.Status = newStatus
|
||||
}
|
||||
|
||||
@@ -182,6 +184,7 @@ func (sm *SystemManager) onRecordAfterUpdateSuccess(e *core.RecordEvent) error {
|
||||
if err := sm.AddRecord(e.Record, nil); err != nil {
|
||||
e.App.Logger().Error("Error adding record", "err", err)
|
||||
}
|
||||
_ = deactivateAlerts(e.App, e.Record.Id)
|
||||
return e.Next()
|
||||
}
|
||||
|
||||
@@ -190,8 +193,6 @@ func (sm *SystemManager) onRecordAfterUpdateSuccess(e *core.RecordEvent) error {
|
||||
return sm.AddRecord(e.Record, nil)
|
||||
}
|
||||
|
||||
prevStatus := system.Status
|
||||
|
||||
// Trigger system alerts when system comes online
|
||||
if newStatus == up {
|
||||
if err := sm.hub.HandleSystemAlerts(e.Record, system.data); err != nil {
|
||||
|
||||
@@ -114,6 +114,9 @@ func (ws *WsConn) Ping() error {
|
||||
|
||||
// sendMessage encodes data to CBOR and sends it as a binary message to the agent.
|
||||
func (ws *WsConn) sendMessage(data common.HubRequest[any]) error {
|
||||
if ws.conn == nil {
|
||||
return gws.ErrConnClosed
|
||||
}
|
||||
bytes, err := cbor.Marshal(data)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
221
beszel/internal/hub/ws/ws_test.go
Normal file
221
beszel/internal/hub/ws/ws_test.go
Normal file
@@ -0,0 +1,221 @@
|
||||
//go:build testing
|
||||
// +build testing
|
||||
|
||||
package ws
|
||||
|
||||
import (
|
||||
"beszel/internal/common"
|
||||
"crypto/ed25519"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/fxamacker/cbor/v2"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
// TestGetUpgrader tests the singleton upgrader
|
||||
func TestGetUpgrader(t *testing.T) {
|
||||
// Reset the global upgrader to test singleton behavior
|
||||
upgrader = nil
|
||||
|
||||
// First call should create the upgrader
|
||||
upgrader1 := GetUpgrader()
|
||||
assert.NotNil(t, upgrader1, "Upgrader should not be nil")
|
||||
|
||||
// Second call should return the same instance
|
||||
upgrader2 := GetUpgrader()
|
||||
assert.Same(t, upgrader1, upgrader2, "Should return the same upgrader instance")
|
||||
|
||||
// Verify it's properly configured
|
||||
assert.NotNil(t, upgrader1, "Upgrader should be configured")
|
||||
}
|
||||
|
||||
// TestNewWsConnection tests WebSocket connection creation
|
||||
func TestNewWsConnection(t *testing.T) {
|
||||
// We can't easily mock gws.Conn, so we'll pass nil and test the structure
|
||||
wsConn := NewWsConnection(nil)
|
||||
|
||||
assert.NotNil(t, wsConn, "WebSocket connection should not be nil")
|
||||
assert.Nil(t, wsConn.conn, "Connection should be nil as passed")
|
||||
assert.NotNil(t, wsConn.responseChan, "Response channel should be initialized")
|
||||
assert.NotNil(t, wsConn.DownChan, "Down channel should be initialized")
|
||||
assert.Equal(t, 1, cap(wsConn.responseChan), "Response channel should have capacity of 1")
|
||||
assert.Equal(t, 1, cap(wsConn.DownChan), "Down channel should have capacity of 1")
|
||||
}
|
||||
|
||||
// TestWsConn_IsConnected tests the connection status check
|
||||
func TestWsConn_IsConnected(t *testing.T) {
|
||||
// Test with nil connection
|
||||
wsConn := NewWsConnection(nil)
|
||||
assert.False(t, wsConn.IsConnected(), "Should not be connected when conn is nil")
|
||||
}
|
||||
|
||||
// TestWsConn_Close tests the connection closing with nil connection
|
||||
func TestWsConn_Close(t *testing.T) {
|
||||
wsConn := NewWsConnection(nil)
|
||||
|
||||
// Should handle nil connection gracefully
|
||||
assert.NotPanics(t, func() {
|
||||
wsConn.Close([]byte("test message"))
|
||||
}, "Should not panic when closing nil connection")
|
||||
}
|
||||
|
||||
// TestWsConn_SendMessage_CBOR tests CBOR encoding in sendMessage
|
||||
func TestWsConn_SendMessage_CBOR(t *testing.T) {
|
||||
wsConn := NewWsConnection(nil)
|
||||
|
||||
testData := common.HubRequest[any]{
|
||||
Action: common.GetData,
|
||||
Data: "test data",
|
||||
}
|
||||
|
||||
// This will fail because conn is nil, but we can test the CBOR encoding logic
|
||||
// by checking that the function properly encodes to CBOR before failing
|
||||
err := wsConn.sendMessage(testData)
|
||||
assert.Error(t, err, "Should error with nil connection")
|
||||
|
||||
// Test CBOR encoding separately
|
||||
bytes, err := cbor.Marshal(testData)
|
||||
assert.NoError(t, err, "Should encode to CBOR successfully")
|
||||
|
||||
// Verify we can decode it back
|
||||
var decodedData common.HubRequest[any]
|
||||
err = cbor.Unmarshal(bytes, &decodedData)
|
||||
assert.NoError(t, err, "Should decode from CBOR successfully")
|
||||
assert.Equal(t, testData.Action, decodedData.Action, "Action should match")
|
||||
}
|
||||
|
||||
// TestWsConn_GetFingerprint_SignatureGeneration tests signature creation logic
|
||||
func TestWsConn_GetFingerprint_SignatureGeneration(t *testing.T) {
|
||||
// Generate test key pair
|
||||
_, privKey, err := ed25519.GenerateKey(nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
signer, err := ssh.NewSignerFromKey(privKey)
|
||||
require.NoError(t, err)
|
||||
|
||||
token := "test-token"
|
||||
|
||||
// This will timeout since conn is nil, but we can verify the signature logic
|
||||
// We can't test the full flow, but we can test that the signature is created properly
|
||||
challenge := []byte(token)
|
||||
signature, err := signer.Sign(nil, challenge)
|
||||
assert.NoError(t, err, "Should create signature successfully")
|
||||
assert.NotEmpty(t, signature.Blob, "Signature blob should not be empty")
|
||||
assert.Equal(t, signer.PublicKey().Type(), signature.Format, "Signature format should match key type")
|
||||
|
||||
// Test the fingerprint request structure
|
||||
fpRequest := common.FingerprintRequest{
|
||||
Signature: signature.Blob,
|
||||
NeedSysInfo: true,
|
||||
}
|
||||
|
||||
// Test CBOR encoding of fingerprint request
|
||||
fpData, err := cbor.Marshal(fpRequest)
|
||||
assert.NoError(t, err, "Should encode fingerprint request to CBOR")
|
||||
|
||||
var decodedFpRequest common.FingerprintRequest
|
||||
err = cbor.Unmarshal(fpData, &decodedFpRequest)
|
||||
assert.NoError(t, err, "Should decode fingerprint request from CBOR")
|
||||
assert.Equal(t, fpRequest.Signature, decodedFpRequest.Signature, "Signature should match")
|
||||
assert.Equal(t, fpRequest.NeedSysInfo, decodedFpRequest.NeedSysInfo, "NeedSysInfo should match")
|
||||
|
||||
// Test the full hub request structure
|
||||
hubRequest := common.HubRequest[any]{
|
||||
Action: common.CheckFingerprint,
|
||||
Data: fpRequest,
|
||||
}
|
||||
|
||||
hubData, err := cbor.Marshal(hubRequest)
|
||||
assert.NoError(t, err, "Should encode hub request to CBOR")
|
||||
|
||||
var decodedHubRequest common.HubRequest[cbor.RawMessage]
|
||||
err = cbor.Unmarshal(hubData, &decodedHubRequest)
|
||||
assert.NoError(t, err, "Should decode hub request from CBOR")
|
||||
assert.Equal(t, common.CheckFingerprint, decodedHubRequest.Action, "Action should be CheckFingerprint")
|
||||
}
|
||||
|
||||
// TestWsConn_RequestSystemData_RequestFormat tests system data request format
|
||||
func TestWsConn_RequestSystemData_RequestFormat(t *testing.T) {
|
||||
// Test the request format that would be sent
|
||||
request := common.HubRequest[any]{
|
||||
Action: common.GetData,
|
||||
}
|
||||
|
||||
// Test CBOR encoding
|
||||
data, err := cbor.Marshal(request)
|
||||
assert.NoError(t, err, "Should encode request to CBOR")
|
||||
|
||||
// Test decoding
|
||||
var decodedRequest common.HubRequest[any]
|
||||
err = cbor.Unmarshal(data, &decodedRequest)
|
||||
assert.NoError(t, err, "Should decode request from CBOR")
|
||||
assert.Equal(t, common.GetData, decodedRequest.Action, "Should have GetData action")
|
||||
}
|
||||
|
||||
// TestFingerprintRecord tests the FingerprintRecord struct
|
||||
func TestFingerprintRecord(t *testing.T) {
|
||||
record := FingerprintRecord{
|
||||
Id: "test-id",
|
||||
SystemId: "system-123",
|
||||
Fingerprint: "test-fingerprint",
|
||||
Token: "test-token",
|
||||
}
|
||||
|
||||
assert.Equal(t, "test-id", record.Id)
|
||||
assert.Equal(t, "system-123", record.SystemId)
|
||||
assert.Equal(t, "test-fingerprint", record.Fingerprint)
|
||||
assert.Equal(t, "test-token", record.Token)
|
||||
}
|
||||
|
||||
// TestDeadlineConstant tests that the deadline constant is reasonable
|
||||
func TestDeadlineConstant(t *testing.T) {
|
||||
assert.Equal(t, 70*time.Second, deadline, "Deadline should be 70 seconds")
|
||||
}
|
||||
|
||||
// TestCommonActions tests that the common actions are properly defined
|
||||
func TestCommonActions(t *testing.T) {
|
||||
// Test that the actions we use exist and have expected values
|
||||
assert.Equal(t, common.WebSocketAction(0), common.GetData, "GetData should be action 0")
|
||||
assert.Equal(t, common.WebSocketAction(1), common.CheckFingerprint, "CheckFingerprint should be action 1")
|
||||
}
|
||||
|
||||
// TestHandler tests that we can create a Handler
|
||||
func TestHandler(t *testing.T) {
|
||||
handler := &Handler{}
|
||||
assert.NotNil(t, handler, "Handler should be created successfully")
|
||||
|
||||
// The Handler embeds gws.BuiltinEventHandler, so it should have the embedded type
|
||||
assert.NotNil(t, handler.BuiltinEventHandler, "Should have embedded BuiltinEventHandler")
|
||||
}
|
||||
|
||||
// TestWsConnChannelBehavior tests channel behavior without WebSocket connections
|
||||
func TestWsConnChannelBehavior(t *testing.T) {
|
||||
wsConn := NewWsConnection(nil)
|
||||
|
||||
// Test that channels are properly initialized and can be used
|
||||
select {
|
||||
case wsConn.DownChan <- struct{}{}:
|
||||
// Should be able to write to channel
|
||||
default:
|
||||
t.Error("Should be able to write to DownChan")
|
||||
}
|
||||
|
||||
// Test reading from DownChan
|
||||
select {
|
||||
case <-wsConn.DownChan:
|
||||
// Should be able to read from channel
|
||||
case <-time.After(10 * time.Millisecond):
|
||||
t.Error("Should be able to read from DownChan")
|
||||
}
|
||||
|
||||
// Response channel should be empty initially
|
||||
select {
|
||||
case <-wsConn.responseChan:
|
||||
t.Error("Response channel should be empty initially")
|
||||
default:
|
||||
// Expected - channel should be empty
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ package records
|
||||
import (
|
||||
"beszel/internal/entities/container"
|
||||
"beszel/internal/entities/system"
|
||||
"encoding/json"
|
||||
"encoding/json/v2"
|
||||
"fmt"
|
||||
"log"
|
||||
"math"
|
||||
@@ -203,12 +203,19 @@ func (rm *RecordManager) AverageSystemStats(db dbx.Builder, records RecordIds) *
|
||||
sum.DiskWritePs += stats.DiskWritePs
|
||||
sum.NetworkSent += stats.NetworkSent
|
||||
sum.NetworkRecv += stats.NetworkRecv
|
||||
sum.LoadAvg[0] += stats.LoadAvg[0]
|
||||
sum.LoadAvg[1] += stats.LoadAvg[1]
|
||||
sum.LoadAvg[2] += stats.LoadAvg[2]
|
||||
sum.Bandwidth[0] += stats.Bandwidth[0]
|
||||
sum.Bandwidth[1] += stats.Bandwidth[1]
|
||||
// Set peak values
|
||||
sum.MaxCpu = max(sum.MaxCpu, stats.MaxCpu, stats.Cpu)
|
||||
sum.MaxNetworkSent = max(sum.MaxNetworkSent, stats.MaxNetworkSent, stats.NetworkSent)
|
||||
sum.MaxNetworkRecv = max(sum.MaxNetworkRecv, stats.MaxNetworkRecv, stats.NetworkRecv)
|
||||
sum.MaxDiskReadPs = max(sum.MaxDiskReadPs, stats.MaxDiskReadPs, stats.DiskReadPs)
|
||||
sum.MaxDiskWritePs = max(sum.MaxDiskWritePs, stats.MaxDiskWritePs, stats.DiskWritePs)
|
||||
sum.MaxBandwidth[0] = max(sum.MaxBandwidth[0], stats.MaxBandwidth[0], stats.Bandwidth[0])
|
||||
sum.MaxBandwidth[1] = max(sum.MaxBandwidth[1], stats.MaxBandwidth[1], stats.Bandwidth[1])
|
||||
|
||||
// Accumulate temperatures
|
||||
if stats.Temperatures != nil {
|
||||
@@ -278,7 +285,11 @@ func (rm *RecordManager) AverageSystemStats(db dbx.Builder, records RecordIds) *
|
||||
sum.DiskWritePs = twoDecimals(sum.DiskWritePs / count)
|
||||
sum.NetworkSent = twoDecimals(sum.NetworkSent / count)
|
||||
sum.NetworkRecv = twoDecimals(sum.NetworkRecv / count)
|
||||
|
||||
sum.LoadAvg[0] = twoDecimals(sum.LoadAvg[0] / count)
|
||||
sum.LoadAvg[1] = twoDecimals(sum.LoadAvg[1] / count)
|
||||
sum.LoadAvg[2] = twoDecimals(sum.LoadAvg[2] / count)
|
||||
sum.Bandwidth[0] = sum.Bandwidth[0] / uint64(count)
|
||||
sum.Bandwidth[1] = sum.Bandwidth[1] / uint64(count)
|
||||
// Average temperatures
|
||||
if sum.Temperatures != nil && tempCount > 0 {
|
||||
for key := range sum.Temperatures {
|
||||
@@ -361,12 +372,46 @@ func (rm *RecordManager) AverageContainerStats(db dbx.Builder, records RecordIds
|
||||
return result
|
||||
}
|
||||
|
||||
// Deletes records older than what is displayed in the UI
|
||||
// Delete old records
|
||||
func (rm *RecordManager) DeleteOldRecords() {
|
||||
// Define the collections to process
|
||||
rm.app.RunInTransaction(func(txApp core.App) error {
|
||||
err := deleteOldSystemStats(txApp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = deleteOldAlertsHistory(txApp, 200, 250)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// Delete old alerts history records
|
||||
func deleteOldAlertsHistory(app core.App, countToKeep, countBeforeDeletion int) error {
|
||||
db := app.DB()
|
||||
var users []struct {
|
||||
Id string `db:"user"`
|
||||
}
|
||||
err := db.NewQuery("SELECT user, COUNT(*) as count FROM alerts_history GROUP BY user HAVING count > {:countBeforeDeletion}").Bind(dbx.Params{"countBeforeDeletion": countBeforeDeletion}).All(&users)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, user := range users {
|
||||
_, err = db.NewQuery("DELETE FROM alerts_history WHERE user = {:user} AND id NOT IN (SELECT id FROM alerts_history WHERE user = {:user} ORDER BY created DESC LIMIT {:countToKeep})").Bind(dbx.Params{"user": user.Id, "countToKeep": countToKeep}).Execute()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Deletes system_stats records older than what is displayed in the UI
|
||||
func deleteOldSystemStats(app core.App) error {
|
||||
// Collections to process
|
||||
collections := [2]string{"system_stats", "container_stats"}
|
||||
|
||||
// Define record types and their retention periods
|
||||
// Record types and their retention periods
|
||||
type RecordDeletionData struct {
|
||||
recordType string
|
||||
retention time.Duration
|
||||
@@ -382,10 +427,9 @@ func (rm *RecordManager) DeleteOldRecords() {
|
||||
now := time.Now().UTC()
|
||||
|
||||
for _, collection := range collections {
|
||||
// Build the WHERE clause dynamically
|
||||
// Build the WHERE clause
|
||||
var conditionParts []string
|
||||
var params dbx.Params = make(map[string]any)
|
||||
|
||||
for i := range recordData {
|
||||
rd := recordData[i]
|
||||
// Create parameterized condition for this record type
|
||||
@@ -393,19 +437,15 @@ func (rm *RecordManager) DeleteOldRecords() {
|
||||
conditionParts = append(conditionParts, fmt.Sprintf("(type = '%s' AND created < {:%s})", rd.recordType, dateParam))
|
||||
params[dateParam] = now.Add(-rd.retention)
|
||||
}
|
||||
|
||||
// Combine conditions with OR
|
||||
conditionStr := strings.Join(conditionParts, " OR ")
|
||||
|
||||
// Construct the full raw query
|
||||
// Construct and execute the full raw query
|
||||
rawQuery := fmt.Sprintf("DELETE FROM %s WHERE %s", collection, conditionStr)
|
||||
|
||||
// Execute the query with parameters
|
||||
if _, err := rm.app.DB().NewQuery(rawQuery).Bind(params).Execute(); err != nil {
|
||||
// return fmt.Errorf("failed to delete from %s: %v", collection, err)
|
||||
rm.app.Logger().Error("failed to delete", "collection", collection, "error", err)
|
||||
if _, err := app.DB().NewQuery(rawQuery).Bind(params).Execute(); err != nil {
|
||||
return fmt.Errorf("failed to delete from %s: %v", collection, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
/* Round float to two decimals */
|
||||
|
||||
381
beszel/internal/records/records_test.go
Normal file
381
beszel/internal/records/records_test.go
Normal file
@@ -0,0 +1,381 @@
|
||||
//go:build testing
|
||||
// +build testing
|
||||
|
||||
package records_test
|
||||
|
||||
import (
|
||||
"beszel/internal/records"
|
||||
"beszel/internal/tests"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/pocketbase/dbx"
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
"github.com/pocketbase/pocketbase/tools/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// TestDeleteOldRecords tests the main DeleteOldRecords function
|
||||
func TestDeleteOldRecords(t *testing.T) {
|
||||
hub, err := tests.NewTestHub(t.TempDir())
|
||||
require.NoError(t, err)
|
||||
defer hub.Cleanup()
|
||||
|
||||
rm := records.NewRecordManager(hub)
|
||||
|
||||
// Create test user for alerts history
|
||||
user, err := tests.CreateUser(hub, "test@example.com", "testtesttest")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create test system
|
||||
system, err := tests.CreateRecord(hub, "systems", map[string]any{
|
||||
"name": "test-system",
|
||||
"host": "localhost",
|
||||
"port": "45876",
|
||||
"status": "up",
|
||||
"users": []string{user.Id},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
now := time.Now()
|
||||
|
||||
// Create old system_stats records that should be deleted
|
||||
var record *core.Record
|
||||
record, err = tests.CreateRecord(hub, "system_stats", map[string]any{
|
||||
"system": system.Id,
|
||||
"type": "1m",
|
||||
"stats": `{"cpu": 50.0, "mem": 1024}`,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
// created is autodate field, so we need to set it manually
|
||||
record.SetRaw("created", now.UTC().Add(-2*time.Hour).Format(types.DefaultDateLayout))
|
||||
err = hub.SaveNoValidate(record)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, record)
|
||||
require.InDelta(t, record.GetDateTime("created").Time().UTC().Unix(), now.UTC().Add(-2*time.Hour).Unix(), 1)
|
||||
require.Equal(t, record.Get("system"), system.Id)
|
||||
require.Equal(t, record.Get("type"), "1m")
|
||||
|
||||
// Create recent system_stats record that should be kept
|
||||
_, err = tests.CreateRecord(hub, "system_stats", map[string]any{
|
||||
"system": system.Id,
|
||||
"type": "1m",
|
||||
"stats": `{"cpu": 30.0, "mem": 512}`,
|
||||
"created": now.Add(-30 * time.Minute), // 30 minutes old, should be kept
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create many alerts history records to trigger deletion
|
||||
for i := range 260 { // More than countBeforeDeletion (250)
|
||||
_, err = tests.CreateRecord(hub, "alerts_history", map[string]any{
|
||||
"user": user.Id,
|
||||
"name": "CPU",
|
||||
"value": i + 1,
|
||||
"system": system.Id,
|
||||
"created": now.Add(-time.Duration(i) * time.Minute),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// Count records before deletion
|
||||
systemStatsCountBefore, err := hub.CountRecords("system_stats")
|
||||
require.NoError(t, err)
|
||||
alertsCountBefore, err := hub.CountRecords("alerts_history")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Run deletion
|
||||
rm.DeleteOldRecords()
|
||||
|
||||
// Count records after deletion
|
||||
systemStatsCountAfter, err := hub.CountRecords("system_stats")
|
||||
require.NoError(t, err)
|
||||
alertsCountAfter, err := hub.CountRecords("alerts_history")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify old system stats were deleted
|
||||
assert.Less(t, systemStatsCountAfter, systemStatsCountBefore, "Old system stats should be deleted")
|
||||
|
||||
// Verify alerts history was trimmed
|
||||
assert.Less(t, alertsCountAfter, alertsCountBefore, "Excessive alerts history should be deleted")
|
||||
assert.Equal(t, alertsCountAfter, int64(200), "Alerts count should be equal to countToKeep (200)")
|
||||
}
|
||||
|
||||
// TestDeleteOldSystemStats tests the deleteOldSystemStats function
|
||||
func TestDeleteOldSystemStats(t *testing.T) {
|
||||
hub, err := tests.NewTestHub(t.TempDir())
|
||||
require.NoError(t, err)
|
||||
defer hub.Cleanup()
|
||||
|
||||
// Create test system
|
||||
user, err := tests.CreateUser(hub, "test@example.com", "testtesttest")
|
||||
require.NoError(t, err)
|
||||
|
||||
system, err := tests.CreateRecord(hub, "systems", map[string]any{
|
||||
"name": "test-system",
|
||||
"host": "localhost",
|
||||
"port": "45876",
|
||||
"status": "up",
|
||||
"users": []string{user.Id},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
now := time.Now().UTC()
|
||||
|
||||
// Test data for different record types and their retention periods
|
||||
testCases := []struct {
|
||||
recordType string
|
||||
retention time.Duration
|
||||
shouldBeKept bool
|
||||
ageFromNow time.Duration
|
||||
description string
|
||||
}{
|
||||
{"1m", time.Hour, true, 30 * time.Minute, "1m record within 1 hour should be kept"},
|
||||
{"1m", time.Hour, false, 2 * time.Hour, "1m record older than 1 hour should be deleted"},
|
||||
{"10m", 12 * time.Hour, true, 6 * time.Hour, "10m record within 12 hours should be kept"},
|
||||
{"10m", 12 * time.Hour, false, 24 * time.Hour, "10m record older than 12 hours should be deleted"},
|
||||
{"20m", 24 * time.Hour, true, 12 * time.Hour, "20m record within 24 hours should be kept"},
|
||||
{"20m", 24 * time.Hour, false, 48 * time.Hour, "20m record older than 24 hours should be deleted"},
|
||||
{"120m", 7 * 24 * time.Hour, true, 3 * 24 * time.Hour, "120m record within 7 days should be kept"},
|
||||
{"120m", 7 * 24 * time.Hour, false, 10 * 24 * time.Hour, "120m record older than 7 days should be deleted"},
|
||||
{"480m", 30 * 24 * time.Hour, true, 15 * 24 * time.Hour, "480m record within 30 days should be kept"},
|
||||
{"480m", 30 * 24 * time.Hour, false, 45 * 24 * time.Hour, "480m record older than 30 days should be deleted"},
|
||||
}
|
||||
|
||||
// Create test records for both system_stats and container_stats
|
||||
collections := []string{"system_stats", "container_stats"}
|
||||
recordIds := make(map[string][]string)
|
||||
|
||||
for _, collection := range collections {
|
||||
recordIds[collection] = make([]string, 0)
|
||||
|
||||
for i, tc := range testCases {
|
||||
recordTime := now.Add(-tc.ageFromNow)
|
||||
|
||||
var stats string
|
||||
if collection == "system_stats" {
|
||||
stats = fmt.Sprintf(`{"cpu": %d.0, "mem": %d}`, i*10, i*100)
|
||||
} else {
|
||||
stats = fmt.Sprintf(`[{"name": "container%d", "cpu": %d.0, "mem": %d}]`, i, i*5, i*50)
|
||||
}
|
||||
|
||||
record, err := tests.CreateRecord(hub, collection, map[string]any{
|
||||
"system": system.Id,
|
||||
"type": tc.recordType,
|
||||
"stats": stats,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
record.SetRaw("created", recordTime.Format(types.DefaultDateLayout))
|
||||
err = hub.SaveNoValidate(record)
|
||||
require.NoError(t, err)
|
||||
recordIds[collection] = append(recordIds[collection], record.Id)
|
||||
}
|
||||
}
|
||||
|
||||
// Run deletion
|
||||
err = records.TestDeleteOldSystemStats(hub)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify results
|
||||
for _, collection := range collections {
|
||||
for i, tc := range testCases {
|
||||
recordId := recordIds[collection][i]
|
||||
|
||||
// Try to find the record
|
||||
_, err := hub.FindRecordById(collection, recordId)
|
||||
|
||||
if tc.shouldBeKept {
|
||||
assert.NoError(t, err, "Record should exist: %s", tc.description)
|
||||
} else {
|
||||
assert.Error(t, err, "Record should be deleted: %s", tc.description)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestDeleteOldAlertsHistory tests the deleteOldAlertsHistory function
|
||||
func TestDeleteOldAlertsHistory(t *testing.T) {
|
||||
hub, err := tests.NewTestHub(t.TempDir())
|
||||
require.NoError(t, err)
|
||||
defer hub.Cleanup()
|
||||
|
||||
// Create test users
|
||||
user1, err := tests.CreateUser(hub, "user1@example.com", "testtesttest")
|
||||
require.NoError(t, err)
|
||||
|
||||
user2, err := tests.CreateUser(hub, "user2@example.com", "testtesttest")
|
||||
require.NoError(t, err)
|
||||
|
||||
system, err := tests.CreateRecord(hub, "systems", map[string]any{
|
||||
"name": "test-system",
|
||||
"host": "localhost",
|
||||
"port": "45876",
|
||||
"status": "up",
|
||||
"users": []string{user1.Id, user2.Id},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
now := time.Now().UTC()
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
user *core.Record
|
||||
alertCount int
|
||||
countToKeep int
|
||||
countBeforeDeletion int
|
||||
expectedAfterDeletion int
|
||||
description string
|
||||
}{
|
||||
{
|
||||
name: "User with few alerts (below threshold)",
|
||||
user: user1,
|
||||
alertCount: 100,
|
||||
countToKeep: 50,
|
||||
countBeforeDeletion: 150,
|
||||
expectedAfterDeletion: 100, // No deletion because below threshold
|
||||
description: "User with alerts below countBeforeDeletion should not have any deleted",
|
||||
},
|
||||
{
|
||||
name: "User with many alerts (above threshold)",
|
||||
user: user2,
|
||||
alertCount: 300,
|
||||
countToKeep: 100,
|
||||
countBeforeDeletion: 200,
|
||||
expectedAfterDeletion: 100, // Should be trimmed to countToKeep
|
||||
description: "User with alerts above countBeforeDeletion should be trimmed to countToKeep",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// Create alerts for this user
|
||||
for i := 0; i < tc.alertCount; i++ {
|
||||
_, err := tests.CreateRecord(hub, "alerts_history", map[string]any{
|
||||
"user": tc.user.Id,
|
||||
"name": "CPU",
|
||||
"value": i + 1,
|
||||
"system": system.Id,
|
||||
"created": now.Add(-time.Duration(i) * time.Minute),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// Count before deletion
|
||||
countBefore, err := hub.CountRecords("alerts_history",
|
||||
dbx.NewExp("user = {:user}", dbx.Params{"user": tc.user.Id}))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int64(tc.alertCount), countBefore, "Initial count should match")
|
||||
|
||||
// Run deletion
|
||||
err = records.TestDeleteOldAlertsHistory(hub, tc.countToKeep, tc.countBeforeDeletion)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Count after deletion
|
||||
countAfter, err := hub.CountRecords("alerts_history",
|
||||
dbx.NewExp("user = {:user}", dbx.Params{"user": tc.user.Id}))
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, int64(tc.expectedAfterDeletion), countAfter, tc.description)
|
||||
|
||||
// If deletion occurred, verify the most recent records were kept
|
||||
if tc.expectedAfterDeletion < tc.alertCount {
|
||||
records, err := hub.FindRecordsByFilter("alerts_history",
|
||||
"user = {:user}",
|
||||
"-created", // Order by created DESC
|
||||
tc.countToKeep,
|
||||
0,
|
||||
map[string]any{"user": tc.user.Id})
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, records, tc.expectedAfterDeletion, "Should have exactly countToKeep records")
|
||||
|
||||
// Verify records are in descending order by created time
|
||||
for i := 1; i < len(records); i++ {
|
||||
prev := records[i-1].GetDateTime("created").Time()
|
||||
curr := records[i].GetDateTime("created").Time()
|
||||
assert.True(t, prev.After(curr) || prev.Equal(curr),
|
||||
"Records should be ordered by created time (newest first)")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestDeleteOldAlertsHistoryEdgeCases tests edge cases for alerts history deletion
|
||||
func TestDeleteOldAlertsHistoryEdgeCases(t *testing.T) {
|
||||
hub, err := tests.NewTestHub(t.TempDir())
|
||||
require.NoError(t, err)
|
||||
defer hub.Cleanup()
|
||||
|
||||
t.Run("No users with excessive alerts", func(t *testing.T) {
|
||||
// Create user with few alerts
|
||||
user, err := tests.CreateUser(hub, "few@example.com", "testtesttest")
|
||||
require.NoError(t, err)
|
||||
|
||||
system, err := tests.CreateRecord(hub, "systems", map[string]any{
|
||||
"name": "test-system",
|
||||
"host": "localhost",
|
||||
"port": "45876",
|
||||
"status": "up",
|
||||
"users": []string{user.Id},
|
||||
})
|
||||
|
||||
// Create only 5 alerts (well below threshold)
|
||||
for i := range 5 {
|
||||
_, err := tests.CreateRecord(hub, "alerts_history", map[string]any{
|
||||
"user": user.Id,
|
||||
"name": "CPU",
|
||||
"value": i + 1,
|
||||
"system": system.Id,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// Should not error and should not delete anything
|
||||
err = records.TestDeleteOldAlertsHistory(hub, 10, 20)
|
||||
require.NoError(t, err)
|
||||
|
||||
count, err := hub.CountRecords("alerts_history")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int64(5), count, "All alerts should remain")
|
||||
})
|
||||
|
||||
t.Run("Empty alerts_history table", func(t *testing.T) {
|
||||
// Clear any existing alerts
|
||||
_, err := hub.DB().NewQuery("DELETE FROM alerts_history").Execute()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Should not error with empty table
|
||||
err = records.TestDeleteOldAlertsHistory(hub, 10, 20)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
// TestRecordManagerCreation tests RecordManager creation
|
||||
func TestRecordManagerCreation(t *testing.T) {
|
||||
hub, err := tests.NewTestHub(t.TempDir())
|
||||
require.NoError(t, err)
|
||||
defer hub.Cleanup()
|
||||
|
||||
rm := records.NewRecordManager(hub)
|
||||
assert.NotNil(t, rm, "RecordManager should not be nil")
|
||||
}
|
||||
|
||||
// TestTwoDecimals tests the twoDecimals helper function
|
||||
func TestTwoDecimals(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input float64
|
||||
expected float64
|
||||
}{
|
||||
{1.234567, 1.23},
|
||||
{1.235, 1.24}, // Should round up
|
||||
{1.0, 1.0},
|
||||
{0.0, 0.0},
|
||||
{-1.234567, -1.23},
|
||||
{-1.235, -1.23}, // Negative rounding
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
result := records.TestTwoDecimals(tc.input)
|
||||
assert.InDelta(t, tc.expected, result, 0.02, "twoDecimals(%f) should equal %f", tc.input, tc.expected)
|
||||
}
|
||||
}
|
||||
23
beszel/internal/records/records_test_helpers.go
Normal file
23
beszel/internal/records/records_test_helpers.go
Normal file
@@ -0,0 +1,23 @@
|
||||
//go:build testing
|
||||
// +build testing
|
||||
|
||||
package records
|
||||
|
||||
import (
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
)
|
||||
|
||||
// TestDeleteOldSystemStats exposes deleteOldSystemStats for testing
|
||||
func TestDeleteOldSystemStats(app core.App) error {
|
||||
return deleteOldSystemStats(app)
|
||||
}
|
||||
|
||||
// TestDeleteOldAlertsHistory exposes deleteOldAlertsHistory for testing
|
||||
func TestDeleteOldAlertsHistory(app core.App, countToKeep, countBeforeDeletion int) error {
|
||||
return deleteOldAlertsHistory(app, countToKeep, countBeforeDeletion)
|
||||
}
|
||||
|
||||
// TestTwoDecimals exposes twoDecimals for testing
|
||||
func TestTwoDecimals(value float64) float64 {
|
||||
return twoDecimals(value)
|
||||
}
|
||||
307
beszel/internal/tests/api.go
Normal file
307
beszel/internal/tests/api.go
Normal file
@@ -0,0 +1,307 @@
|
||||
package tests
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"maps"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/pocketbase/pocketbase/apis"
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
pbtests "github.com/pocketbase/pocketbase/tests"
|
||||
"github.com/pocketbase/pocketbase/tools/hook"
|
||||
)
|
||||
|
||||
// NOTE: This is a copy of https://github.com/pocketbase/pocketbase/blob/master/tests/api.go
|
||||
// with the following changes:
|
||||
// - Removed automatic cleanup of the test app in ApiScenario.Test (Aug 17 2025)
|
||||
|
||||
// ApiScenario defines a single api request test case/scenario.
|
||||
type ApiScenario struct {
|
||||
// Name is the test name.
|
||||
Name string
|
||||
|
||||
// Method is the HTTP method of the test request to use.
|
||||
Method string
|
||||
|
||||
// URL is the url/path of the endpoint you want to test.
|
||||
URL string
|
||||
|
||||
// Body specifies the body to send with the request.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// strings.NewReader(`{"title":"abc"}`)
|
||||
Body io.Reader
|
||||
|
||||
// Headers specifies the headers to send with the request (e.g. "Authorization": "abc")
|
||||
Headers map[string]string
|
||||
|
||||
// Delay adds a delay before checking the expectations usually
|
||||
// to ensure that all fired non-awaited go routines have finished
|
||||
Delay time.Duration
|
||||
|
||||
// Timeout specifies how long to wait before cancelling the request context.
|
||||
//
|
||||
// A zero or negative value means that there will be no timeout.
|
||||
Timeout time.Duration
|
||||
|
||||
// expectations
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
// ExpectedStatus specifies the expected response HTTP status code.
|
||||
ExpectedStatus int
|
||||
|
||||
// List of keywords that MUST exist in the response body.
|
||||
//
|
||||
// Either ExpectedContent or NotExpectedContent must be set if the response body is non-empty.
|
||||
// Leave both fields empty if you want to ensure that the response didn't have any body (e.g. 204).
|
||||
ExpectedContent []string
|
||||
|
||||
// List of keywords that MUST NOT exist in the response body.
|
||||
//
|
||||
// Either ExpectedContent or NotExpectedContent must be set if the response body is non-empty.
|
||||
// Leave both fields empty if you want to ensure that the response didn't have any body (e.g. 204).
|
||||
NotExpectedContent []string
|
||||
|
||||
// List of hook events to check whether they were fired or not.
|
||||
//
|
||||
// You can use the wildcard "*" event key if you want to ensure
|
||||
// that no other hook events except those listed have been fired.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// map[string]int{ "*": 0 } // no hook events were fired
|
||||
// map[string]int{ "*": 0, "EventA": 2 } // no hook events, except EventA were fired
|
||||
// map[string]int{ "EventA": 2, "EventB": 0 } // ensures that EventA was fired exactly 2 times and EventB exactly 0 times.
|
||||
ExpectedEvents map[string]int
|
||||
|
||||
// test hooks
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
TestAppFactory func(t testing.TB) *pbtests.TestApp
|
||||
BeforeTestFunc func(t testing.TB, app *pbtests.TestApp, e *core.ServeEvent)
|
||||
AfterTestFunc func(t testing.TB, app *pbtests.TestApp, res *http.Response)
|
||||
}
|
||||
|
||||
// Test executes the test scenario.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// func TestListExample(t *testing.T) {
|
||||
// scenario := tests.ApiScenario{
|
||||
// Name: "list example collection",
|
||||
// Method: http.MethodGet,
|
||||
// URL: "/api/collections/example/records",
|
||||
// ExpectedStatus: 200,
|
||||
// ExpectedContent: []string{
|
||||
// `"totalItems":3`,
|
||||
// `"id":"0yxhwia2amd8gec"`,
|
||||
// `"id":"achvryl401bhse3"`,
|
||||
// `"id":"llvuca81nly1qls"`,
|
||||
// },
|
||||
// ExpectedEvents: map[string]int{
|
||||
// "OnRecordsListRequest": 1,
|
||||
// "OnRecordEnrich": 3,
|
||||
// },
|
||||
// }
|
||||
//
|
||||
// scenario.Test(t)
|
||||
// }
|
||||
func (scenario *ApiScenario) Test(t *testing.T) {
|
||||
t.Run(scenario.normalizedName(), func(t *testing.T) {
|
||||
scenario.test(t)
|
||||
})
|
||||
}
|
||||
|
||||
// Benchmark benchmarks the test scenario.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// func BenchmarkListExample(b *testing.B) {
|
||||
// scenario := tests.ApiScenario{
|
||||
// Name: "list example collection",
|
||||
// Method: http.MethodGet,
|
||||
// URL: "/api/collections/example/records",
|
||||
// ExpectedStatus: 200,
|
||||
// ExpectedContent: []string{
|
||||
// `"totalItems":3`,
|
||||
// `"id":"0yxhwia2amd8gec"`,
|
||||
// `"id":"achvryl401bhse3"`,
|
||||
// `"id":"llvuca81nly1qls"`,
|
||||
// },
|
||||
// ExpectedEvents: map[string]int{
|
||||
// "OnRecordsListRequest": 1,
|
||||
// "OnRecordEnrich": 3,
|
||||
// },
|
||||
// }
|
||||
//
|
||||
// scenario.Benchmark(b)
|
||||
// }
|
||||
func (scenario *ApiScenario) Benchmark(b *testing.B) {
|
||||
b.Run(scenario.normalizedName(), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
scenario.test(b)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (scenario *ApiScenario) normalizedName() string {
|
||||
var name = scenario.Name
|
||||
|
||||
if name == "" {
|
||||
name = fmt.Sprintf("%s:%s", scenario.Method, scenario.URL)
|
||||
}
|
||||
|
||||
return name
|
||||
}
|
||||
|
||||
func (scenario *ApiScenario) test(t testing.TB) {
|
||||
var testApp *pbtests.TestApp
|
||||
if scenario.TestAppFactory != nil {
|
||||
testApp = scenario.TestAppFactory(t)
|
||||
if testApp == nil {
|
||||
t.Fatal("TestAppFactory must return a non-nill app instance")
|
||||
}
|
||||
} else {
|
||||
var testAppErr error
|
||||
testApp, testAppErr = pbtests.NewTestApp()
|
||||
if testAppErr != nil {
|
||||
t.Fatalf("Failed to initialize the test app instance: %v", testAppErr)
|
||||
}
|
||||
}
|
||||
// defer testApp.Cleanup()
|
||||
|
||||
baseRouter, err := apis.NewRouter(testApp)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// manually trigger the serve event to ensure that custom app routes and middlewares are registered
|
||||
serveEvent := new(core.ServeEvent)
|
||||
serveEvent.App = testApp
|
||||
serveEvent.Router = baseRouter
|
||||
|
||||
serveErr := testApp.OnServe().Trigger(serveEvent, func(e *core.ServeEvent) error {
|
||||
if scenario.BeforeTestFunc != nil {
|
||||
scenario.BeforeTestFunc(t, testApp, e)
|
||||
}
|
||||
|
||||
// reset the event counters in case a hook was triggered from a before func (eg. db save)
|
||||
testApp.ResetEventCalls()
|
||||
|
||||
// add middleware to timeout long-running requests (eg. keep-alive routes)
|
||||
e.Router.Bind(&hook.Handler[*core.RequestEvent]{
|
||||
Func: func(re *core.RequestEvent) error {
|
||||
slowTimer := time.AfterFunc(3*time.Second, func() {
|
||||
t.Logf("[WARN] Long running test %q", scenario.Name)
|
||||
})
|
||||
defer slowTimer.Stop()
|
||||
|
||||
if scenario.Timeout > 0 {
|
||||
ctx, cancelFunc := context.WithTimeout(re.Request.Context(), scenario.Timeout)
|
||||
defer cancelFunc()
|
||||
re.Request = re.Request.Clone(ctx)
|
||||
}
|
||||
|
||||
return re.Next()
|
||||
},
|
||||
Priority: -9999,
|
||||
})
|
||||
|
||||
recorder := httptest.NewRecorder()
|
||||
|
||||
req := httptest.NewRequest(scenario.Method, scenario.URL, scenario.Body)
|
||||
|
||||
// set default header
|
||||
req.Header.Set("content-type", "application/json")
|
||||
|
||||
// set scenario headers
|
||||
for k, v := range scenario.Headers {
|
||||
req.Header.Set(k, v)
|
||||
}
|
||||
|
||||
// execute request
|
||||
mux, err := e.Router.BuildMux()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to build router mux: %v", err)
|
||||
}
|
||||
mux.ServeHTTP(recorder, req)
|
||||
|
||||
res := recorder.Result()
|
||||
|
||||
if res.StatusCode != scenario.ExpectedStatus {
|
||||
t.Errorf("Expected status code %d, got %d", scenario.ExpectedStatus, res.StatusCode)
|
||||
}
|
||||
|
||||
if scenario.Delay > 0 {
|
||||
time.Sleep(scenario.Delay)
|
||||
}
|
||||
|
||||
if len(scenario.ExpectedContent) == 0 && len(scenario.NotExpectedContent) == 0 {
|
||||
if len(recorder.Body.Bytes()) != 0 {
|
||||
t.Errorf("Expected empty body, got \n%v", recorder.Body.String())
|
||||
}
|
||||
} else {
|
||||
// normalize json response format
|
||||
/* buffer := new(bytes.Buffer)
|
||||
err := json.Compact(buffer, recorder.Body.Bytes())
|
||||
var normalizedBody string
|
||||
if err != nil {
|
||||
// not a json...
|
||||
normalizedBody = recorder.Body.String()
|
||||
} else {
|
||||
normalizedBody = buffer.String()
|
||||
}
|
||||
|
||||
for _, item := range scenario.ExpectedContent {
|
||||
if !strings.Contains(normalizedBody, item) {
|
||||
t.Errorf("Cannot find %v in response body \n%v", item, normalizedBody)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for _, item := range scenario.NotExpectedContent {
|
||||
if strings.Contains(normalizedBody, item) {
|
||||
t.Errorf("Didn't expect %v in response body \n%v", item, normalizedBody)
|
||||
break
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
remainingEvents := maps.Clone(testApp.EventCalls)
|
||||
|
||||
var noOtherEventsShouldRemain bool
|
||||
for event, expectedNum := range scenario.ExpectedEvents {
|
||||
if event == "*" && expectedNum <= 0 {
|
||||
noOtherEventsShouldRemain = true
|
||||
continue
|
||||
}
|
||||
|
||||
actualNum := remainingEvents[event]
|
||||
if actualNum != expectedNum {
|
||||
t.Errorf("Expected event %s to be called %d, got %d", event, expectedNum, actualNum)
|
||||
}
|
||||
|
||||
delete(remainingEvents, event)
|
||||
}
|
||||
|
||||
if noOtherEventsShouldRemain && len(remainingEvents) > 0 {
|
||||
t.Errorf("Missing expected remaining events:\n%#v\nAll triggered app events are:\n%#v", remainingEvents, testApp.EventCalls)
|
||||
}
|
||||
|
||||
if scenario.AfterTestFunc != nil {
|
||||
scenario.AfterTestFunc(t, testApp, res)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if serveErr != nil {
|
||||
t.Fatalf("Failed to trigger app serve hook: %v", serveErr)
|
||||
}
|
||||
}
|
||||
@@ -6,9 +6,12 @@ package tests
|
||||
|
||||
import (
|
||||
"beszel/internal/hub"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
"github.com/pocketbase/pocketbase/tests"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
_ "github.com/pocketbase/pocketbase/migrations"
|
||||
)
|
||||
@@ -86,3 +89,10 @@ func CreateRecord(app core.App, collectionName string, fields map[string]any) (*
|
||||
|
||||
return record, app.Save(record)
|
||||
}
|
||||
|
||||
func ClearCollection(t testing.TB, app core.App, collectionName string) error {
|
||||
_, err := app.DB().NewQuery(fmt.Sprintf("DELETE from %s", collectionName)).Execute()
|
||||
recordCount, err := app.CountRecords(collectionName)
|
||||
assert.EqualValues(t, recordCount, 0, "should have 0 records after clearing")
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/pocketbase/dbx"
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
)
|
||||
|
||||
@@ -13,15 +14,6 @@ type UserManager struct {
|
||||
app core.App
|
||||
}
|
||||
|
||||
type UserSettings struct {
|
||||
ChartTime string `json:"chartTime"`
|
||||
NotificationEmails []string `json:"emails"`
|
||||
NotificationWebhooks []string `json:"webhooks"`
|
||||
// UnitTemp uint8 `json:"unitTemp"` // 0 for Celsius, 1 for Fahrenheit
|
||||
// UnitNet uint8 `json:"unitNet"` // 0 for bytes, 1 for bits
|
||||
// UnitDisk uint8 `json:"unitDisk"` // 0 for bytes, 1 for bits
|
||||
}
|
||||
|
||||
func NewUserManager(app core.App) *UserManager {
|
||||
return &UserManager{
|
||||
app: app,
|
||||
@@ -39,29 +31,26 @@ func (um *UserManager) InitializeUserRole(e *core.RecordEvent) error {
|
||||
// Initialize user settings with defaults if not set
|
||||
func (um *UserManager) InitializeUserSettings(e *core.RecordEvent) error {
|
||||
record := e.Record
|
||||
// intialize settings with defaults
|
||||
settings := UserSettings{
|
||||
ChartTime: "1h",
|
||||
NotificationEmails: []string{},
|
||||
NotificationWebhooks: []string{},
|
||||
// intialize settings with defaults (zero values can be ignored)
|
||||
settings := struct {
|
||||
ChartTime string `json:"chartTime"`
|
||||
Emails []string `json:"emails"`
|
||||
}{
|
||||
ChartTime: "1h",
|
||||
}
|
||||
record.UnmarshalJSONField("settings", &settings)
|
||||
if len(settings.NotificationEmails) == 0 {
|
||||
// get user email from auth record
|
||||
if errs := um.app.ExpandRecord(record, []string{"user"}, nil); len(errs) == 0 {
|
||||
// app.Logger().Error("failed to expand user relation", "errs", errs)
|
||||
if user := record.ExpandedOne("user"); user != nil {
|
||||
settings.NotificationEmails = []string{user.GetString("email")}
|
||||
} else {
|
||||
log.Println("Failed to get user email from auth record")
|
||||
}
|
||||
} else {
|
||||
log.Println("failed to expand user relation", "errs", errs)
|
||||
}
|
||||
// get user email from auth record
|
||||
var user struct {
|
||||
Email string `db:"email"`
|
||||
}
|
||||
// if len(settings.NotificationWebhooks) == 0 {
|
||||
// settings.NotificationWebhooks = []string{""}
|
||||
// }
|
||||
err := e.App.DB().NewQuery("SELECT email FROM users WHERE id = {:id}").Bind(dbx.Params{
|
||||
"id": record.GetString("user"),
|
||||
}).One(&user)
|
||||
if err != nil {
|
||||
log.Println("failed to get user email", "err", err)
|
||||
return err
|
||||
}
|
||||
settings.Emails = []string{user.Email}
|
||||
record.Set("settings", settings)
|
||||
return e.Next()
|
||||
}
|
||||
|
||||
@@ -140,6 +140,124 @@ func init() {
|
||||
],
|
||||
"system": false
|
||||
},
|
||||
{
|
||||
"id": "pbc_1697146157",
|
||||
"listRule": "@request.auth.id != \"\" && user.id = @request.auth.id",
|
||||
"viewRule": "@request.auth.id != \"\" && user.id = @request.auth.id",
|
||||
"createRule": null,
|
||||
"updateRule": null,
|
||||
"deleteRule": "@request.auth.id != \"\" && user.id = @request.auth.id",
|
||||
"name": "alerts_history",
|
||||
"type": "base",
|
||||
"fields": [
|
||||
{
|
||||
"autogeneratePattern": "[a-z0-9]{15}",
|
||||
"hidden": false,
|
||||
"id": "text3208210256",
|
||||
"max": 15,
|
||||
"min": 15,
|
||||
"name": "id",
|
||||
"pattern": "^[a-z0-9]+$",
|
||||
"presentable": false,
|
||||
"primaryKey": true,
|
||||
"required": true,
|
||||
"system": true,
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"cascadeDelete": true,
|
||||
"collectionId": "_pb_users_auth_",
|
||||
"hidden": false,
|
||||
"id": "relation2375276105",
|
||||
"maxSelect": 1,
|
||||
"minSelect": 0,
|
||||
"name": "user",
|
||||
"presentable": false,
|
||||
"required": true,
|
||||
"system": false,
|
||||
"type": "relation"
|
||||
},
|
||||
{
|
||||
"cascadeDelete": true,
|
||||
"collectionId": "2hz5ncl8tizk5nx",
|
||||
"hidden": false,
|
||||
"id": "relation3377271179",
|
||||
"maxSelect": 1,
|
||||
"minSelect": 0,
|
||||
"name": "system",
|
||||
"presentable": false,
|
||||
"required": true,
|
||||
"system": false,
|
||||
"type": "relation"
|
||||
},
|
||||
{
|
||||
"autogeneratePattern": "",
|
||||
"hidden": false,
|
||||
"id": "text2466471794",
|
||||
"max": 0,
|
||||
"min": 0,
|
||||
"name": "alert_id",
|
||||
"pattern": "",
|
||||
"presentable": false,
|
||||
"primaryKey": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"autogeneratePattern": "",
|
||||
"hidden": false,
|
||||
"id": "text1579384326",
|
||||
"max": 0,
|
||||
"min": 0,
|
||||
"name": "name",
|
||||
"pattern": "",
|
||||
"presentable": false,
|
||||
"primaryKey": false,
|
||||
"required": true,
|
||||
"system": false,
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"hidden": false,
|
||||
"id": "number494360628",
|
||||
"max": null,
|
||||
"min": null,
|
||||
"name": "value",
|
||||
"onlyInt": false,
|
||||
"presentable": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"hidden": false,
|
||||
"id": "autodate2990389176",
|
||||
"name": "created",
|
||||
"onCreate": true,
|
||||
"onUpdate": false,
|
||||
"presentable": false,
|
||||
"system": false,
|
||||
"type": "autodate"
|
||||
},
|
||||
{
|
||||
"hidden": false,
|
||||
"id": "date2276568630",
|
||||
"max": "",
|
||||
"min": "",
|
||||
"name": "resolved",
|
||||
"presentable": false,
|
||||
"required": false,
|
||||
"system": false,
|
||||
"type": "date"
|
||||
}
|
||||
],
|
||||
"indexes": [
|
||||
"CREATE INDEX ` + "`" + `idx_YdGnup5aqB` + "`" + ` ON ` + "`" + `alerts_history` + "`" + ` (` + "`" + `user` + "`" + `)",
|
||||
"CREATE INDEX ` + "`" + `idx_taLet9VdME` + "`" + ` ON ` + "`" + `alerts_history` + "`" + ` (` + "`" + `created` + "`" + `)"
|
||||
],
|
||||
"system": false
|
||||
},
|
||||
{
|
||||
"id": "juohu4jipgc13v7",
|
||||
"listRule": "@request.auth.id != \"\"",
|
||||
@@ -757,7 +875,6 @@ func init() {
|
||||
LEFT JOIN fingerprints f ON s.id = f.system
|
||||
WHERE f.system IS NULL
|
||||
`).Column(&systemIds)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
Binary file not shown.
@@ -4,7 +4,7 @@
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="manifest" href="./static/manifest.json" />
|
||||
<link rel="icon" type="image/svg+xml" href="./static/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0,maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
|
||||
<title>Beszel</title>
|
||||
<script>
|
||||
globalThis.BESZEL = {
|
||||
|
||||
435
beszel/site/package-lock.json
generated
435
beszel/site/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "beszel",
|
||||
"private": true,
|
||||
"version": "0.12.0-beta2",
|
||||
"version": "0.12.3",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
@@ -13,9 +13,9 @@
|
||||
"dependencies": {
|
||||
"@henrygd/queue": "^1.0.7",
|
||||
"@henrygd/semaphore": "^0.0.2",
|
||||
"@lingui/detect-locale": "^5.3.2",
|
||||
"@lingui/macro": "^5.3.2",
|
||||
"@lingui/react": "^5.3.2",
|
||||
"@lingui/detect-locale": "^5.3.3",
|
||||
"@lingui/macro": "^5.3.3",
|
||||
"@lingui/react": "^5.3.3",
|
||||
"@nanostores/react": "^0.7.3",
|
||||
"@nanostores/router": "^0.11.0",
|
||||
"@radix-ui/react-alert-dialog": "^1.1.14",
|
||||
@@ -39,24 +39,25 @@
|
||||
"d3-time": "^3.1.0",
|
||||
"lucide-react": "^0.452.0",
|
||||
"nanostores": "^0.11.4",
|
||||
"pocketbase": "^0.26.0",
|
||||
"pocketbase": "^0.26.2",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"recharts": "^2.15.3",
|
||||
"recharts": "^2.15.4",
|
||||
"tailwind-merge": "^2.6.0",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"valibot": "^0.42.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@lingui/cli": "^5.3.2",
|
||||
"@lingui/cli": "^5.3.3",
|
||||
"@lingui/swc-plugin": "^5.5.2",
|
||||
"@lingui/vite-plugin": "^5.3.2",
|
||||
"@types/bun": "^1.2.15",
|
||||
"@lingui/vite-plugin": "^5.3.3",
|
||||
"@tailwindcss/container-queries": "^0.1.1",
|
||||
"@types/bun": "^1.2.19",
|
||||
"@types/react": "^18.3.23",
|
||||
"@types/react-dom": "^18.3.7",
|
||||
"@vitejs/plugin-react-swc": "^3.10.1",
|
||||
"@vitejs/plugin-react-swc": "^3.11.0",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"postcss": "^8.5.4",
|
||||
"postcss": "^8.5.6",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"tailwindcss-rtl": "^0.9.0",
|
||||
"typescript": "^5.8.3",
|
||||
|
||||
164
beszel/site/src/components/alerts-history-columns.tsx
Normal file
164
beszel/site/src/components/alerts-history-columns.tsx
Normal file
@@ -0,0 +1,164 @@
|
||||
import { ColumnDef } from "@tanstack/react-table"
|
||||
import { AlertsHistoryRecord } from "@/types"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { alertInfo, formatShortDate, toFixedFloat, formatDuration, cn } from "@/lib/utils"
|
||||
import { Trans } from "@lingui/react/macro"
|
||||
import { t } from "@lingui/core/macro"
|
||||
|
||||
export const alertsHistoryColumns: ColumnDef<AlertsHistoryRecord>[] = [
|
||||
{
|
||||
accessorKey: "system",
|
||||
enableSorting: true,
|
||||
header: ({ column }) => (
|
||||
<Button variant="ghost" onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}>
|
||||
<Trans>System</Trans>
|
||||
</Button>
|
||||
),
|
||||
cell: ({ row }) => <span className="ps-2">{row.original.expand?.system?.name || row.original.system}</span>,
|
||||
filterFn: (row, _, filterValue) => {
|
||||
const display = row.original.expand?.system?.name || row.original.system || ""
|
||||
return display.toLowerCase().includes(filterValue.toLowerCase())
|
||||
},
|
||||
},
|
||||
{
|
||||
// accessorKey: "name",
|
||||
id: "name",
|
||||
accessorFn: (record) => {
|
||||
const name = record.name
|
||||
const info = alertInfo[name]
|
||||
return info?.name().replace("cpu", "CPU") || name
|
||||
},
|
||||
header: ({ column }) => (
|
||||
<Button variant="ghost" onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}>
|
||||
<Trans>Name</Trans>
|
||||
</Button>
|
||||
),
|
||||
cell: ({ getValue, row }) => {
|
||||
let name = getValue() as string
|
||||
const info = alertInfo[row.original.name]
|
||||
const Icon = info?.icon
|
||||
|
||||
return (
|
||||
<span className="flex items-center gap-2 ps-1 min-w-40">
|
||||
{Icon && <Icon className="size-3.5" />}
|
||||
{name}
|
||||
</span>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "value",
|
||||
enableSorting: false,
|
||||
header: () => (
|
||||
<Button variant="ghost">
|
||||
<Trans>Value</Trans>
|
||||
</Button>
|
||||
),
|
||||
cell({ row, getValue }) {
|
||||
const name = row.original.name
|
||||
if (name === "Status") {
|
||||
return <span className="ps-2">{t`Down`}</span>
|
||||
}
|
||||
const value = getValue() as number
|
||||
const unit = alertInfo[name]?.unit
|
||||
return (
|
||||
<span className="tabular-nums ps-2.5">
|
||||
{toFixedFloat(value, value < 10 ? 2 : 1)}
|
||||
{unit}
|
||||
</span>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "state",
|
||||
enableSorting: true,
|
||||
sortingFn: (rowA, rowB) => (rowA.original.resolved ? 1 : 0) - (rowB.original.resolved ? 1 : 0),
|
||||
header: ({ column }) => (
|
||||
<Button variant="ghost" onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}>
|
||||
<Trans comment="Context: alert state (active or resolved)">State</Trans>
|
||||
</Button>
|
||||
),
|
||||
cell: ({ row }) => {
|
||||
const resolved = row.original.resolved
|
||||
return (
|
||||
<Badge
|
||||
className={cn(
|
||||
"capitalize pointer-events-none",
|
||||
resolved
|
||||
? "bg-green-100 text-green-800 border-green-200 dark:opacity-80"
|
||||
: "bg-yellow-100 text-yellow-800 border-yellow-200"
|
||||
)}
|
||||
>
|
||||
{/* {resolved ? <CircleCheckIcon className="size-3 me-0.5" /> : <CircleAlertIcon className="size-3 me-0.5" />} */}
|
||||
{resolved ? <Trans>Resolved</Trans> : <Trans>Active</Trans>}
|
||||
</Badge>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "created",
|
||||
accessorFn: (record) => formatShortDate(record.created),
|
||||
enableSorting: true,
|
||||
invertSorting: true,
|
||||
header: ({ column }) => (
|
||||
<Button variant="ghost" onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}>
|
||||
<Trans comment="Context: date created">Created</Trans>
|
||||
</Button>
|
||||
),
|
||||
cell: ({ getValue, row }) => (
|
||||
<span className="ps-1 tabular-nums tracking-tight" title={`${row.original.created} UTC`}>
|
||||
{getValue() as string}
|
||||
</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: "resolved",
|
||||
enableSorting: true,
|
||||
invertSorting: true,
|
||||
header: ({ column }) => (
|
||||
<Button variant="ghost" onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}>
|
||||
<Trans>Resolved</Trans>
|
||||
</Button>
|
||||
),
|
||||
cell: ({ row, getValue }) => {
|
||||
const resolved = getValue() as string | null
|
||||
if (!resolved) {
|
||||
return null
|
||||
}
|
||||
return (
|
||||
<span className="ps-1 tabular-nums tracking-tight" title={`${row.original.resolved} UTC`}>
|
||||
{formatShortDate(resolved)}
|
||||
</span>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "duration",
|
||||
invertSorting: true,
|
||||
enableSorting: true,
|
||||
sortingFn: (rowA, rowB) => {
|
||||
const aCreated = new Date(rowA.original.created)
|
||||
const bCreated = new Date(rowB.original.created)
|
||||
const aResolved = rowA.original.resolved ? new Date(rowA.original.resolved) : null
|
||||
const bResolved = rowB.original.resolved ? new Date(rowB.original.resolved) : null
|
||||
const aDuration = aResolved ? aResolved.getTime() - aCreated.getTime() : null
|
||||
const bDuration = bResolved ? bResolved.getTime() - bCreated.getTime() : null
|
||||
if (!aDuration && bDuration) return -1
|
||||
if (aDuration && !bDuration) return 1
|
||||
return (aDuration || 0) - (bDuration || 0)
|
||||
},
|
||||
header: ({ column }) => (
|
||||
<Button variant="ghost" onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}>
|
||||
<Trans>Duration</Trans>
|
||||
</Button>
|
||||
),
|
||||
cell: ({ row }) => {
|
||||
const duration = formatDuration(row.original.created, row.original.resolved)
|
||||
if (!duration) {
|
||||
return null
|
||||
}
|
||||
return <span className="ps-2">{duration}</span>
|
||||
},
|
||||
},
|
||||
]
|
||||
@@ -1,32 +1,19 @@
|
||||
import { t } from "@lingui/core/macro"
|
||||
import { Trans } from "@lingui/react/macro"
|
||||
import { memo, useMemo, useState } from "react"
|
||||
import { useStore } from "@nanostores/react"
|
||||
import { $alerts } from "@/lib/stores"
|
||||
import {
|
||||
Dialog,
|
||||
DialogTrigger,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog"
|
||||
import { BellIcon, GlobeIcon, ServerIcon, HourglassIcon } from "lucide-react"
|
||||
import { alertInfo, cn } from "@/lib/utils"
|
||||
import { Dialog, DialogTrigger, DialogContent } from "@/components/ui/dialog"
|
||||
import { BellIcon } from "lucide-react"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { AlertRecord, SystemRecord } from "@/types"
|
||||
import { $router, Link } from "../router"
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
||||
import { Checkbox } from "../ui/checkbox"
|
||||
import { Collapsible } from "../ui/collapsible"
|
||||
import { SystemAlert, SystemAlertGlobal } from "./alerts-system"
|
||||
import { getPagePath } from "@nanostores/router"
|
||||
import { SystemRecord } from "@/types"
|
||||
import { AlertDialogContent } from "./alerts-dialog"
|
||||
|
||||
export default memo(function AlertsButton({ system }: { system: SystemRecord }) {
|
||||
const alerts = useStore($alerts)
|
||||
const [opened, setOpened] = useState(false)
|
||||
const alerts = useStore($alerts)
|
||||
|
||||
const hasAlert = alerts.some((alert) => alert.system === system.id)
|
||||
const hasSystemAlert = alerts[system.id]?.size > 0
|
||||
|
||||
return useMemo(
|
||||
() => (
|
||||
@@ -35,7 +22,7 @@ export default memo(function AlertsButton({ system }: { system: SystemRecord })
|
||||
<Button variant="ghost" size="icon" aria-label={t`Alerts`} data-nolink onClick={() => setOpened(true)}>
|
||||
<BellIcon
|
||||
className={cn("h-[1.2em] w-[1.2em] pointer-events-none", {
|
||||
"fill-primary": hasAlert,
|
||||
"fill-primary": hasSystemAlert,
|
||||
})}
|
||||
/>
|
||||
</Button>
|
||||
@@ -45,7 +32,7 @@ export default memo(function AlertsButton({ system }: { system: SystemRecord })
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
),
|
||||
[opened, hasAlert]
|
||||
[opened, hasSystemAlert]
|
||||
)
|
||||
|
||||
// return useMemo(
|
||||
@@ -68,87 +55,3 @@ export default memo(function AlertsButton({ system }: { system: SystemRecord })
|
||||
// [opened, hasAlert]
|
||||
// )
|
||||
})
|
||||
|
||||
function AlertDialogContent({ system }: { system: SystemRecord }) {
|
||||
const alerts = useStore($alerts)
|
||||
const [overwriteExisting, setOverwriteExisting] = useState<boolean | "indeterminate">(false)
|
||||
|
||||
// alertsSignature changes only when alerts for this system change
|
||||
let alertsSignature = ""
|
||||
const systemAlerts = alerts.filter((alert) => {
|
||||
if (alert.system === system.id) {
|
||||
alertsSignature += alert.name + alert.min + alert.value
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}) as AlertRecord[]
|
||||
|
||||
return useMemo(() => {
|
||||
// console.log("render modal", system.name, alertsSignature)
|
||||
const data = Object.keys(alertInfo).map((name) => {
|
||||
const alert = alertInfo[name as keyof typeof alertInfo]
|
||||
return {
|
||||
name: name as keyof typeof alertInfo,
|
||||
alert,
|
||||
system,
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
<>
|
||||
<DialogHeader>
|
||||
<DialogTitle className="text-xl">
|
||||
<Trans>Alerts</Trans>
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
<Trans>
|
||||
See{" "}
|
||||
<Link href={getPagePath($router, "settings", { name: "notifications" })} className="link">
|
||||
notification settings
|
||||
</Link>{" "}
|
||||
to configure how you receive alerts.
|
||||
</Trans>
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<Tabs defaultValue="system">
|
||||
<TabsList className="mb-1 -mt-0.5">
|
||||
<TabsTrigger value="system">
|
||||
<ServerIcon className="me-2 h-3.5 w-3.5" />
|
||||
{system.name}
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="global">
|
||||
<GlobeIcon className="me-1.5 h-3.5 w-3.5" />
|
||||
<Trans>All Systems</Trans>
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="system">
|
||||
<div className="grid gap-3">
|
||||
{data.map((d) => (
|
||||
<SystemAlert key={d.name} system={system} data={d} systemAlerts={systemAlerts} />
|
||||
))}
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="global">
|
||||
<label
|
||||
htmlFor="ovw"
|
||||
className="mb-3 flex gap-2 items-center justify-center cursor-pointer border rounded-sm py-3 px-4 border-destructive text-destructive font-semibold text-sm"
|
||||
>
|
||||
<Checkbox
|
||||
id="ovw"
|
||||
className="text-destructive border-destructive data-[state=checked]:bg-destructive"
|
||||
checked={overwriteExisting}
|
||||
onCheckedChange={setOverwriteExisting}
|
||||
/>
|
||||
<Trans>Overwrite existing alerts</Trans>
|
||||
</label>
|
||||
<div className="grid gap-3">
|
||||
{data.map((d) => (
|
||||
<SystemAlertGlobal key={d.name} data={d} overwrite={overwriteExisting} />
|
||||
))}
|
||||
</div>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</>
|
||||
)
|
||||
}, [alertsSignature, overwriteExisting])
|
||||
}
|
||||
|
||||
297
beszel/site/src/components/alerts/alerts-dialog.tsx
Normal file
297
beszel/site/src/components/alerts/alerts-dialog.tsx
Normal file
@@ -0,0 +1,297 @@
|
||||
import { t } from "@lingui/core/macro"
|
||||
import { Trans, Plural } from "@lingui/react/macro"
|
||||
import { $alerts, $systems, pb } from "@/lib/stores"
|
||||
import { alertInfo, cn, debounce } from "@/lib/utils"
|
||||
import { Switch } from "@/components/ui/switch"
|
||||
import { AlertInfo, AlertRecord, SystemRecord } from "@/types"
|
||||
import { lazy, memo, Suspense, useMemo, useState } from "react"
|
||||
import { toast } from "@/components/ui/use-toast"
|
||||
import { useStore } from "@nanostores/react"
|
||||
import { getPagePath } from "@nanostores/router"
|
||||
import { Checkbox } from "@/components/ui/checkbox"
|
||||
import { DialogTitle, DialogDescription } from "@/components/ui/dialog"
|
||||
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs"
|
||||
import { ServerIcon, GlobeIcon } from "lucide-react"
|
||||
import { $router, Link } from "@/components/router"
|
||||
import { DialogHeader } from "@/components/ui/dialog"
|
||||
|
||||
const Slider = lazy(() => import("@/components/ui/slider"))
|
||||
|
||||
const endpoint = "/api/beszel/user-alerts"
|
||||
|
||||
const alertDebounce = 100
|
||||
|
||||
const alertKeys = Object.keys(alertInfo) as (keyof typeof alertInfo)[]
|
||||
|
||||
const failedUpdateToast = (error: unknown) => {
|
||||
console.error(error)
|
||||
toast({
|
||||
title: t`Failed to update alert`,
|
||||
description: t`Please check logs for more details.`,
|
||||
variant: "destructive",
|
||||
})
|
||||
}
|
||||
|
||||
/** Create or update alerts for a given name and systems */
|
||||
const upsertAlerts = debounce(
|
||||
async ({ name, value, min, systems }: { name: string; value: number; min: number; systems: string[] }) => {
|
||||
try {
|
||||
await pb.send<{ success: boolean }>(endpoint, {
|
||||
method: "POST",
|
||||
// overwrite is always true because we've done filtering client side
|
||||
body: { name, value, min, systems, overwrite: true },
|
||||
})
|
||||
} catch (error) {
|
||||
failedUpdateToast(error)
|
||||
}
|
||||
},
|
||||
alertDebounce
|
||||
)
|
||||
|
||||
/** Delete alerts for a given name and systems */
|
||||
const deleteAlerts = debounce(async ({ name, systems }: { name: string; systems: string[] }) => {
|
||||
try {
|
||||
await pb.send<{ success: boolean }>(endpoint, {
|
||||
method: "DELETE",
|
||||
body: { name, systems },
|
||||
})
|
||||
} catch (error) {
|
||||
failedUpdateToast(error)
|
||||
}
|
||||
}, alertDebounce)
|
||||
|
||||
export const AlertDialogContent = memo(function AlertDialogContent({ system }: { system: SystemRecord }) {
|
||||
const alerts = useStore($alerts)
|
||||
const [overwriteExisting, setOverwriteExisting] = useState<boolean | "indeterminate">(false)
|
||||
const [currentTab, setCurrentTab] = useState("system")
|
||||
|
||||
const systemAlerts = alerts[system.id] ?? new Map()
|
||||
|
||||
// We need to keep a copy of alerts when we switch to global tab. If we always compare to
|
||||
// current alerts, it will only be updated when first checked, then won't be updated because
|
||||
// after that it exists.
|
||||
const alertsWhenGlobalSelected = useMemo(() => {
|
||||
return currentTab === "global" ? structuredClone(alerts) : alerts
|
||||
}, [currentTab])
|
||||
|
||||
return (
|
||||
<>
|
||||
<DialogHeader>
|
||||
<DialogTitle className="text-xl">
|
||||
<Trans>Alerts</Trans>
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
<Trans>
|
||||
See{" "}
|
||||
<Link href={getPagePath($router, "settings", { name: "notifications" })} className="link">
|
||||
notification settings
|
||||
</Link>{" "}
|
||||
to configure how you receive alerts.
|
||||
</Trans>
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<Tabs defaultValue="system" onValueChange={setCurrentTab}>
|
||||
<TabsList className="mb-1 -mt-0.5">
|
||||
<TabsTrigger value="system">
|
||||
<ServerIcon className="me-2 h-3.5 w-3.5" />
|
||||
{system.name}
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="global">
|
||||
<GlobeIcon className="me-1.5 h-3.5 w-3.5" />
|
||||
<Trans>All Systems</Trans>
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="system">
|
||||
<div className="grid gap-3">
|
||||
{alertKeys.map((name) => (
|
||||
<AlertContent
|
||||
key={name}
|
||||
alertKey={name}
|
||||
data={alertInfo[name as keyof typeof alertInfo]}
|
||||
alert={systemAlerts.get(name)}
|
||||
system={system}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="global">
|
||||
<label
|
||||
htmlFor="ovw"
|
||||
className="mb-3 flex gap-2 items-center justify-center cursor-pointer border rounded-sm py-3 px-4 border-destructive text-destructive font-semibold text-sm"
|
||||
>
|
||||
<Checkbox
|
||||
id="ovw"
|
||||
className="text-destructive border-destructive data-[state=checked]:bg-destructive"
|
||||
checked={overwriteExisting}
|
||||
onCheckedChange={setOverwriteExisting}
|
||||
/>
|
||||
<Trans>Overwrite existing alerts</Trans>
|
||||
</label>
|
||||
<div className="grid gap-3">
|
||||
{alertKeys.map((name) => (
|
||||
<AlertContent
|
||||
key={name}
|
||||
alertKey={name}
|
||||
system={system}
|
||||
alert={systemAlerts.get(name)}
|
||||
data={alertInfo[name as keyof typeof alertInfo]}
|
||||
global={true}
|
||||
overwriteExisting={!!overwriteExisting}
|
||||
initialAlertsState={alertsWhenGlobalSelected}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</>
|
||||
)
|
||||
})
|
||||
|
||||
export function AlertContent({
|
||||
alertKey,
|
||||
data: alertData,
|
||||
system,
|
||||
alert,
|
||||
global = false,
|
||||
overwriteExisting = false,
|
||||
initialAlertsState = {},
|
||||
}: {
|
||||
alertKey: string
|
||||
data: AlertInfo
|
||||
system: SystemRecord
|
||||
alert?: AlertRecord
|
||||
global?: boolean
|
||||
overwriteExisting?: boolean
|
||||
initialAlertsState?: Record<string, Map<string, AlertRecord>>
|
||||
}) {
|
||||
const { name } = alertData
|
||||
|
||||
const singleDescription = alertData.singleDesc?.()
|
||||
|
||||
const [checked, setChecked] = useState(global ? false : !!alert)
|
||||
const [min, setMin] = useState(alert?.min || 10)
|
||||
const [value, setValue] = useState(alert?.value || (singleDescription ? 0 : alertData.start ?? 80))
|
||||
|
||||
const Icon = alertData.icon
|
||||
|
||||
/** Get system ids to update */
|
||||
function getSystemIds(): string[] {
|
||||
// if not global, update only the current system
|
||||
if (!global) {
|
||||
return [system.id]
|
||||
}
|
||||
// if global, update all systems when overwriteExisting is true
|
||||
// update only systems without an existing alert when overwriteExisting is false
|
||||
const allSystems = $systems.get()
|
||||
const systemIds: string[] = []
|
||||
for (const system of allSystems) {
|
||||
if (overwriteExisting || !initialAlertsState[system.id]?.has(alertKey)) {
|
||||
systemIds.push(system.id)
|
||||
}
|
||||
}
|
||||
return systemIds
|
||||
}
|
||||
|
||||
function sendUpsert(min: number, value: number) {
|
||||
const systems = getSystemIds()
|
||||
systems.length &&
|
||||
upsertAlerts({
|
||||
name: alertKey,
|
||||
value,
|
||||
min,
|
||||
systems,
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="rounded-lg border border-muted-foreground/15 hover:border-muted-foreground/20 transition-colors duration-100 group">
|
||||
<label
|
||||
htmlFor={`s${name}`}
|
||||
className={cn("flex flex-row items-center justify-between gap-4 cursor-pointer p-4", {
|
||||
"pb-0": checked,
|
||||
})}
|
||||
>
|
||||
<div className="grid gap-1 select-none">
|
||||
<p className="font-semibold flex gap-3 items-center">
|
||||
<Icon className="h-4 w-4 opacity-85" /> {alertData.name()}
|
||||
</p>
|
||||
{!checked && <span className="block text-sm text-muted-foreground">{alertData.desc()}</span>}
|
||||
</div>
|
||||
<Switch
|
||||
id={`s${name}`}
|
||||
checked={checked}
|
||||
onCheckedChange={(newChecked) => {
|
||||
setChecked(newChecked)
|
||||
if (newChecked) {
|
||||
// if alert checked, create or update alert
|
||||
sendUpsert(min, value)
|
||||
} else {
|
||||
// if unchecked, delete alert (unless global and overwriteExisting is false)
|
||||
deleteAlerts({ name: alertKey, systems: getSystemIds() })
|
||||
// when force deleting all alerts of a type, also remove them from initialAlertsState
|
||||
if (overwriteExisting) {
|
||||
for (const curAlerts of Object.values(initialAlertsState)) {
|
||||
curAlerts.delete(alertKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</label>
|
||||
{checked && (
|
||||
<div className="grid sm:grid-cols-2 mt-1.5 gap-5 px-4 pb-5 tabular-nums text-muted-foreground">
|
||||
<Suspense fallback={<div className="h-10" />}>
|
||||
{!singleDescription && (
|
||||
<div>
|
||||
<p id={`v${name}`} className="text-sm block h-8">
|
||||
<Trans>
|
||||
Average exceeds{" "}
|
||||
<strong className="text-foreground">
|
||||
{value}
|
||||
{alertData.unit}
|
||||
</strong>
|
||||
</Trans>
|
||||
</p>
|
||||
<div className="flex gap-3">
|
||||
<Slider
|
||||
aria-labelledby={`v${name}`}
|
||||
defaultValue={[value]}
|
||||
onValueCommit={(val) => sendUpsert(min, val[0])}
|
||||
onValueChange={(val) => setValue(val[0])}
|
||||
step={alertData.step ?? 1}
|
||||
min={alertData.min ?? 1}
|
||||
max={alertData.max ?? 99}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className={cn(singleDescription && "col-span-full lowercase")}>
|
||||
<p id={`t${name}`} className="text-sm block h-8 first-letter:uppercase">
|
||||
{singleDescription && (
|
||||
<>
|
||||
{singleDescription}
|
||||
{` `}
|
||||
</>
|
||||
)}
|
||||
<Trans>
|
||||
For <strong className="text-foreground">{min}</strong>{" "}
|
||||
<Plural value={min} one="minute" other="minutes" />
|
||||
</Trans>
|
||||
</p>
|
||||
<div className="flex gap-3">
|
||||
<Slider
|
||||
aria-labelledby={`v${name}`}
|
||||
defaultValue={[min]}
|
||||
onValueCommit={(minVal) => sendUpsert(minVal[0], value)}
|
||||
onValueChange={(val) => setMin(val[0])}
|
||||
min={1}
|
||||
max={60}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Suspense>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,311 +0,0 @@
|
||||
import { t } from "@lingui/core/macro"
|
||||
import { Trans, Plural } from "@lingui/react/macro"
|
||||
import { $alerts, $systems, pb } from "@/lib/stores"
|
||||
import { alertInfo, cn } from "@/lib/utils"
|
||||
import { Switch } from "@/components/ui/switch"
|
||||
import { AlertInfo, AlertRecord, SystemRecord } from "@/types"
|
||||
import { lazy, Suspense, useMemo, useState } from "react"
|
||||
import { toast } from "../ui/use-toast"
|
||||
import { BatchService } from "pocketbase"
|
||||
import { getSemaphore } from "@henrygd/semaphore"
|
||||
|
||||
interface AlertData {
|
||||
checked?: boolean
|
||||
val?: number
|
||||
min?: number
|
||||
updateAlert?: (checked: boolean, value: number, min: number) => void
|
||||
name: keyof typeof alertInfo
|
||||
alert: AlertInfo
|
||||
system: SystemRecord
|
||||
}
|
||||
|
||||
const Slider = lazy(() => import("@/components/ui/slider"))
|
||||
|
||||
const failedUpdateToast = () =>
|
||||
toast({
|
||||
title: t`Failed to update alert`,
|
||||
description: t`Please check logs for more details.`,
|
||||
variant: "destructive",
|
||||
})
|
||||
|
||||
export function SystemAlert({
|
||||
system,
|
||||
systemAlerts,
|
||||
data,
|
||||
}: {
|
||||
system: SystemRecord
|
||||
systemAlerts: AlertRecord[]
|
||||
data: AlertData
|
||||
}) {
|
||||
const alert = systemAlerts.find((alert) => alert.name === data.name)
|
||||
|
||||
data.updateAlert = async (checked: boolean, value: number, min: number) => {
|
||||
try {
|
||||
if (alert && !checked) {
|
||||
await pb.collection("alerts").delete(alert.id)
|
||||
} else if (alert && checked) {
|
||||
await pb.collection("alerts").update(alert.id, { value, min, triggered: false })
|
||||
} else if (checked) {
|
||||
pb.collection("alerts").create({
|
||||
system: system.id,
|
||||
user: pb.authStore.record!.id,
|
||||
name: data.name,
|
||||
value: value,
|
||||
min: min,
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
failedUpdateToast()
|
||||
}
|
||||
}
|
||||
|
||||
if (alert) {
|
||||
data.checked = true
|
||||
data.val = alert.value
|
||||
data.min = alert.min || 1
|
||||
}
|
||||
|
||||
return <AlertContent data={data} />
|
||||
}
|
||||
|
||||
export const SystemAlertGlobal = ({ data, overwrite }: { data: AlertData; overwrite: boolean | "indeterminate" }) => {
|
||||
data.checked = false
|
||||
data.val = data.min = 0
|
||||
|
||||
// set of system ids that have an alert for this name when the component is mounted
|
||||
const existingAlertsSystems = useMemo(() => {
|
||||
const map = new Set<string>()
|
||||
const alerts = $alerts.get()
|
||||
for (const alert of alerts) {
|
||||
if (alert.name === data.name) {
|
||||
map.add(alert.system)
|
||||
}
|
||||
}
|
||||
return map
|
||||
}, [])
|
||||
|
||||
data.updateAlert = async (checked: boolean, value: number, min: number) => {
|
||||
const sem = getSemaphore("alerts")
|
||||
await sem.acquire()
|
||||
try {
|
||||
// if another update is waiting behind, don't start this one
|
||||
if (sem.size() > 1) {
|
||||
return
|
||||
}
|
||||
|
||||
const recordData: Partial<AlertRecord> = {
|
||||
value,
|
||||
min,
|
||||
triggered: false,
|
||||
}
|
||||
|
||||
const batch = batchWrapper("alerts", 25)
|
||||
const systems = $systems.get()
|
||||
const currentAlerts = $alerts.get()
|
||||
|
||||
// map of current alerts with this name right now by system id
|
||||
const currentAlertsSystems = new Map<string, AlertRecord>()
|
||||
for (const alert of currentAlerts) {
|
||||
if (alert.name === data.name) {
|
||||
currentAlertsSystems.set(alert.system, alert)
|
||||
}
|
||||
}
|
||||
|
||||
if (overwrite) {
|
||||
existingAlertsSystems.clear()
|
||||
}
|
||||
|
||||
const processSystem = async (system: SystemRecord): Promise<void> => {
|
||||
const existingAlert = existingAlertsSystems.has(system.id)
|
||||
|
||||
if (!overwrite && existingAlert) {
|
||||
return
|
||||
}
|
||||
|
||||
const currentAlert = currentAlertsSystems.get(system.id)
|
||||
|
||||
// delete existing alert if unchecked
|
||||
if (!checked && currentAlert) {
|
||||
return batch.remove(currentAlert.id)
|
||||
}
|
||||
if (checked && currentAlert) {
|
||||
// update existing alert if checked
|
||||
return batch.update(currentAlert.id, recordData)
|
||||
}
|
||||
if (checked) {
|
||||
// create new alert if checked and not existing
|
||||
return batch.create({
|
||||
system: system.id,
|
||||
user: pb.authStore.record!.id,
|
||||
name: data.name,
|
||||
...recordData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// make sure current system is updated in the first batch
|
||||
await processSystem(data.system)
|
||||
for (const system of systems) {
|
||||
if (system.id === data.system.id) {
|
||||
continue
|
||||
}
|
||||
if (sem.size() > 1) {
|
||||
return
|
||||
}
|
||||
await processSystem(system)
|
||||
}
|
||||
await batch.send()
|
||||
} finally {
|
||||
sem.release()
|
||||
}
|
||||
}
|
||||
|
||||
return <AlertContent data={data} />
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a wrapper for performing batch operations on a specified collection.
|
||||
*/
|
||||
function batchWrapper(collection: string, batchSize: number) {
|
||||
let batch: BatchService | undefined
|
||||
let count = 0
|
||||
|
||||
const create = async <T extends Record<string, any>>(options: T) => {
|
||||
batch ||= pb.createBatch()
|
||||
batch.collection(collection).create(options)
|
||||
if (++count >= batchSize) {
|
||||
await send()
|
||||
}
|
||||
}
|
||||
|
||||
const update = async <T extends Record<string, any>>(id: string, data: T) => {
|
||||
batch ||= pb.createBatch()
|
||||
batch.collection(collection).update(id, data)
|
||||
if (++count >= batchSize) {
|
||||
await send()
|
||||
}
|
||||
}
|
||||
|
||||
const remove = async (id: string) => {
|
||||
batch ||= pb.createBatch()
|
||||
batch.collection(collection).delete(id)
|
||||
if (++count >= batchSize) {
|
||||
await send()
|
||||
}
|
||||
}
|
||||
|
||||
const send = async () => {
|
||||
if (count) {
|
||||
await batch?.send({ requestKey: null })
|
||||
batch = undefined
|
||||
count = 0
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
update,
|
||||
remove,
|
||||
send,
|
||||
create,
|
||||
}
|
||||
}
|
||||
|
||||
function AlertContent({ data }: { data: AlertData }) {
|
||||
const { name } = data
|
||||
|
||||
const singleDescription = data.alert.singleDesc?.()
|
||||
|
||||
const [checked, setChecked] = useState(data.checked || false)
|
||||
const [min, setMin] = useState(data.min || 10)
|
||||
const [value, setValue] = useState(data.val || (singleDescription ? 0 : data.alert.start ?? 80))
|
||||
|
||||
const Icon = alertInfo[name].icon
|
||||
|
||||
return (
|
||||
<div className="rounded-lg border border-muted-foreground/15 hover:border-muted-foreground/20 transition-colors duration-100 group">
|
||||
<label
|
||||
htmlFor={`s${name}`}
|
||||
className={cn("flex flex-row items-center justify-between gap-4 cursor-pointer p-4", {
|
||||
"pb-0": checked,
|
||||
})}
|
||||
>
|
||||
<div className="grid gap-1 select-none">
|
||||
<p className="font-semibold flex gap-3 items-center">
|
||||
<Icon className="h-4 w-4 opacity-85" /> {data.alert.name()}
|
||||
</p>
|
||||
{!checked && <span className="block text-sm text-muted-foreground">{data.alert.desc()}</span>}
|
||||
</div>
|
||||
<Switch
|
||||
id={`s${name}`}
|
||||
checked={checked}
|
||||
onCheckedChange={(newChecked) => {
|
||||
setChecked(newChecked)
|
||||
data.updateAlert?.(newChecked, value, min)
|
||||
}}
|
||||
/>
|
||||
</label>
|
||||
{checked && (
|
||||
<div className="grid sm:grid-cols-2 mt-1.5 gap-5 px-4 pb-5 tabular-nums text-muted-foreground">
|
||||
<Suspense fallback={<div className="h-10" />}>
|
||||
{!singleDescription && (
|
||||
<div>
|
||||
<p id={`v${name}`} className="text-sm block h-8">
|
||||
<Trans>
|
||||
Average exceeds{" "}
|
||||
<strong className="text-foreground">
|
||||
{value}
|
||||
{data.alert.unit}
|
||||
</strong>
|
||||
</Trans>
|
||||
</p>
|
||||
<div className="flex gap-3">
|
||||
<Slider
|
||||
aria-labelledby={`v${name}`}
|
||||
defaultValue={[value]}
|
||||
onValueCommit={(val) => {
|
||||
data.updateAlert?.(true, val[0], min)
|
||||
}}
|
||||
onValueChange={(val) => {
|
||||
setValue(val[0])
|
||||
}}
|
||||
step={data.alert.step ?? 1}
|
||||
min={data.alert.min ?? 1}
|
||||
max={alertInfo[name].max ?? 99}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className={cn(singleDescription && "col-span-full lowercase")}>
|
||||
<p id={`t${name}`} className="text-sm block h-8 first-letter:uppercase">
|
||||
{singleDescription && (
|
||||
<>
|
||||
{singleDescription}
|
||||
{` `}
|
||||
</>
|
||||
)}
|
||||
<Trans>
|
||||
For <strong className="text-foreground">{min}</strong>{" "}
|
||||
<Plural value={min} one="minute" other="minutes" />
|
||||
</Trans>
|
||||
</p>
|
||||
<div className="flex gap-3">
|
||||
<Slider
|
||||
aria-labelledby={`v${name}`}
|
||||
defaultValue={[min]}
|
||||
onValueCommit={(min) => {
|
||||
data.updateAlert?.(true, value, min[0])
|
||||
}}
|
||||
onValueChange={(val) => {
|
||||
setMin(val[0])
|
||||
}}
|
||||
min={1}
|
||||
max={60}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Suspense>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,128 +1,91 @@
|
||||
import { t } from "@lingui/core/macro"
|
||||
|
||||
import { Area, AreaChart, CartesianGrid, YAxis } from "recharts"
|
||||
import { ChartContainer, ChartTooltip, ChartTooltipContent, xAxis } from "@/components/ui/chart"
|
||||
import { useYAxisWidth, cn, formatShortDate, chartMargin } from "@/lib/utils"
|
||||
// import Spinner from '../spinner'
|
||||
import { ChartData } from "@/types"
|
||||
import { memo, useMemo } from "react"
|
||||
import { useLingui } from "@lingui/react/macro"
|
||||
import { ChartData, SystemStatsRecord } from "@/types"
|
||||
import { useMemo } from "react"
|
||||
|
||||
/** [label, key, color, opacity] */
|
||||
type DataKeys = [string, string, number, number]
|
||||
|
||||
const getNestedValue = (path: string, max = false, data: any): number | null => {
|
||||
// fallback value (obj?.stats?.cpum ? 0 : null) should only come into play when viewing
|
||||
// a max value which doesn't exist, or the value was zero and omitted from the stats object.
|
||||
// so we check if cpum is present. if so, return 0 to make sure the zero value is displayed.
|
||||
// if not, return null - there is no max data so do not display anything.
|
||||
return `stats.${path}${max ? "m" : ""}`
|
||||
.split(".")
|
||||
.reduce((acc: any, key: string) => acc?.[key] ?? (data.stats?.cpum ? 0 : null), data)
|
||||
export type DataPoint = {
|
||||
label: string
|
||||
dataKey: (data: SystemStatsRecord) => number | undefined
|
||||
color: string
|
||||
opacity: number
|
||||
}
|
||||
|
||||
export default memo(function AreaChartDefault({
|
||||
maxToggled = false,
|
||||
chartName,
|
||||
export default function AreaChartDefault({
|
||||
chartData,
|
||||
max,
|
||||
maxToggled,
|
||||
tickFormatter,
|
||||
contentFormatter,
|
||||
}: {
|
||||
maxToggled?: boolean
|
||||
chartName: string
|
||||
dataPoints,
|
||||
}: // logRender = false,
|
||||
{
|
||||
chartData: ChartData
|
||||
max?: number
|
||||
tickFormatter: (value: number) => string
|
||||
contentFormatter: ({ value }: { value: number }) => string
|
||||
maxToggled?: boolean
|
||||
tickFormatter: (value: number, index: number) => string
|
||||
contentFormatter: ({ value, payload }: { value: number; payload: SystemStatsRecord }) => string
|
||||
dataPoints?: DataPoint[]
|
||||
// logRender?: boolean
|
||||
}) {
|
||||
const { yAxisWidth, updateYAxisWidth } = useYAxisWidth()
|
||||
const { i18n } = useLingui()
|
||||
|
||||
const { chartTime } = chartData
|
||||
|
||||
const showMax = chartTime !== "1h" && maxToggled
|
||||
|
||||
const dataKeys: DataKeys[] = useMemo(() => {
|
||||
// [label, key, color, opacity]
|
||||
if (chartName === "CPU Usage") {
|
||||
return [[t`CPU Usage`, "cpu", 1, 0.4]]
|
||||
} else if (chartName === "dio") {
|
||||
return [
|
||||
[t({ message: "Write", comment: "Disk write" }), "dw", 3, 0.3],
|
||||
[t({ message: "Read", comment: "Disk read" }), "dr", 1, 0.3],
|
||||
]
|
||||
} else if (chartName === "bw") {
|
||||
return [
|
||||
[t({ message: "Sent", comment: "Network bytes sent (upload)" }), "ns", 5, 0.2],
|
||||
[t({ message: "Received", comment: "Network bytes received (download)" }), "nr", 2, 0.2],
|
||||
]
|
||||
} else if (chartName.startsWith("efs")) {
|
||||
return [
|
||||
[t`Write`, `${chartName}.w`, 3, 0.3],
|
||||
[t`Read`, `${chartName}.r`, 1, 0.3],
|
||||
]
|
||||
} else if (chartName.startsWith("g.")) {
|
||||
return [chartName.includes("mu") ? [t`Used`, chartName, 2, 0.25] : [t`Usage`, chartName, 1, 0.4]]
|
||||
return useMemo(() => {
|
||||
if (chartData.systemStats.length === 0) {
|
||||
return null
|
||||
}
|
||||
return []
|
||||
}, [chartName, i18n.locale])
|
||||
|
||||
// console.log('Rendered at', new Date())
|
||||
|
||||
if (chartData.systemStats.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<ChartContainer
|
||||
className={cn("h-full w-full absolute aspect-auto bg-card opacity-0 transition-opacity", {
|
||||
"opacity-100": yAxisWidth,
|
||||
})}
|
||||
>
|
||||
<AreaChart accessibilityLayer data={chartData.systemStats} margin={chartMargin}>
|
||||
<CartesianGrid vertical={false} />
|
||||
<YAxis
|
||||
direction="ltr"
|
||||
orientation={chartData.orientation}
|
||||
className="tracking-tighter"
|
||||
width={yAxisWidth}
|
||||
domain={[0, max ?? "auto"]}
|
||||
tickFormatter={(value) => updateYAxisWidth(tickFormatter(value))}
|
||||
tickLine={false}
|
||||
axisLine={false}
|
||||
/>
|
||||
{xAxis(chartData)}
|
||||
<ChartTooltip
|
||||
animationEasing="ease-out"
|
||||
animationDuration={150}
|
||||
content={
|
||||
<ChartTooltipContent
|
||||
labelFormatter={(_, data) => formatShortDate(data[0].payload.created)}
|
||||
contentFormatter={contentFormatter}
|
||||
// indicator="line"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
{dataKeys.map((key, i) => {
|
||||
const color = `hsl(var(--chart-${key[2]}))`
|
||||
return (
|
||||
<Area
|
||||
key={i}
|
||||
dataKey={getNestedValue.bind(null, key[1], showMax)}
|
||||
name={key[0]}
|
||||
type="monotoneX"
|
||||
fill={color}
|
||||
fillOpacity={key[3]}
|
||||
stroke={color}
|
||||
isAnimationActive={false}
|
||||
/>
|
||||
)
|
||||
// if (logRender) {
|
||||
// console.log("Rendered at", new Date())
|
||||
// }
|
||||
return (
|
||||
<div>
|
||||
<ChartContainer
|
||||
className={cn("h-full w-full absolute aspect-auto bg-card opacity-0 transition-opacity", {
|
||||
"opacity-100": yAxisWidth,
|
||||
})}
|
||||
{/* <ChartLegend content={<ChartLegendContent />} /> */}
|
||||
</AreaChart>
|
||||
</ChartContainer>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
>
|
||||
<AreaChart accessibilityLayer data={chartData.systemStats} margin={chartMargin}>
|
||||
<CartesianGrid vertical={false} />
|
||||
<YAxis
|
||||
direction="ltr"
|
||||
orientation={chartData.orientation}
|
||||
className="tracking-tighter"
|
||||
width={yAxisWidth}
|
||||
domain={[0, max ?? "auto"]}
|
||||
tickFormatter={(value, index) => updateYAxisWidth(tickFormatter(value, index))}
|
||||
tickLine={false}
|
||||
axisLine={false}
|
||||
/>
|
||||
{xAxis(chartData)}
|
||||
<ChartTooltip
|
||||
animationEasing="ease-out"
|
||||
animationDuration={150}
|
||||
content={
|
||||
<ChartTooltipContent
|
||||
labelFormatter={(_, data) => formatShortDate(data[0].payload.created)}
|
||||
contentFormatter={contentFormatter}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
{dataPoints?.map((dataPoint, i) => {
|
||||
const color = `hsl(var(--chart-${dataPoint.color}))`
|
||||
return (
|
||||
<Area
|
||||
key={i}
|
||||
dataKey={dataPoint.dataKey}
|
||||
name={dataPoint.label}
|
||||
type="monotoneX"
|
||||
fill={color}
|
||||
fillOpacity={dataPoint.opacity}
|
||||
stroke={color}
|
||||
isAnimationActive={false}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
{/* <ChartLegend content={<ChartLegendContent />} /> */}
|
||||
</AreaChart>
|
||||
</ChartContainer>
|
||||
</div>
|
||||
)
|
||||
}, [chartData.systemStats.at(-1), yAxisWidth, maxToggled])
|
||||
}
|
||||
|
||||
@@ -29,41 +29,36 @@ export default memo(function ContainerChart({
|
||||
const isNetChart = chartType === ChartType.Network
|
||||
|
||||
const chartConfig = useMemo(() => {
|
||||
let config = {} as Record<
|
||||
string,
|
||||
{
|
||||
label: string
|
||||
color: string
|
||||
}
|
||||
>
|
||||
const totalUsage = {} as Record<string, number>
|
||||
for (let stats of containerData) {
|
||||
for (let key in stats) {
|
||||
if (!key || key === "created") {
|
||||
continue
|
||||
}
|
||||
if (!(key in totalUsage)) {
|
||||
totalUsage[key] = 0
|
||||
}
|
||||
if (isNetChart) {
|
||||
totalUsage[key] += (stats[key]?.nr ?? 0) + (stats[key]?.ns ?? 0)
|
||||
} else {
|
||||
// @ts-ignore
|
||||
totalUsage[key] += stats[key]?.[dataKey] ?? 0
|
||||
}
|
||||
const config = {} as Record<string, { label: string; color: string }>
|
||||
const totalUsage = new Map<string, number>()
|
||||
|
||||
// calculate total usage of each container
|
||||
for (const stats of containerData) {
|
||||
for (const key in stats) {
|
||||
if (!key || key === "created") continue
|
||||
|
||||
const currentTotal = totalUsage.get(key) ?? 0
|
||||
const increment = isNetChart
|
||||
? (stats[key]?.nr ?? 0) + (stats[key]?.ns ?? 0)
|
||||
: // @ts-ignore
|
||||
stats[key]?.[dataKey] ?? 0
|
||||
|
||||
totalUsage.set(key, currentTotal + increment)
|
||||
}
|
||||
}
|
||||
let keys = Object.keys(totalUsage)
|
||||
keys.sort((a, b) => (totalUsage[a] > totalUsage[b] ? -1 : 1))
|
||||
const length = keys.length
|
||||
for (let i = 0; i < length; i++) {
|
||||
const key = keys[i]
|
||||
|
||||
// Sort keys and generate colors based on usage
|
||||
const sortedEntries = Array.from(totalUsage.entries()).sort(([, a], [, b]) => b - a)
|
||||
|
||||
const length = sortedEntries.length
|
||||
sortedEntries.forEach(([key], i) => {
|
||||
const hue = ((i * 360) / length) % 360
|
||||
config[key] = {
|
||||
label: key,
|
||||
color: `hsl(${hue}, 60%, 55%)`,
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return config satisfies ChartConfig
|
||||
}, [chartData])
|
||||
|
||||
@@ -124,6 +119,8 @@ export default memo(function ContainerChart({
|
||||
return obj
|
||||
}, [])
|
||||
|
||||
const filterLower = filter?.toLowerCase()
|
||||
|
||||
// console.log('rendered at', new Date())
|
||||
|
||||
if (containerData.length === 0) {
|
||||
@@ -165,7 +162,7 @@ export default memo(function ContainerChart({
|
||||
content={<ChartTooltipContent filter={filter} contentFormatter={toolTipFormatter} />}
|
||||
/>
|
||||
{Object.keys(chartConfig).map((key) => {
|
||||
const filtered = filter && !key.toLowerCase().includes(filter.toLowerCase())
|
||||
const filtered = filterLower && !key.toLowerCase().includes(filterLower)
|
||||
let fillOpacity = filtered ? 0.05 : 0.4
|
||||
let strokeOpacity = filtered ? 0.1 : 1
|
||||
return (
|
||||
|
||||
@@ -8,65 +8,31 @@ import {
|
||||
ChartTooltipContent,
|
||||
xAxis,
|
||||
} from "@/components/ui/chart"
|
||||
import {
|
||||
useYAxisWidth,
|
||||
cn,
|
||||
formatShortDate,
|
||||
toFixedWithoutTrailingZeros,
|
||||
decimalString,
|
||||
chartMargin,
|
||||
} from "@/lib/utils"
|
||||
import { ChartData } from "@/types"
|
||||
import { memo, useMemo } from "react"
|
||||
import { useYAxisWidth, cn, formatShortDate, toFixedFloat, decimalString, chartMargin } from "@/lib/utils"
|
||||
import { ChartData, SystemStats } from "@/types"
|
||||
import { memo } from "react"
|
||||
import { t } from "@lingui/core/macro"
|
||||
|
||||
export default memo(function LoadAverageChart({ chartData }: { chartData: ChartData }) {
|
||||
const { yAxisWidth, updateYAxisWidth } = useYAxisWidth()
|
||||
|
||||
if (chartData.systemStats.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
/** Format load average data for chart */
|
||||
const newChartData = useMemo(() => {
|
||||
const newChartData = { data: [], colors: {} } as {
|
||||
data: Record<string, number | string>[]
|
||||
colors: Record<string, string>
|
||||
}
|
||||
|
||||
// Define colors for the three load average lines
|
||||
const colors = {
|
||||
"1m": "hsl(25, 95%, 53%)", // Orange for 1-minute
|
||||
"5m": "hsl(217, 91%, 60%)", // Blue for 5-minute
|
||||
"15m": "hsl(271, 81%, 56%)", // Purple for 15-minute
|
||||
}
|
||||
|
||||
for (let data of chartData.systemStats) {
|
||||
let newData = { created: data.created } as Record<string, number | string>
|
||||
|
||||
// Add load average values if they exist and stats is not null
|
||||
if (data.stats && data.stats.l1 !== undefined) {
|
||||
newData["1m"] = data.stats.l1
|
||||
}
|
||||
if (data.stats && data.stats.l5 !== undefined) {
|
||||
newData["5m"] = data.stats.l5
|
||||
}
|
||||
if (data.stats && data.stats.l15 !== undefined) {
|
||||
newData["15m"] = data.stats.l15
|
||||
}
|
||||
|
||||
newChartData.data.push(newData)
|
||||
}
|
||||
|
||||
newChartData.colors = colors
|
||||
return newChartData
|
||||
}, [chartData])
|
||||
|
||||
const loadKeys = ["1m", "5m", "15m"].filter(key =>
|
||||
newChartData.data.some(data => data[key] !== undefined)
|
||||
)
|
||||
|
||||
// console.log('rendered at', new Date())
|
||||
const keys: { legacy: keyof SystemStats; color: string; label: string }[] = [
|
||||
{
|
||||
legacy: "l1",
|
||||
color: "hsl(271, 81%, 60%)", // Purple
|
||||
label: t({ message: `1 min`, comment: "Load average" }),
|
||||
},
|
||||
{
|
||||
legacy: "l5",
|
||||
color: "hsl(217, 91%, 60%)", // Blue
|
||||
label: t({ message: `5 min`, comment: "Load average" }),
|
||||
},
|
||||
{
|
||||
legacy: "l15",
|
||||
color: "hsl(25, 95%, 53%)", // Orange
|
||||
label: t({ message: `15 min`, comment: "Load average" }),
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -75,7 +41,7 @@ export default memo(function LoadAverageChart({ chartData }: { chartData: ChartD
|
||||
"opacity-100": yAxisWidth,
|
||||
})}
|
||||
>
|
||||
<LineChart accessibilityLayer data={newChartData.data} margin={chartMargin}>
|
||||
<LineChart accessibilityLayer data={chartData.systemStats} margin={chartMargin}>
|
||||
<CartesianGrid vertical={false} />
|
||||
<YAxis
|
||||
direction="ltr"
|
||||
@@ -84,8 +50,7 @@ export default memo(function LoadAverageChart({ chartData }: { chartData: ChartD
|
||||
domain={[0, "auto"]}
|
||||
width={yAxisWidth}
|
||||
tickFormatter={(value) => {
|
||||
const val = toFixedWithoutTrailingZeros(value, 2)
|
||||
return updateYAxisWidth(val)
|
||||
return updateYAxisWidth(String(toFixedFloat(value, 2)))
|
||||
}}
|
||||
tickLine={false}
|
||||
axisLine={false}
|
||||
@@ -95,7 +60,7 @@ export default memo(function LoadAverageChart({ chartData }: { chartData: ChartD
|
||||
animationEasing="ease-out"
|
||||
animationDuration={150}
|
||||
// @ts-ignore
|
||||
itemSorter={(a, b) => b.value - a.value}
|
||||
// itemSorter={(a, b) => b.value - a.value}
|
||||
content={
|
||||
<ChartTooltipContent
|
||||
labelFormatter={(_, data) => formatShortDate(data[0].payload.created)}
|
||||
@@ -103,21 +68,29 @@ export default memo(function LoadAverageChart({ chartData }: { chartData: ChartD
|
||||
/>
|
||||
}
|
||||
/>
|
||||
{loadKeys.map((key) => (
|
||||
<Line
|
||||
key={key}
|
||||
dataKey={key}
|
||||
name={key === "1m" ? t`1 min` : key === "5m" ? t`5 min` : t`15 min`}
|
||||
type="monotoneX"
|
||||
dot={false}
|
||||
strokeWidth={1.5}
|
||||
stroke={newChartData.colors[key]}
|
||||
isAnimationActive={false}
|
||||
/>
|
||||
))}
|
||||
{keys.map(({ legacy, color, label }, i) => {
|
||||
const dataKey = (value: { stats: SystemStats }) => {
|
||||
if (chartData.agentVersion.patch < 1) {
|
||||
return value.stats?.[legacy]
|
||||
}
|
||||
return value.stats?.la?.[i] ?? value.stats?.[legacy]
|
||||
}
|
||||
return (
|
||||
<Line
|
||||
key={i}
|
||||
dataKey={dataKey}
|
||||
name={label}
|
||||
type="monotoneX"
|
||||
dot={false}
|
||||
strokeWidth={1.5}
|
||||
stroke={color}
|
||||
isAnimationActive={false}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
<ChartLegend content={<ChartLegendContent />} />
|
||||
</LineChart>
|
||||
</ChartContainer>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {
|
||||
AlertOctagonIcon,
|
||||
BookIcon,
|
||||
DatabaseBackupIcon,
|
||||
FingerprintIcon,
|
||||
@@ -69,7 +70,7 @@ export default memo(function CommandPalette({ open, setOpen }: { open: boolean;
|
||||
setOpen(false)
|
||||
}}
|
||||
>
|
||||
<Server className="me-2 h-4 w-4" />
|
||||
<Server className="me-2 size-4" />
|
||||
<span>{system.name}</span>
|
||||
<CommandShortcut>{getHostDisplayValue(system)}</CommandShortcut>
|
||||
</CommandItem>
|
||||
@@ -86,7 +87,7 @@ export default memo(function CommandPalette({ open, setOpen }: { open: boolean;
|
||||
setOpen(false)
|
||||
}}
|
||||
>
|
||||
<LayoutDashboard className="me-2 h-4 w-4" />
|
||||
<LayoutDashboard className="me-2 size-4" />
|
||||
<span>
|
||||
<Trans>Dashboard</Trans>
|
||||
</span>
|
||||
@@ -100,7 +101,7 @@ export default memo(function CommandPalette({ open, setOpen }: { open: boolean;
|
||||
setOpen(false)
|
||||
}}
|
||||
>
|
||||
<SettingsIcon className="me-2 h-4 w-4" />
|
||||
<SettingsIcon className="me-2 size-4" />
|
||||
<span>
|
||||
<Trans>Settings</Trans>
|
||||
</span>
|
||||
@@ -113,7 +114,7 @@ export default memo(function CommandPalette({ open, setOpen }: { open: boolean;
|
||||
setOpen(false)
|
||||
}}
|
||||
>
|
||||
<MailIcon className="me-2 h-4 w-4" />
|
||||
<MailIcon className="me-2 size-4" />
|
||||
<span>
|
||||
<Trans>Notifications</Trans>
|
||||
</span>
|
||||
@@ -125,19 +126,31 @@ export default memo(function CommandPalette({ open, setOpen }: { open: boolean;
|
||||
setOpen(false)
|
||||
}}
|
||||
>
|
||||
<FingerprintIcon className="me-2 h-4 w-4" />
|
||||
<FingerprintIcon className="me-2 size-4" />
|
||||
<span>
|
||||
<Trans>Tokens & Fingerprints</Trans>
|
||||
</span>
|
||||
{SettingsShortcut}
|
||||
</CommandItem>
|
||||
<CommandItem
|
||||
onSelect={() => {
|
||||
navigate(getPagePath($router, "settings", { name: "alert-history" }))
|
||||
setOpen(false)
|
||||
}}
|
||||
>
|
||||
<AlertOctagonIcon className="me-2 size-4" />
|
||||
<span>
|
||||
<Trans>Alert History</Trans>
|
||||
</span>
|
||||
{SettingsShortcut}
|
||||
</CommandItem>
|
||||
<CommandItem
|
||||
keywords={["help", "oauth", "oidc"]}
|
||||
onSelect={() => {
|
||||
window.location.href = "https://beszel.dev/guide/what-is-beszel"
|
||||
}}
|
||||
>
|
||||
<BookIcon className="me-2 h-4 w-4" />
|
||||
<BookIcon className="me-2 size-4" />
|
||||
<span>
|
||||
<Trans>Documentation</Trans>
|
||||
</span>
|
||||
@@ -155,7 +168,7 @@ export default memo(function CommandPalette({ open, setOpen }: { open: boolean;
|
||||
window.open(prependBasePath("/_/"), "_blank")
|
||||
}}
|
||||
>
|
||||
<UsersIcon className="me-2 h-4 w-4" />
|
||||
<UsersIcon className="me-2 size-4" />
|
||||
<span>
|
||||
<Trans>Users</Trans>
|
||||
</span>
|
||||
@@ -167,7 +180,7 @@ export default memo(function CommandPalette({ open, setOpen }: { open: boolean;
|
||||
window.open(prependBasePath("/_/#/logs"), "_blank")
|
||||
}}
|
||||
>
|
||||
<LogsIcon className="me-2 h-4 w-4" />
|
||||
<LogsIcon className="me-2 size-4" />
|
||||
<span>
|
||||
<Trans>Logs</Trans>
|
||||
</span>
|
||||
@@ -179,7 +192,7 @@ export default memo(function CommandPalette({ open, setOpen }: { open: boolean;
|
||||
window.open(prependBasePath("/_/#/settings/backups"), "_blank")
|
||||
}}
|
||||
>
|
||||
<DatabaseBackupIcon className="me-2 h-4 w-4" />
|
||||
<DatabaseBackupIcon className="me-2 size-4" />
|
||||
<span>
|
||||
<Trans>Backups</Trans>
|
||||
</span>
|
||||
@@ -192,7 +205,7 @@ export default memo(function CommandPalette({ open, setOpen }: { open: boolean;
|
||||
window.open(prependBasePath("/_/#/settings/mail"), "_blank")
|
||||
}}
|
||||
>
|
||||
<MailIcon className="me-2 h-4 w-4" />
|
||||
<MailIcon className="me-2 size-4" />
|
||||
<span>
|
||||
<Trans>SMTP settings</Trans>
|
||||
</span>
|
||||
|
||||
@@ -3,8 +3,8 @@ import { DropdownMenuContent, DropdownMenuItem } from "./ui/dropdown-menu"
|
||||
import { copyToClipboard, getHubURL } from "@/lib/utils"
|
||||
import { i18n } from "@lingui/core"
|
||||
|
||||
const isBeta = BESZEL.HUB_VERSION.includes("beta")
|
||||
const imageTag = isBeta ? ":edge" : ""
|
||||
// const isbeta = beszel.hub_version.includes("beta")
|
||||
// const imagetag = isbeta ? ":edge" : ""
|
||||
|
||||
/**
|
||||
* Get the URL of the script to install the agent.
|
||||
@@ -12,18 +12,20 @@ const imageTag = isBeta ? ":edge" : ""
|
||||
* @returns The URL for the script.
|
||||
*/
|
||||
const getScriptUrl = (path: string = "") => {
|
||||
const url = new URL("https://get.beszel.dev")
|
||||
url.pathname = path
|
||||
if (isBeta) {
|
||||
url.searchParams.set("beta", "1")
|
||||
}
|
||||
return url.toString()
|
||||
return `https://get.beszel.dev${path}`
|
||||
// no beta for now
|
||||
// const url = new URL("https://get.beszel.dev")
|
||||
// url.pathname = path
|
||||
// if (isBeta) {
|
||||
// url.searchParams.set("beta", "1")
|
||||
// }
|
||||
// return url.toString()
|
||||
}
|
||||
|
||||
export function copyDockerCompose(port = "45876", publicKey: string, token: string) {
|
||||
copyToClipboard(`services:
|
||||
beszel-agent:
|
||||
image: henrygd/beszel-agent${imageTag}
|
||||
image: henrygd/beszel-agent
|
||||
container_name: beszel-agent
|
||||
restart: unless-stopped
|
||||
network_mode: host
|
||||
@@ -41,7 +43,7 @@ export function copyDockerCompose(port = "45876", publicKey: string, token: stri
|
||||
|
||||
export function copyDockerRun(port = "45876", publicKey: string, token: string) {
|
||||
copyToClipboard(
|
||||
`docker run -d --name beszel-agent --network host --restart unless-stopped -v /var/run/docker.sock:/var/run/docker.sock:ro -v ./beszel_agent_data:/var/lib/beszel-agent -e KEY="${publicKey}" -e LISTEN=${port} -e TOKEN="${token}" -e HUB_URL="${getHubURL()}" henrygd/beszel-agent${imageTag}`
|
||||
`docker run -d --name beszel-agent --network host --restart unless-stopped -v /var/run/docker.sock:/var/run/docker.sock:ro -v ./beszel_agent_data:/var/lib/beszel-agent -e KEY="${publicKey}" -e LISTEN=${port} -e TOKEN="${token}" -e HUB_URL="${getHubURL()}" henrygd/beszel-agent`
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { t } from "@lingui/core/macro";
|
||||
import { Trans } from "@lingui/react/macro";
|
||||
import { t } from "@lingui/core/macro"
|
||||
import { Trans } from "@lingui/react/macro"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { buttonVariants } from "@/components/ui/button"
|
||||
import { Input } from "@/components/ui/input"
|
||||
@@ -43,6 +43,14 @@ const showLoginFaliedToast = () => {
|
||||
})
|
||||
}
|
||||
|
||||
const getAuthProviderIcon = (provider: AuthProviderInfo) => {
|
||||
let { name } = provider
|
||||
if (name.startsWith("oidc")) {
|
||||
name = "oidc"
|
||||
}
|
||||
return prependBasePath(`/_/images/oauth2/${name}.svg`)
|
||||
}
|
||||
|
||||
export function UserAuthForm({
|
||||
className,
|
||||
isFirstRun,
|
||||
@@ -165,8 +173,8 @@ export function UserAuthForm({
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className={cn("grid gap-6", className)} {...props}>
|
||||
{passwordEnabled && (
|
||||
<div className={cn("grid gap-6", className)} {...props}>
|
||||
{passwordEnabled && (
|
||||
<>
|
||||
<form onSubmit={handleSubmit} onChange={() => setErrors({})}>
|
||||
<div className="grid gap-2.5">
|
||||
@@ -242,20 +250,20 @@ export function UserAuthForm({
|
||||
</form>
|
||||
{(isFirstRun || oauthEnabled) && (
|
||||
// only show 'continue with' during onboarding or if we have auth providers
|
||||
(<div className="relative">
|
||||
<div className="absolute inset-0 flex items-center">
|
||||
<div className="relative">
|
||||
<div className="absolute inset-0 flex items-center">
|
||||
<span className="w-full border-t" />
|
||||
</div>
|
||||
<div className="relative flex justify-center text-xs uppercase">
|
||||
<div className="relative flex justify-center text-xs uppercase">
|
||||
<span className="bg-background px-2 text-muted-foreground">
|
||||
<Trans>Or continue with</Trans>
|
||||
</span>
|
||||
</div>
|
||||
</div>)
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{oauthEnabled && (
|
||||
{oauthEnabled && (
|
||||
<div className="grid gap-2 -mt-1">
|
||||
{authMethods.oauth2.providers.map((provider) => (
|
||||
<button
|
||||
@@ -273,7 +281,7 @@ export function UserAuthForm({
|
||||
) : (
|
||||
<img
|
||||
className="me-2 h-4 w-4 dark:brightness-0 dark:invert"
|
||||
src={prependBasePath(`/_/images/oauth2/${provider.name}.svg`)}
|
||||
src={getAuthProviderIcon(provider)}
|
||||
alt=""
|
||||
// onError={(e) => {
|
||||
// e.currentTarget.src = "/static/lock.svg"
|
||||
@@ -285,16 +293,16 @@ export function UserAuthForm({
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{!oauthEnabled && isFirstRun && (
|
||||
{!oauthEnabled && isFirstRun && (
|
||||
// only show GitHub button / dialog during onboarding
|
||||
(<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<button type="button" className={cn(buttonVariants({ variant: "outline" }))}>
|
||||
<img className="me-2 h-4 w-4 dark:invert" src={prependBasePath("/_/images/oauth2/github.svg")} alt="" />
|
||||
<span className="translate-y-[1px]">GitHub</span>
|
||||
</button>
|
||||
</DialogTrigger>
|
||||
<DialogContent style={{ maxWidth: 440, width: "90%" }}>
|
||||
<DialogContent style={{ maxWidth: 440, width: "90%" }}>
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
<Trans>OAuth 2 / OIDC support</Trans>
|
||||
@@ -318,9 +326,9 @@ export function UserAuthForm({
|
||||
</p>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>)
|
||||
</Dialog>
|
||||
)}
|
||||
{passwordEnabled && !isFirstRun && (
|
||||
{passwordEnabled && !isFirstRun && (
|
||||
<Link
|
||||
href={getPagePath($router, "forgot_password")}
|
||||
className="text-sm mx-auto hover:text-brand underline underline-offset-4 opacity-70 hover:opacity-100 transition-opacity"
|
||||
@@ -328,6 +336,6 @@ export function UserAuthForm({
|
||||
<Trans>Forgot password?</Trans>
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { $alerts, $systems, pb } from "@/lib/stores"
|
||||
import { useStore } from "@nanostores/react"
|
||||
import { GithubIcon } from "lucide-react"
|
||||
import { Separator } from "../ui/separator"
|
||||
import { alertInfo, updateRecordList, updateSystemList } from "@/lib/utils"
|
||||
import { alertInfo, getSystemNameFromId, updateRecordList, updateSystemList } from "@/lib/utils"
|
||||
import { AlertRecord, SystemRecord } from "@/types"
|
||||
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"
|
||||
import { $router, Link } from "../router"
|
||||
@@ -14,24 +14,8 @@ import { getPagePath } from "@nanostores/router"
|
||||
const SystemsTable = lazy(() => import("../systems-table/systems-table"))
|
||||
|
||||
export const Home = memo(() => {
|
||||
const alerts = useStore($alerts)
|
||||
const systems = useStore($systems)
|
||||
const { t } = useLingui()
|
||||
|
||||
let alertsKey = ""
|
||||
const activeAlerts = useMemo(() => {
|
||||
const activeAlerts = alerts.filter((alert) => {
|
||||
const active = alert.triggered && alert.name in alertInfo
|
||||
if (!active) {
|
||||
return false
|
||||
}
|
||||
alert.sysname = systems.find((system) => system.id === alert.system)?.name
|
||||
alertsKey += alert.id
|
||||
return true
|
||||
})
|
||||
return activeAlerts
|
||||
}, [systems, alerts])
|
||||
|
||||
useEffect(() => {
|
||||
document.title = t`Dashboard` + " / Beszel"
|
||||
}, [t])
|
||||
@@ -44,20 +28,15 @@ export const Home = memo(() => {
|
||||
pb.collection<SystemRecord>("systems").subscribe("*", (e) => {
|
||||
updateRecordList(e, $systems)
|
||||
})
|
||||
pb.collection<AlertRecord>("alerts").subscribe("*", (e) => {
|
||||
updateRecordList(e, $alerts)
|
||||
})
|
||||
return () => {
|
||||
pb.collection("systems").unsubscribe("*")
|
||||
// pb.collection('alerts').unsubscribe('*')
|
||||
}
|
||||
}, [])
|
||||
|
||||
return useMemo(
|
||||
() => (
|
||||
<>
|
||||
{/* show active alerts */}
|
||||
{activeAlerts.length > 0 && <ActiveAlerts key={activeAlerts.length} activeAlerts={activeAlerts} />}
|
||||
<ActiveAlerts />
|
||||
<Suspense>
|
||||
<SystemsTable />
|
||||
</Suspense>
|
||||
@@ -81,51 +60,79 @@ export const Home = memo(() => {
|
||||
</div>
|
||||
</>
|
||||
),
|
||||
[alertsKey]
|
||||
[]
|
||||
)
|
||||
})
|
||||
|
||||
const ActiveAlerts = memo(({ activeAlerts }: { activeAlerts: AlertRecord[] }) => {
|
||||
return (
|
||||
<Card className="mb-4">
|
||||
<CardHeader className="pb-4 px-2 sm:px-6 max-sm:pt-5 max-sm:pb-1">
|
||||
<div className="px-2 sm:px-1">
|
||||
<CardTitle>
|
||||
<Trans>Active Alerts</Trans>
|
||||
</CardTitle>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="max-sm:p-2">
|
||||
{activeAlerts.length > 0 && (
|
||||
<div className="grid sm:grid-cols-2 lg:grid-cols-3 2xl:grid-cols-4 gap-3">
|
||||
{activeAlerts.map((alert) => {
|
||||
const info = alertInfo[alert.name as keyof typeof alertInfo]
|
||||
return (
|
||||
<Alert
|
||||
key={alert.id}
|
||||
className="hover:-translate-y-[1px] duration-200 bg-transparent border-foreground/10 hover:shadow-md shadow-black"
|
||||
>
|
||||
<info.icon className="h-4 w-4" />
|
||||
<AlertTitle>
|
||||
{alert.sysname} {info.name().toLowerCase().replace("cpu", "CPU")}
|
||||
</AlertTitle>
|
||||
<AlertDescription>
|
||||
<Trans>
|
||||
Exceeds {alert.value}
|
||||
{info.unit} in last <Plural value={alert.min} one="# minute" other="# minutes" />
|
||||
</Trans>
|
||||
</AlertDescription>
|
||||
<Link
|
||||
href={getPagePath($router, "system", { name: alert.sysname! })}
|
||||
className="absolute inset-0 w-full h-full"
|
||||
aria-label="View system"
|
||||
></Link>
|
||||
</Alert>
|
||||
)
|
||||
})}
|
||||
const ActiveAlerts = () => {
|
||||
const alerts = useStore($alerts)
|
||||
|
||||
const { activeAlerts, alertsKey } = useMemo(() => {
|
||||
const activeAlerts: AlertRecord[] = []
|
||||
// key to prevent re-rendering if alerts change but active alerts didn't
|
||||
const alertsKey: string[] = []
|
||||
|
||||
for (const systemId of Object.keys(alerts)) {
|
||||
for (const alert of alerts[systemId].values()) {
|
||||
if (alert.triggered && alert.name in alertInfo) {
|
||||
activeAlerts.push(alert)
|
||||
alertsKey.push(`${alert.system}${alert.value}${alert.min}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { activeAlerts, alertsKey }
|
||||
}, [alerts])
|
||||
|
||||
return useMemo(() => {
|
||||
if (activeAlerts.length === 0) {
|
||||
return null
|
||||
}
|
||||
return (
|
||||
<Card className="mb-4">
|
||||
<CardHeader className="pb-4 px-2 sm:px-6 max-sm:pt-5 max-sm:pb-1">
|
||||
<div className="px-2 sm:px-1">
|
||||
<CardTitle>
|
||||
<Trans>Active Alerts</Trans>
|
||||
</CardTitle>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
})
|
||||
</CardHeader>
|
||||
<CardContent className="max-sm:p-2">
|
||||
{activeAlerts.length > 0 && (
|
||||
<div className="grid sm:grid-cols-2 lg:grid-cols-3 2xl:grid-cols-4 gap-3">
|
||||
{activeAlerts.map((alert) => {
|
||||
const info = alertInfo[alert.name as keyof typeof alertInfo]
|
||||
return (
|
||||
<Alert
|
||||
key={alert.id}
|
||||
className="hover:-translate-y-[1px] duration-200 bg-transparent border-foreground/10 hover:shadow-md shadow-black"
|
||||
>
|
||||
<info.icon className="h-4 w-4" />
|
||||
<AlertTitle>
|
||||
{getSystemNameFromId(alert.system)} {info.name().toLowerCase().replace("cpu", "CPU")}
|
||||
</AlertTitle>
|
||||
<AlertDescription>
|
||||
{alert.name === "Status" ? (
|
||||
<Trans>Connection is down</Trans>
|
||||
) : (
|
||||
<Trans>
|
||||
Exceeds {alert.value}
|
||||
{info.unit} in last <Plural value={alert.min} one="# minute" other="# minutes" />
|
||||
</Trans>
|
||||
)}
|
||||
</AlertDescription>
|
||||
<Link
|
||||
href={getPagePath($router, "system", { name: getSystemNameFromId(alert.system) })}
|
||||
className="absolute inset-0 w-full h-full"
|
||||
aria-label="View system"
|
||||
></Link>
|
||||
</Alert>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}, [alertsKey.join("")])
|
||||
}
|
||||
|
||||
@@ -0,0 +1,385 @@
|
||||
import { pb } from "@/lib/stores"
|
||||
import { alertInfo, cn, formatDuration, formatShortDate } from "@/lib/utils"
|
||||
import { AlertsHistoryRecord } from "@/types"
|
||||
import {
|
||||
getCoreRowModel,
|
||||
getPaginationRowModel,
|
||||
getSortedRowModel,
|
||||
getFilteredRowModel,
|
||||
useReactTable,
|
||||
flexRender,
|
||||
ColumnFiltersState,
|
||||
SortingState,
|
||||
VisibilityState,
|
||||
} from "@tanstack/react-table"
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
|
||||
import { Button, buttonVariants } from "@/components/ui/button"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { alertsHistoryColumns } from "../../alerts-history-columns"
|
||||
import { Checkbox } from "@/components/ui/checkbox"
|
||||
import { memo, useEffect, useState } from "react"
|
||||
import { Label } from "@/components/ui/label"
|
||||
import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from "@/components/ui/select"
|
||||
import {
|
||||
ChevronLeftIcon,
|
||||
ChevronRightIcon,
|
||||
ChevronsLeftIcon,
|
||||
ChevronsRightIcon,
|
||||
DownloadIcon,
|
||||
Trash2Icon,
|
||||
} from "lucide-react"
|
||||
import { Trans } from "@lingui/react/macro"
|
||||
import { t } from "@lingui/core/macro"
|
||||
import { useToast } from "@/components/ui/use-toast"
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
AlertDialogTrigger,
|
||||
} from "@/components/ui/alert-dialog"
|
||||
|
||||
const SectionIntro = memo(() => {
|
||||
return (
|
||||
<div>
|
||||
<h3 className="text-xl font-medium mb-2">
|
||||
<Trans>Alert History</Trans>
|
||||
</h3>
|
||||
<p className="text-sm text-muted-foreground leading-relaxed">
|
||||
<Trans>View your 200 most recent alerts.</Trans>
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
export default function AlertsHistoryDataTable() {
|
||||
const [data, setData] = useState<AlertsHistoryRecord[]>([])
|
||||
const [sorting, setSorting] = useState<SortingState>([])
|
||||
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([])
|
||||
const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({})
|
||||
const [rowSelection, setRowSelection] = useState({})
|
||||
const [globalFilter, setGlobalFilter] = useState("")
|
||||
const { toast } = useToast()
|
||||
const [deleteOpen, setDeleteDialogOpen] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
let unsubscribe: (() => void) | undefined
|
||||
const pbOptions = {
|
||||
expand: "system",
|
||||
fields: "id,name,value,state,created,resolved,expand.system.name",
|
||||
}
|
||||
// Initial load
|
||||
pb.collection<AlertsHistoryRecord>("alerts_history")
|
||||
.getList(0, 200, {
|
||||
...pbOptions,
|
||||
sort: "-created",
|
||||
})
|
||||
.then(({ items }) => setData(items))
|
||||
|
||||
// Subscribe to changes
|
||||
;(async () => {
|
||||
unsubscribe = await pb.collection("alerts_history").subscribe(
|
||||
"*",
|
||||
(e) => {
|
||||
if (e.action === "create") {
|
||||
setData((current) => [e.record as AlertsHistoryRecord, ...current])
|
||||
}
|
||||
if (e.action === "update") {
|
||||
setData((current) => current.map((r) => (r.id === e.record.id ? (e.record as AlertsHistoryRecord) : r)))
|
||||
}
|
||||
if (e.action === "delete") {
|
||||
setData((current) => current.filter((r) => r.id !== e.record.id))
|
||||
}
|
||||
},
|
||||
pbOptions
|
||||
)
|
||||
})()
|
||||
// Unsubscribe on unmount
|
||||
return () => unsubscribe?.()
|
||||
}, [])
|
||||
|
||||
const table = useReactTable({
|
||||
data,
|
||||
columns: [
|
||||
{
|
||||
id: "select",
|
||||
header: ({ table }) => (
|
||||
<Checkbox
|
||||
className="ms-2"
|
||||
checked={table.getIsAllPageRowsSelected() || (table.getIsSomePageRowsSelected() && "indeterminate")}
|
||||
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
|
||||
aria-label="Select all"
|
||||
/>
|
||||
),
|
||||
cell: ({ row }) => (
|
||||
<Checkbox
|
||||
checked={row.getIsSelected()}
|
||||
onCheckedChange={(value) => row.toggleSelected(!!value)}
|
||||
aria-label="Select row"
|
||||
/>
|
||||
),
|
||||
enableSorting: false,
|
||||
enableHiding: false,
|
||||
},
|
||||
...alertsHistoryColumns,
|
||||
],
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
onSortingChange: setSorting,
|
||||
onColumnFiltersChange: setColumnFilters,
|
||||
onColumnVisibilityChange: setColumnVisibility,
|
||||
onRowSelectionChange: setRowSelection,
|
||||
state: {
|
||||
sorting,
|
||||
columnFilters,
|
||||
columnVisibility,
|
||||
rowSelection,
|
||||
globalFilter,
|
||||
},
|
||||
onGlobalFilterChange: setGlobalFilter,
|
||||
globalFilterFn: (row, _columnId, filterValue) => {
|
||||
const system = row.original.expand?.system?.name ?? ""
|
||||
const name = row.getValue("name") ?? ""
|
||||
const created = row.getValue("created") ?? ""
|
||||
const search = String(filterValue).toLowerCase()
|
||||
return (
|
||||
system.toLowerCase().includes(search) ||
|
||||
(name as string).toLowerCase().includes(search) ||
|
||||
(created as string).toLowerCase().includes(search)
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
// Bulk delete handler
|
||||
const handleBulkDelete = async () => {
|
||||
setDeleteDialogOpen(false)
|
||||
const selectedIds = table.getSelectedRowModel().rows.map((row) => row.original.id)
|
||||
try {
|
||||
let batch = pb.createBatch()
|
||||
let inBatch = 0
|
||||
for (const id of selectedIds) {
|
||||
batch.collection("alerts_history").delete(id)
|
||||
inBatch++
|
||||
if (inBatch > 20) {
|
||||
await batch.send()
|
||||
batch = pb.createBatch()
|
||||
inBatch = 0
|
||||
}
|
||||
}
|
||||
inBatch && (await batch.send())
|
||||
table.resetRowSelection()
|
||||
} catch (e) {
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: t`Error`,
|
||||
description: `Failed to delete records.`,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Export to CSV handler
|
||||
const handleExportCSV = () => {
|
||||
const selectedRows = table.getSelectedRowModel().rows
|
||||
if (!selectedRows.length) return
|
||||
const cells: Record<string, (record: AlertsHistoryRecord) => string> = {
|
||||
system: (record) => record.expand?.system?.name || record.system,
|
||||
name: (record) => alertInfo[record.name]?.name() || record.name,
|
||||
value: (record) => record.value + (alertInfo[record.name]?.unit ?? ""),
|
||||
state: (record) => (record.resolved ? t`Resolved` : t`Active`),
|
||||
created: (record) => formatShortDate(record.created),
|
||||
resolved: (record) => (record.resolved ? formatShortDate(record.resolved) : ""),
|
||||
duration: (record) => (record.resolved ? formatDuration(record.created, record.resolved) : ""),
|
||||
}
|
||||
const csvRows = [Object.keys(cells).join(",")]
|
||||
for (const row of selectedRows) {
|
||||
const r = row.original
|
||||
csvRows.push(
|
||||
Object.values(cells)
|
||||
.map((val) => val(r))
|
||||
.join(",")
|
||||
)
|
||||
}
|
||||
const blob = new Blob([csvRows.join("\n")], { type: "text/csv" })
|
||||
const url = URL.createObjectURL(blob)
|
||||
const a = document.createElement("a")
|
||||
a.href = url
|
||||
a.download = "alerts_history.csv"
|
||||
a.click()
|
||||
URL.revokeObjectURL(url)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="@container w-full">
|
||||
<div className="@3xl:flex items-end mb-4 gap-4">
|
||||
<SectionIntro />
|
||||
<div className="flex items-center gap-2 ms-auto mt-3 @3xl:mt-0">
|
||||
{table.getFilteredSelectedRowModel().rows.length > 0 && (
|
||||
<div className="fixed bottom-0 left-0 w-full p-4 grid grid-cols-2 items-center gap-4 z-50 backdrop-blur-md shrink-0 @lg:static @lg:p-0 @lg:w-auto @lg:gap-3">
|
||||
<AlertDialog open={deleteOpen} onOpenChange={(open) => setDeleteDialogOpen(open)}>
|
||||
<AlertDialogTrigger asChild>
|
||||
<Button variant="destructive" className="h-9 shrink-0">
|
||||
<Trash2Icon className="size-4 shrink-0" />
|
||||
<span className="ms-1">
|
||||
<Trans>Delete</Trans>
|
||||
</span>
|
||||
</Button>
|
||||
</AlertDialogTrigger>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>
|
||||
<Trans>Are you sure?</Trans>
|
||||
</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
<Trans>This will permanently delete all selected records from the database.</Trans>
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>
|
||||
<Trans>Cancel</Trans>
|
||||
</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
className={cn(buttonVariants({ variant: "destructive" }))}
|
||||
onClick={handleBulkDelete}
|
||||
>
|
||||
<Trans>Continue</Trans>
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
<Button variant="outline" className="h-10" onClick={handleExportCSV}>
|
||||
<DownloadIcon className="size-4" />
|
||||
<span className="ms-1">
|
||||
<Trans>Export</Trans>
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
<Input
|
||||
placeholder={t`Filter...`}
|
||||
value={globalFilter}
|
||||
onChange={(e) => setGlobalFilter(e.target.value)}
|
||||
className="px-4 w-full max-w-full @3xl:w-64"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="rounded-md border overflow-x-auto whitespace-nowrap">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<TableRow key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => (
|
||||
<TableHead className="px-2" key={header.id}>
|
||||
{header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}
|
||||
</TableHead>
|
||||
))}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{table.getRowModel().rows.length ? (
|
||||
table.getRowModel().rows.map((row) => (
|
||||
<TableRow key={row.id} data-state={row.getIsSelected() && "selected"}>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<TableCell key={cell.id} className="py-3">
|
||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell colSpan={table.getAllColumns().length} className="h-24 text-center">
|
||||
<Trans>No results.</Trans>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
<div className="flex items-center justify-between ps-1 tabular-nums">
|
||||
<div className="text-muted-foreground hidden flex-1 text-sm lg:flex">
|
||||
<Trans>
|
||||
{table.getFilteredSelectedRowModel().rows.length} of {table.getFilteredRowModel().rows.length} row(s)
|
||||
selected.
|
||||
</Trans>
|
||||
</div>
|
||||
<div className="flex w-full items-center gap-8 lg:w-fit my-3">
|
||||
<div className="hidden items-center gap-2 lg:flex">
|
||||
<Label htmlFor="rows-per-page" className="text-sm font-medium">
|
||||
<Trans>Rows per page</Trans>
|
||||
</Label>
|
||||
<Select
|
||||
value={`${table.getState().pagination.pageSize}`}
|
||||
onValueChange={(value) => {
|
||||
table.setPageSize(Number(value))
|
||||
}}
|
||||
>
|
||||
<SelectTrigger className="w-[4.8em]" id="rows-per-page">
|
||||
<SelectValue placeholder={table.getState().pagination.pageSize} />
|
||||
</SelectTrigger>
|
||||
<SelectContent side="top">
|
||||
{[10, 20, 50, 100, 200].map((pageSize) => (
|
||||
<SelectItem key={pageSize} value={`${pageSize}`}>
|
||||
{pageSize}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="flex w-fit items-center justify-center text-sm font-medium">
|
||||
<Trans>
|
||||
Page {table.getState().pagination.pageIndex + 1} of {table.getPageCount()}
|
||||
</Trans>
|
||||
</div>
|
||||
<div className="ms-auto flex items-center gap-2 lg:ms-0">
|
||||
<Button
|
||||
variant="outline"
|
||||
className="hidden size-9 p-0 lg:flex"
|
||||
onClick={() => table.setPageIndex(0)}
|
||||
disabled={!table.getCanPreviousPage()}
|
||||
>
|
||||
<span className="sr-only">Go to first page</span>
|
||||
<ChevronsLeftIcon className="size-5" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="size-9"
|
||||
size="icon"
|
||||
onClick={() => table.previousPage()}
|
||||
disabled={!table.getCanPreviousPage()}
|
||||
>
|
||||
<span className="sr-only">Go to previous page</span>
|
||||
<ChevronLeftIcon className="size-5" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="size-9"
|
||||
size="icon"
|
||||
onClick={() => table.nextPage()}
|
||||
disabled={!table.getCanNextPage()}
|
||||
>
|
||||
<span className="sr-only">Go to next page</span>
|
||||
<ChevronRightIcon className="size-5" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="hidden size-9 lg:flex"
|
||||
size="icon"
|
||||
onClick={() => table.setPageIndex(table.getPageCount() - 1)}
|
||||
disabled={!table.getCanNextPage()}
|
||||
>
|
||||
<span className="sr-only">Go to last page</span>
|
||||
<ChevronsRightIcon className="size-5" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import { useState } from "react"
|
||||
import languages from "@/lib/languages"
|
||||
import { dynamicActivate } from "@/lib/i18n"
|
||||
import { useLingui } from "@lingui/react/macro"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Unit } from "@/lib/enums"
|
||||
|
||||
export default function SettingsProfilePage({ userSettings }: { userSettings: UserSettings }) {
|
||||
@@ -104,7 +105,7 @@ export default function SettingsProfilePage({ userSettings }: { userSettings: Us
|
||||
<div className="space-y-2">
|
||||
<div className="mb-4">
|
||||
<h3 className="mb-1 text-lg font-medium">
|
||||
<Trans>Unit preferences</Trans>
|
||||
<Trans comment="Temperature / network units">Unit preferences</Trans>
|
||||
</h3>
|
||||
<p className="text-sm text-muted-foreground leading-relaxed">
|
||||
<Trans>Change display units for metrics.</Trans>
|
||||
@@ -133,10 +134,9 @@ export default function SettingsProfilePage({ userSettings }: { userSettings: Us
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label className="block" htmlFor="unitNet">
|
||||
<Trans>Network unit</Trans>
|
||||
<Trans comment="Context: Bytes or bits">Network unit</Trans>
|
||||
</Label>
|
||||
<Select
|
||||
name="unitNet"
|
||||
@@ -156,7 +156,6 @@ export default function SettingsProfilePage({ userSettings }: { userSettings: Us
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label className="block" htmlFor="unitDisk">
|
||||
<Trans>Disk unit</Trans>
|
||||
@@ -182,6 +181,47 @@ export default function SettingsProfilePage({ userSettings }: { userSettings: Us
|
||||
</div>
|
||||
</div>
|
||||
<Separator />
|
||||
<div className="space-y-2">
|
||||
<div className="mb-4">
|
||||
<h3 className="mb-1 text-lg font-medium">
|
||||
<Trans>Warning thresholds</Trans>
|
||||
</h3>
|
||||
<p className="text-sm text-muted-foreground leading-relaxed">
|
||||
<Trans>Set percentage thresholds for meter colors.</Trans>
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 lg:grid-cols-3 gap-4 items-end">
|
||||
<div className="space-y-1">
|
||||
<Label htmlFor="colorWarn">
|
||||
<Trans>Warning (%)</Trans>
|
||||
</Label>
|
||||
<Input
|
||||
id="colorWarn"
|
||||
name="colorWarn"
|
||||
type="number"
|
||||
min={1}
|
||||
max={100}
|
||||
className="min-w-24"
|
||||
defaultValue={userSettings.colorWarn ?? 65}
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<Label htmlFor="colorCrit">
|
||||
<Trans>Critical (%)</Trans>
|
||||
</Label>
|
||||
<Input
|
||||
id="colorCrit"
|
||||
name="colorCrit"
|
||||
type="number"
|
||||
min={1}
|
||||
max={100}
|
||||
className="min-w-24"
|
||||
defaultValue={userSettings.colorCrit ?? 90}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Separator />
|
||||
<Button type="submit" className="flex items-center gap-1.5 disabled:opacity-100" disabled={isLoading}>
|
||||
{isLoading ? <LoaderCircleIcon className="h-4 w-4 animate-spin" /> : <SaveIcon className="h-4 w-4" />}
|
||||
<Trans>Save Settings</Trans>
|
||||
|
||||
@@ -7,15 +7,16 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/com
|
||||
import { useStore } from "@nanostores/react"
|
||||
import { $router } from "@/components/router.tsx"
|
||||
import { getPagePath, redirectPage } from "@nanostores/router"
|
||||
import { BellIcon, FileSlidersIcon, FingerprintIcon, SettingsIcon } from "lucide-react"
|
||||
import { BellIcon, FileSlidersIcon, FingerprintIcon, SettingsIcon, AlertOctagonIcon } from "lucide-react"
|
||||
import { $userSettings, pb } from "@/lib/stores.ts"
|
||||
import { toast } from "@/components/ui/use-toast.ts"
|
||||
import { UserSettings } from "@/types.js"
|
||||
import { UserSettings } from "@/types"
|
||||
import General from "./general.tsx"
|
||||
import Notifications from "./notifications.tsx"
|
||||
import ConfigYaml from "./config-yaml.tsx"
|
||||
import { useLingui } from "@lingui/react/macro"
|
||||
import Fingerprints from "./tokens-fingerprints.tsx"
|
||||
import AlertsHistoryDataTable from "./alerts-history-data-table"
|
||||
|
||||
export async function saveSettings(newSettings: Partial<UserSettings>) {
|
||||
try {
|
||||
@@ -65,6 +66,11 @@ export default function SettingsLayout() {
|
||||
icon: FingerprintIcon,
|
||||
noReadOnly: true,
|
||||
},
|
||||
{
|
||||
title: t`Alert History`,
|
||||
href: getPagePath($router, "settings", { name: "alert-history" }),
|
||||
icon: AlertOctagonIcon,
|
||||
},
|
||||
{
|
||||
title: t`YAML Config`,
|
||||
href: getPagePath($router, "settings", { name: "config" }),
|
||||
@@ -121,5 +127,7 @@ function SettingsContent({ name }: { name: string }) {
|
||||
return <ConfigYaml />
|
||||
case "tokens":
|
||||
return <Fingerprints />
|
||||
case "alert-history":
|
||||
return <AlertsHistoryDataTable />
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,7 +178,7 @@ const ShoutrrrUrlCard = ({ url, onUrlChange, onRemove }: ShoutrrrUrlCardProps) =
|
||||
|
||||
const sendTestNotification = async () => {
|
||||
setIsLoading(true)
|
||||
const res = await pb.send("/api/beszel/send-test-notification", { url })
|
||||
const res = await pb.send("/api/beszel/test-notification", { method: "POST", body: { url } })
|
||||
if ("err" in res && !res.err) {
|
||||
toast({
|
||||
title: t`Test notification sent`,
|
||||
|
||||
@@ -33,7 +33,7 @@ export function SidebarNav({ className, items, ...props }: SidebarNavProps) {
|
||||
return (
|
||||
<SelectItem key={item.href} value={item.href}>
|
||||
<span className="flex items-center gap-2 truncate">
|
||||
{item.icon && <item.icon className="h-4 w-4" />}
|
||||
{item.icon && <item.icon className="size-4" />}
|
||||
<span className="truncate">{item.title}</span>
|
||||
</span>
|
||||
</SelectItem>
|
||||
@@ -45,7 +45,7 @@ export function SidebarNav({ className, items, ...props }: SidebarNavProps) {
|
||||
</div>
|
||||
|
||||
{/* Desktop View */}
|
||||
<nav className={cn("hidden md:grid gap-1", className)} {...props}>
|
||||
<nav className={cn("hidden md:grid gap-1 sticky top-6", className)} {...props}>
|
||||
{items.map((item) => {
|
||||
if ((item.admin && !isAdmin()) || (item.noReadOnly && isReadOnlyUser())) {
|
||||
return null
|
||||
@@ -56,11 +56,11 @@ export function SidebarNav({ className, items, ...props }: SidebarNavProps) {
|
||||
href={item.href}
|
||||
className={cn(
|
||||
buttonVariants({ variant: "ghost" }),
|
||||
"flex items-center gap-3 justify-start truncate",
|
||||
page?.path === item.href ? "bg-muted hover:bg-muted" : "hover:bg-muted/50"
|
||||
"flex items-center gap-3 justify-start truncate duration-50",
|
||||
page?.path === item.href ? "bg-muted hover:bg-accent/70" : "hover:bg-accent/50"
|
||||
)}
|
||||
>
|
||||
{item.icon && <item.icon className="h-4 w-4 shrink-0" />}
|
||||
{item.icon && <item.icon className="size-4 shrink-0" />}
|
||||
<span className="truncate">{item.title}</span>
|
||||
</Link>
|
||||
)
|
||||
|
||||
@@ -42,6 +42,10 @@ const pbFingerprintOptions = {
|
||||
fields: "id,fingerprint,token,system,expand.system.name",
|
||||
}
|
||||
|
||||
function sortFingerprints(fingerprints: FingerprintRecord[]) {
|
||||
return fingerprints.sort((a, b) => a.expand.system.name.localeCompare(b.expand.system.name))
|
||||
}
|
||||
|
||||
const SettingsFingerprintsPage = memo(() => {
|
||||
if (isReadOnlyUser()) {
|
||||
redirectPage($router, "settings", { name: "general" })
|
||||
@@ -51,9 +55,10 @@ const SettingsFingerprintsPage = memo(() => {
|
||||
// Get fingerprint records on mount
|
||||
useEffect(() => {
|
||||
pb.collection("fingerprints")
|
||||
.getFullList(pbFingerprintOptions)
|
||||
// @ts-ignore
|
||||
.then(setFingerprints)
|
||||
.getFullList<FingerprintRecord>(pbFingerprintOptions)
|
||||
.then((prints) => {
|
||||
setFingerprints(sortFingerprints(prints))
|
||||
})
|
||||
}, [])
|
||||
|
||||
// Subscribe to fingerprint updates
|
||||
@@ -66,7 +71,7 @@ const SettingsFingerprintsPage = memo(() => {
|
||||
(res) => {
|
||||
setFingerprints((currentFingerprints) => {
|
||||
if (res.action === "create") {
|
||||
return [...currentFingerprints, res.record as FingerprintRecord]
|
||||
return sortFingerprints([...currentFingerprints, res.record as FingerprintRecord])
|
||||
}
|
||||
if (res.action === "update") {
|
||||
return currentFingerprints.map((fingerprint) => {
|
||||
@@ -159,7 +164,7 @@ const SectionUniversalToken = memo(() => {
|
||||
or on hub restart.
|
||||
</Trans>
|
||||
</p>
|
||||
<div className="min-h-16 overflow-auto max-w-full inline-flex items-center gap-5 mt-3 border py-2 pl-5 pr-4 rounded-md">
|
||||
<div className="min-h-16 overflow-auto max-w-full inline-flex items-center gap-5 mt-3 border py-2 ps-5 pe-4 rounded-md">
|
||||
{!isLoading && (
|
||||
<>
|
||||
<Switch
|
||||
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
getHostDisplayValue,
|
||||
getPbTimestamp,
|
||||
listen,
|
||||
parseSemVer,
|
||||
toFixedFloat,
|
||||
useLocalStorage,
|
||||
} from "@/lib/utils"
|
||||
@@ -191,6 +192,7 @@ export default function SystemDetail({ name }: { name: string }) {
|
||||
chartTime,
|
||||
orientation: direction === "rtl" ? "right" : "left",
|
||||
...getTimeData(chartTime, lastCreated),
|
||||
agentVersion: parseSemVer(system?.info?.v),
|
||||
}
|
||||
}, [systemStats, containerData, direction])
|
||||
|
||||
@@ -371,6 +373,7 @@ export default function SystemDetail({ name }: { name: string }) {
|
||||
|
||||
// select field for switching between avg and max values
|
||||
const maxValSelect = isLongerChart ? <SelectAvgMax max={maxValues} /> : null
|
||||
const showMax = chartTime !== "1h" && maxValues
|
||||
|
||||
// if no data, show empty message
|
||||
const dataEmpty = !chartLoading && chartData.systemStats.length === 0
|
||||
@@ -453,9 +456,9 @@ export default function SystemDetail({ name }: { name: string }) {
|
||||
onClick={() => setGrid(!grid)}
|
||||
>
|
||||
{grid ? (
|
||||
<LayoutGridIcon className="h-[1.2rem] w-[1.2rem] opacity-85" />
|
||||
<LayoutGridIcon className="h-[1.2rem] w-[1.2rem] opacity-75" />
|
||||
) : (
|
||||
<Rows className="h-[1.3rem] w-[1.3rem] opacity-85" />
|
||||
<Rows className="h-[1.3rem] w-[1.3rem] opacity-75" />
|
||||
)}
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
@@ -477,25 +480,20 @@ export default function SystemDetail({ name }: { name: string }) {
|
||||
>
|
||||
<AreaChartDefault
|
||||
chartData={chartData}
|
||||
chartName="CPU Usage"
|
||||
maxToggled={maxValues}
|
||||
dataPoints={[
|
||||
{
|
||||
label: t`CPU Usage`,
|
||||
dataKey: ({ stats }) => (showMax ? stats?.cpum : stats?.cpu),
|
||||
color: "1",
|
||||
opacity: 0.4,
|
||||
},
|
||||
]}
|
||||
tickFormatter={(val) => toFixedFloat(val, 2) + "%"}
|
||||
contentFormatter={({ value }) => decimalString(value) + "%"}
|
||||
/>
|
||||
</ChartCard>
|
||||
|
||||
{/* Load Average chart */}
|
||||
{(systemStats.at(-1)?.stats.l1 !== undefined || systemStats.at(-1)?.stats.l5 !== undefined || systemStats.at(-1)?.stats.l15 !== undefined) && (
|
||||
<ChartCard
|
||||
empty={dataEmpty}
|
||||
grid={grid}
|
||||
title={t`Load Average`}
|
||||
description={t`System load averages over time`}
|
||||
>
|
||||
<LoadAverageChart chartData={chartData} />
|
||||
</ChartCard>
|
||||
)}
|
||||
|
||||
{containerFilterBar && (
|
||||
<ChartCard
|
||||
empty={dataEmpty}
|
||||
@@ -542,8 +540,21 @@ export default function SystemDetail({ name }: { name: string }) {
|
||||
>
|
||||
<AreaChartDefault
|
||||
chartData={chartData}
|
||||
chartName="dio"
|
||||
maxToggled={maxValues}
|
||||
dataPoints={[
|
||||
{
|
||||
label: t({ message: "Write", comment: "Disk write" }),
|
||||
dataKey: ({ stats }) => (showMax ? stats?.dwm : stats?.dw),
|
||||
color: "3",
|
||||
opacity: 0.3,
|
||||
},
|
||||
{
|
||||
label: t({ message: "Read", comment: "Disk read" }),
|
||||
dataKey: ({ stats }) => (showMax ? stats?.drm : stats?.dr),
|
||||
color: "1",
|
||||
opacity: 0.3,
|
||||
},
|
||||
]}
|
||||
tickFormatter={(val) => {
|
||||
const { value, unit } = formatBytes(val, true, userSettings.unitDisk, true)
|
||||
return toFixedFloat(value, value >= 10 ? 0 : 1) + " " + unit
|
||||
@@ -564,15 +575,39 @@ export default function SystemDetail({ name }: { name: string }) {
|
||||
>
|
||||
<AreaChartDefault
|
||||
chartData={chartData}
|
||||
chartName="bw"
|
||||
maxToggled={maxValues}
|
||||
dataPoints={[
|
||||
{
|
||||
label: t`Sent`,
|
||||
// use bytes if available, otherwise multiply old MB (can remove in future)
|
||||
dataKey(data) {
|
||||
if (showMax) {
|
||||
return data?.stats?.bm?.[0] ?? (data?.stats?.nsm ?? 0) * 1024 * 1024
|
||||
}
|
||||
return data?.stats?.b?.[0] ?? data?.stats?.ns * 1024 * 1024
|
||||
},
|
||||
color: "5",
|
||||
opacity: 0.2,
|
||||
},
|
||||
{
|
||||
label: t`Received`,
|
||||
dataKey(data) {
|
||||
if (showMax) {
|
||||
return data?.stats?.bm?.[1] ?? (data?.stats?.nrm ?? 0) * 1024 * 1024
|
||||
}
|
||||
return data?.stats?.b?.[1] ?? data?.stats?.nr * 1024 * 1024
|
||||
},
|
||||
color: "2",
|
||||
opacity: 0.2,
|
||||
},
|
||||
]}
|
||||
tickFormatter={(val) => {
|
||||
let { value, unit } = formatBytes(val, true, userSettings.unitNet, true)
|
||||
let { value, unit } = formatBytes(val, true, userSettings.unitNet, false)
|
||||
return toFixedFloat(value, value >= 10 ? 0 : 1) + " " + unit
|
||||
}}
|
||||
contentFormatter={({ value }) => {
|
||||
const { value: convertedValue, unit } = formatBytes(value, true, userSettings.unitNet, true)
|
||||
return decimalString(convertedValue, convertedValue >= 100 ? 1 : 2) + " " + unit
|
||||
contentFormatter={(data) => {
|
||||
const { value, unit } = formatBytes(data.value, true, userSettings.unitNet, false)
|
||||
return decimalString(value, value >= 100 ? 1 : 2) + " " + unit
|
||||
}}
|
||||
/>
|
||||
</ChartCard>
|
||||
@@ -608,6 +643,18 @@ export default function SystemDetail({ name }: { name: string }) {
|
||||
</ChartCard>
|
||||
)}
|
||||
|
||||
{/* Load Average chart */}
|
||||
{chartData.agentVersion?.minor >= 12 && (
|
||||
<ChartCard
|
||||
empty={dataEmpty}
|
||||
grid={grid}
|
||||
title={t`Load Average`}
|
||||
description={t`System load averages over time`}
|
||||
>
|
||||
<LoadAverageChart chartData={chartData} />
|
||||
</ChartCard>
|
||||
)}
|
||||
|
||||
{/* Temperature chart */}
|
||||
{systemStats.at(-1)?.stats.t && (
|
||||
<ChartCard
|
||||
@@ -649,7 +696,14 @@ export default function SystemDetail({ name }: { name: string }) {
|
||||
>
|
||||
<AreaChartDefault
|
||||
chartData={chartData}
|
||||
chartName={`g.${id}.u`}
|
||||
dataPoints={[
|
||||
{
|
||||
label: t`Usage`,
|
||||
dataKey: ({ stats }) => stats?.g?.[id]?.u ?? 0,
|
||||
color: "1",
|
||||
opacity: 0.35,
|
||||
},
|
||||
]}
|
||||
tickFormatter={(val) => toFixedFloat(val, 2) + "%"}
|
||||
contentFormatter={({ value }) => decimalString(value) + "%"}
|
||||
/>
|
||||
@@ -662,7 +716,14 @@ export default function SystemDetail({ name }: { name: string }) {
|
||||
>
|
||||
<AreaChartDefault
|
||||
chartData={chartData}
|
||||
chartName={`g.${id}.mu`}
|
||||
dataPoints={[
|
||||
{
|
||||
label: t`Usage`,
|
||||
dataKey: ({ stats }) => stats?.g?.[id]?.mu ?? 0,
|
||||
color: "2",
|
||||
opacity: 0.25,
|
||||
},
|
||||
]}
|
||||
max={gpu.mt}
|
||||
tickFormatter={(val) => {
|
||||
const { value, unit } = formatBytes(val, false, Unit.Bytes, true)
|
||||
@@ -707,7 +768,20 @@ export default function SystemDetail({ name }: { name: string }) {
|
||||
>
|
||||
<AreaChartDefault
|
||||
chartData={chartData}
|
||||
chartName={`efs.${extraFsName}`}
|
||||
dataPoints={[
|
||||
{
|
||||
label: t`Write`,
|
||||
dataKey: ({ stats }) => stats?.efs?.[extraFsName]?.[showMax ? "wm" : "w"] ?? 0,
|
||||
color: "3",
|
||||
opacity: 0.3,
|
||||
},
|
||||
{
|
||||
label: t`Read`,
|
||||
dataKey: ({ stats }) => stats?.efs?.[extraFsName]?.[showMax ? "rm" : "r"] ?? 0,
|
||||
color: "1",
|
||||
opacity: 0.3,
|
||||
},
|
||||
]}
|
||||
maxToggled={maxValues}
|
||||
tickFormatter={(val) => {
|
||||
const { value, unit } = formatBytes(val, true, userSettings.unitDisk, true)
|
||||
|
||||
@@ -0,0 +1,440 @@
|
||||
import { SystemRecord } from "@/types"
|
||||
import { CellContext, ColumnDef, HeaderContext } from "@tanstack/react-table"
|
||||
import { ClassValue } from "clsx"
|
||||
import {
|
||||
ArrowUpDownIcon,
|
||||
CopyIcon,
|
||||
CpuIcon,
|
||||
HardDriveIcon,
|
||||
MemoryStickIcon,
|
||||
MoreHorizontalIcon,
|
||||
PauseCircleIcon,
|
||||
PenBoxIcon,
|
||||
PlayCircleIcon,
|
||||
ServerIcon,
|
||||
Trash2Icon,
|
||||
WifiIcon,
|
||||
} from "lucide-react"
|
||||
import { Button } from "../ui/button"
|
||||
import {
|
||||
cn,
|
||||
copyToClipboard,
|
||||
decimalString,
|
||||
formatBytes,
|
||||
formatTemperature,
|
||||
getMeterState,
|
||||
isReadOnlyUser,
|
||||
parseSemVer,
|
||||
} from "@/lib/utils"
|
||||
import { EthernetIcon, GpuIcon, HourglassIcon, ThermometerIcon } from "../ui/icons"
|
||||
import { useStore } from "@nanostores/react"
|
||||
import { $userSettings, pb } from "@/lib/stores"
|
||||
import { Trans, useLingui } from "@lingui/react/macro"
|
||||
import { useMemo, useRef, useState } from "react"
|
||||
import { memo } from "react"
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from "../ui/dropdown-menu"
|
||||
import AlertButton from "../alerts/alert-button"
|
||||
import { Dialog } from "../ui/dialog"
|
||||
import { SystemDialog } from "../add-system"
|
||||
import { AlertDialog } from "../ui/alert-dialog"
|
||||
import {
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
} from "../ui/alert-dialog"
|
||||
import { buttonVariants } from "../ui/button"
|
||||
import { t } from "@lingui/core/macro"
|
||||
import { MeterState } from "@/lib/enums"
|
||||
|
||||
const STATUS_COLORS = {
|
||||
up: "bg-green-500",
|
||||
down: "bg-red-500",
|
||||
paused: "bg-primary/40",
|
||||
pending: "bg-yellow-500",
|
||||
} as const
|
||||
|
||||
/**
|
||||
* @param viewMode - "table" or "grid"
|
||||
* @returns - Column definitions for the systems table
|
||||
*/
|
||||
export default function SystemsTableColumns(viewMode: "table" | "grid"): ColumnDef<SystemRecord>[] {
|
||||
return [
|
||||
{
|
||||
size: 200,
|
||||
minSize: 0,
|
||||
accessorKey: "name",
|
||||
id: "system",
|
||||
name: () => t`System`,
|
||||
filterFn: (() => {
|
||||
let filterInput = ""
|
||||
let filterInputLower = ""
|
||||
const nameCache = new Map<string, string>()
|
||||
const statusTranslations = {
|
||||
up: t`Up`.toLowerCase(),
|
||||
down: t`Down`.toLowerCase(),
|
||||
paused: t`Paused`.toLowerCase(),
|
||||
} as const
|
||||
|
||||
// match filter value against name or translated status
|
||||
return (row, _, newFilterInput) => {
|
||||
const { name, status } = row.original
|
||||
if (newFilterInput !== filterInput) {
|
||||
filterInput = newFilterInput
|
||||
filterInputLower = newFilterInput.toLowerCase()
|
||||
}
|
||||
let nameLower = nameCache.get(name)
|
||||
if (nameLower === undefined) {
|
||||
nameLower = name.toLowerCase()
|
||||
nameCache.set(name, nameLower)
|
||||
}
|
||||
if (nameLower.includes(filterInputLower)) {
|
||||
return true
|
||||
}
|
||||
const statusLower = statusTranslations[status as keyof typeof statusTranslations]
|
||||
return statusLower?.includes(filterInputLower) || false
|
||||
}
|
||||
})(),
|
||||
enableHiding: false,
|
||||
invertSorting: false,
|
||||
Icon: ServerIcon,
|
||||
cell: (info) => (
|
||||
<span className="flex gap-2 items-center font-medium text-sm text-nowrap md:ps-1 md:pe-5">
|
||||
<IndicatorDot system={info.row.original} />
|
||||
{info.getValue() as string}
|
||||
</span>
|
||||
),
|
||||
header: sortableHeader,
|
||||
},
|
||||
{
|
||||
accessorFn: ({ info }) => info.cpu,
|
||||
id: "cpu",
|
||||
name: () => t`CPU`,
|
||||
cell: TableCellWithMeter,
|
||||
Icon: CpuIcon,
|
||||
header: sortableHeader,
|
||||
},
|
||||
{
|
||||
// accessorKey: "info.mp",
|
||||
accessorFn: ({ info }) => info.mp,
|
||||
id: "memory",
|
||||
name: () => t`Memory`,
|
||||
cell: TableCellWithMeter,
|
||||
Icon: MemoryStickIcon,
|
||||
header: sortableHeader,
|
||||
},
|
||||
{
|
||||
accessorFn: ({ info }) => info.dp,
|
||||
id: "disk",
|
||||
name: () => t`Disk`,
|
||||
cell: TableCellWithMeter,
|
||||
Icon: HardDriveIcon,
|
||||
header: sortableHeader,
|
||||
},
|
||||
{
|
||||
accessorFn: ({ info }) => info.g,
|
||||
id: "gpu",
|
||||
name: () => "GPU",
|
||||
cell: TableCellWithMeter,
|
||||
Icon: GpuIcon,
|
||||
header: sortableHeader,
|
||||
},
|
||||
{
|
||||
id: "loadAverage",
|
||||
accessorFn: ({ info }) => {
|
||||
const sum = info.la?.reduce((acc, curr) => acc + curr, 0)
|
||||
// TODO: remove this in future release in favor of la array
|
||||
if (!sum) {
|
||||
return (info.l1 ?? 0) + (info.l5 ?? 0) + (info.l15 ?? 0)
|
||||
}
|
||||
return sum
|
||||
},
|
||||
name: () => t({ message: "Load Avg", comment: "Short label for load average" }),
|
||||
size: 0,
|
||||
Icon: HourglassIcon,
|
||||
header: sortableHeader,
|
||||
cell(info: CellContext<SystemRecord, unknown>) {
|
||||
const { info: sysInfo, status } = info.row.original
|
||||
// agent version
|
||||
const { minor, patch } = parseSemVer(sysInfo.v)
|
||||
let loadAverages = sysInfo.la
|
||||
|
||||
// use legacy load averages if agent version is less than 12.1.0
|
||||
if (!loadAverages || (minor === 12 && patch < 1)) {
|
||||
loadAverages = [sysInfo.l1 ?? 0, sysInfo.l5 ?? 0, sysInfo.l15 ?? 0]
|
||||
}
|
||||
|
||||
const max = Math.max(...loadAverages)
|
||||
if (max === 0 && (status === "paused" || minor < 12)) {
|
||||
return null
|
||||
}
|
||||
|
||||
const normalizedLoad = max / (sysInfo.t ?? 1)
|
||||
const threshold = getMeterState(normalizedLoad * 100)
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-[.35em] w-full tabular-nums tracking-tight">
|
||||
<span
|
||||
className={cn("inline-block size-2 rounded-full me-0.5", {
|
||||
[STATUS_COLORS.up]: threshold === MeterState.Good,
|
||||
[STATUS_COLORS.pending]: threshold === MeterState.Warn,
|
||||
[STATUS_COLORS.down]: threshold === MeterState.Crit,
|
||||
[STATUS_COLORS.paused]: status !== "up",
|
||||
})}
|
||||
/>
|
||||
{loadAverages?.map((la, i) => (
|
||||
<span key={i}>{decimalString(la, la >= 10 ? 1 : 2)}</span>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorFn: ({ info }) => info.bb || (info.b || 0) * 1024 * 1024,
|
||||
id: "net",
|
||||
name: () => t`Net`,
|
||||
size: 0,
|
||||
Icon: EthernetIcon,
|
||||
header: sortableHeader,
|
||||
cell(info) {
|
||||
const sys = info.row.original
|
||||
const userSettings = useStore($userSettings, { keys: ["unitNet"] })
|
||||
if (sys.status === "paused") {
|
||||
return null
|
||||
}
|
||||
const { value, unit } = formatBytes(info.getValue() as number, true, userSettings.unitNet, false)
|
||||
return (
|
||||
<span className="tabular-nums whitespace-nowrap">
|
||||
{decimalString(value, value >= 100 ? 1 : 2)} {unit}
|
||||
</span>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorFn: ({ info }) => info.dt,
|
||||
id: "temp",
|
||||
name: () => t({ message: "Temp", comment: "Temperature label in systems table" }),
|
||||
size: 50,
|
||||
hideSort: true,
|
||||
Icon: ThermometerIcon,
|
||||
header: sortableHeader,
|
||||
cell(info) {
|
||||
const val = info.getValue() as number
|
||||
const userSettings = useStore($userSettings, { keys: ["unitTemp"] })
|
||||
if (!val) {
|
||||
return null
|
||||
}
|
||||
const { value, unit } = formatTemperature(val, userSettings.unitTemp)
|
||||
return (
|
||||
<span className={cn("tabular-nums whitespace-nowrap", viewMode === "table" && "ps-0.5")}>
|
||||
{decimalString(value, value >= 100 ? 1 : 2)} {unit}
|
||||
</span>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorFn: ({ info }) => info.v,
|
||||
id: "agent",
|
||||
name: () => t`Agent`,
|
||||
// invertSorting: true,
|
||||
size: 50,
|
||||
Icon: WifiIcon,
|
||||
hideSort: true,
|
||||
header: sortableHeader,
|
||||
cell(info) {
|
||||
const version = info.getValue() as string
|
||||
if (!version) {
|
||||
return null
|
||||
}
|
||||
const system = info.row.original
|
||||
return (
|
||||
<span className={cn("flex gap-2 items-center md:pe-5 tabular-nums", viewMode === "table" && "ps-0.5")}>
|
||||
<IndicatorDot
|
||||
system={system}
|
||||
className={
|
||||
(system.status !== "up" && STATUS_COLORS.paused) ||
|
||||
(version === globalThis.BESZEL.HUB_VERSION && STATUS_COLORS.up) ||
|
||||
STATUS_COLORS.pending
|
||||
}
|
||||
/>
|
||||
<span className="truncate max-w-14">{info.getValue() as string}</span>
|
||||
</span>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "actions",
|
||||
// @ts-ignore
|
||||
name: () => t({ message: "Actions", comment: "Table column" }),
|
||||
size: 50,
|
||||
cell: ({ row }) => (
|
||||
<div className="flex justify-end items-center gap-1 -ms-3">
|
||||
<AlertButton system={row.original} />
|
||||
<ActionsButton system={row.original} />
|
||||
</div>
|
||||
),
|
||||
},
|
||||
] as ColumnDef<SystemRecord>[]
|
||||
}
|
||||
|
||||
function sortableHeader(context: HeaderContext<SystemRecord, unknown>) {
|
||||
const { column } = context
|
||||
// @ts-ignore
|
||||
const { Icon, hideSort, name }: { Icon: React.ElementType; name: () => string; hideSort: boolean } = column.columnDef
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="h-9 px-3 flex"
|
||||
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
||||
>
|
||||
{Icon && <Icon className="me-2 size-4" />}
|
||||
{name()}
|
||||
{hideSort || <ArrowUpDownIcon className="ms-2 size-4" />}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
function TableCellWithMeter(info: CellContext<SystemRecord, unknown>) {
|
||||
const val = Number(info.getValue()) || 0
|
||||
const threshold = getMeterState(val)
|
||||
return (
|
||||
<div className="flex gap-2 items-center tabular-nums tracking-tight">
|
||||
<span className="min-w-8">{decimalString(val, val >= 10 ? 1 : 2)}%</span>
|
||||
<span className="grow min-w-8 block bg-muted h-[1em] relative rounded-sm overflow-hidden">
|
||||
<span
|
||||
className={cn(
|
||||
"absolute inset-0 w-full h-full origin-left",
|
||||
(info.row.original.status !== "up" && STATUS_COLORS.paused) ||
|
||||
(threshold === MeterState.Good && STATUS_COLORS.up) ||
|
||||
(threshold === MeterState.Warn && STATUS_COLORS.pending) ||
|
||||
STATUS_COLORS.down
|
||||
)}
|
||||
style={{
|
||||
transform: `scalex(${val / 100})`,
|
||||
}}
|
||||
></span>
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function IndicatorDot({ system, className }: { system: SystemRecord; className?: ClassValue }) {
|
||||
className ||= STATUS_COLORS[system.status as keyof typeof STATUS_COLORS] || ""
|
||||
return (
|
||||
<span
|
||||
className={cn("flex-shrink-0 size-2 rounded-full", className)}
|
||||
// style={{ marginBottom: "-1px" }}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export const ActionsButton = memo(({ system }: { system: SystemRecord }) => {
|
||||
const [deleteOpen, setDeleteOpen] = useState(false)
|
||||
const [editOpen, setEditOpen] = useState(false)
|
||||
let editOpened = useRef(false)
|
||||
const { t } = useLingui()
|
||||
const { id, status, host, name } = system
|
||||
|
||||
return useMemo(() => {
|
||||
return (
|
||||
<>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost" size={"icon"} data-nolink>
|
||||
<span className="sr-only">
|
||||
<Trans>Open menu</Trans>
|
||||
</span>
|
||||
<MoreHorizontalIcon className="w-5" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
{!isReadOnlyUser() && (
|
||||
<DropdownMenuItem
|
||||
onSelect={() => {
|
||||
editOpened.current = true
|
||||
setEditOpen(true)
|
||||
}}
|
||||
>
|
||||
<PenBoxIcon className="me-2.5 size-4" />
|
||||
<Trans>Edit</Trans>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
<DropdownMenuItem
|
||||
className={cn(isReadOnlyUser() && "hidden")}
|
||||
onClick={() => {
|
||||
pb.collection("systems").update(id, {
|
||||
status: status === "paused" ? "pending" : "paused",
|
||||
})
|
||||
}}
|
||||
>
|
||||
{status === "paused" ? (
|
||||
<>
|
||||
<PlayCircleIcon className="me-2.5 size-4" />
|
||||
<Trans>Resume</Trans>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<PauseCircleIcon className="me-2.5 size-4" />
|
||||
<Trans>Pause</Trans>
|
||||
</>
|
||||
)}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => copyToClipboard(name)}>
|
||||
<CopyIcon className="me-2.5 size-4" />
|
||||
<Trans>Copy name</Trans>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => copyToClipboard(host)}>
|
||||
<CopyIcon className="me-2.5 size-4" />
|
||||
<Trans>Copy host</Trans>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator className={cn(isReadOnlyUser() && "hidden")} />
|
||||
<DropdownMenuItem className={cn(isReadOnlyUser() && "hidden")} onSelect={() => setDeleteOpen(true)}>
|
||||
<Trash2Icon className="me-2.5 size-4" />
|
||||
<Trans>Delete</Trans>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
{/* edit dialog */}
|
||||
<Dialog open={editOpen} onOpenChange={setEditOpen}>
|
||||
{editOpened.current && <SystemDialog system={system} setOpen={setEditOpen} />}
|
||||
</Dialog>
|
||||
{/* deletion dialog */}
|
||||
<AlertDialog open={deleteOpen} onOpenChange={(open) => setDeleteOpen(open)}>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>
|
||||
<Trans>Are you sure you want to delete {name}?</Trans>
|
||||
</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
<Trans>
|
||||
This action cannot be undone. This will permanently delete all current records for {name} from the
|
||||
database.
|
||||
</Trans>
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>
|
||||
<Trans>Cancel</Trans>
|
||||
</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
className={cn(buttonVariants({ variant: "destructive" }))}
|
||||
onClick={() => pb.collection("systems").delete(id)}
|
||||
>
|
||||
<Trans>Continue</Trans>
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
</>
|
||||
)
|
||||
}, [id, status, host, name, t, deleteOpen, editOpen])
|
||||
})
|
||||
@@ -1,5 +1,4 @@
|
||||
import {
|
||||
CellContext,
|
||||
ColumnDef,
|
||||
ColumnFiltersState,
|
||||
getFilteredRowModel,
|
||||
@@ -9,14 +8,13 @@ import {
|
||||
VisibilityState,
|
||||
getCoreRowModel,
|
||||
useReactTable,
|
||||
HeaderContext,
|
||||
Row,
|
||||
Table as TableType,
|
||||
} from "@tanstack/react-table"
|
||||
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
|
||||
|
||||
import { Button, buttonVariants } from "@/components/ui/button"
|
||||
import { Button } from "@/components/ui/button"
|
||||
|
||||
import {
|
||||
DropdownMenu,
|
||||
@@ -29,105 +27,30 @@ import {
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu"
|
||||
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
} from "@/components/ui/alert-dialog"
|
||||
|
||||
import { SystemRecord } from "@/types"
|
||||
import {
|
||||
MoreHorizontalIcon,
|
||||
ArrowUpDownIcon,
|
||||
MemoryStickIcon,
|
||||
CopyIcon,
|
||||
PauseCircleIcon,
|
||||
PlayCircleIcon,
|
||||
Trash2Icon,
|
||||
WifiIcon,
|
||||
HardDriveIcon,
|
||||
ServerIcon,
|
||||
CpuIcon,
|
||||
LayoutGridIcon,
|
||||
LayoutListIcon,
|
||||
ArrowDownIcon,
|
||||
ArrowUpIcon,
|
||||
Settings2Icon,
|
||||
EyeIcon,
|
||||
PenBoxIcon,
|
||||
} from "lucide-react"
|
||||
import { memo, useEffect, useMemo, useRef, useState } from "react"
|
||||
import { $systems, $userSettings, pb } from "@/lib/stores"
|
||||
import { memo, useEffect, useMemo, useState } from "react"
|
||||
import { $systems } from "@/lib/stores"
|
||||
import { useStore } from "@nanostores/react"
|
||||
import {
|
||||
cn,
|
||||
copyToClipboard,
|
||||
isReadOnlyUser,
|
||||
useLocalStorage,
|
||||
formatTemperature,
|
||||
decimalString,
|
||||
formatBytes,
|
||||
} from "@/lib/utils"
|
||||
import AlertsButton from "../alerts/alert-button"
|
||||
import { cn, useLocalStorage } from "@/lib/utils"
|
||||
import { $router, Link, navigate } from "../router"
|
||||
import { EthernetIcon, GpuIcon, HourglassIcon, ThermometerIcon } from "../ui/icons"
|
||||
import { useLingui, Trans } from "@lingui/react/macro"
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../ui/card"
|
||||
import { Input } from "../ui/input"
|
||||
import { ClassValue } from "clsx"
|
||||
import { getPagePath } from "@nanostores/router"
|
||||
import { SystemDialog } from "../add-system"
|
||||
import { Dialog } from "../ui/dialog"
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "../ui/tooltip"
|
||||
import SystemsTableColumns, { ActionsButton, IndicatorDot } from "./systems-table-columns"
|
||||
import AlertButton from "../alerts/alert-button"
|
||||
|
||||
type ViewMode = "table" | "grid"
|
||||
|
||||
function CellFormatter(info: CellContext<SystemRecord, unknown>) {
|
||||
const val = (info.getValue() as number) || 0
|
||||
return (
|
||||
<div className="flex gap-2 items-center tabular-nums tracking-tight">
|
||||
<span className="min-w-8">{decimalString(val, 1)}%</span>
|
||||
<span className="grow min-w-8 block bg-muted h-[1em] relative rounded-sm overflow-hidden">
|
||||
<span
|
||||
className={cn(
|
||||
"absolute inset-0 w-full h-full origin-left",
|
||||
(info.row.original.status !== "up" && "bg-primary/30") ||
|
||||
(val < 65 && "bg-green-500") ||
|
||||
(val < 90 && "bg-yellow-500") ||
|
||||
"bg-red-600"
|
||||
)}
|
||||
style={{
|
||||
transform: `scalex(${val / 100})`,
|
||||
}}
|
||||
></span>
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function sortableHeader(context: HeaderContext<SystemRecord, unknown>) {
|
||||
const { column } = context
|
||||
// @ts-ignore
|
||||
const { Icon, hideSort, name }: { Icon: React.ElementType; name: () => string; hideSort: boolean } = column.columnDef
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="h-9 px-3 flex"
|
||||
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
||||
>
|
||||
{Icon && <Icon className="me-2 size-4" />}
|
||||
{name()}
|
||||
{hideSort || <ArrowUpDownIcon className="ms-2 size-4" />}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
export default function SystemsTable() {
|
||||
const data = useStore($systems)
|
||||
const { i18n, t } = useLingui()
|
||||
@@ -145,224 +68,7 @@ export default function SystemsTable() {
|
||||
}
|
||||
}, [filter])
|
||||
|
||||
const columnDefs = useMemo(() => {
|
||||
const statusTranslations = {
|
||||
up: () => t`Up`.toLowerCase(),
|
||||
down: () => t`Down`.toLowerCase(),
|
||||
paused: () => t`Paused`.toLowerCase(),
|
||||
}
|
||||
return [
|
||||
{
|
||||
size: 200,
|
||||
minSize: 0,
|
||||
accessorKey: "name",
|
||||
id: "system",
|
||||
name: () => t`System`,
|
||||
filterFn: (row, _, filterVal) => {
|
||||
const filterLower = filterVal.toLowerCase()
|
||||
const { name, status } = row.original
|
||||
// Check if the filter matches the name or status for this row
|
||||
if (
|
||||
name.toLowerCase().includes(filterLower) ||
|
||||
statusTranslations[status as keyof typeof statusTranslations]?.().includes(filterLower)
|
||||
) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
enableHiding: false,
|
||||
invertSorting: false,
|
||||
Icon: ServerIcon,
|
||||
cell: (info) => (
|
||||
<span className="flex gap-0.5 items-center text-base md:pe-5">
|
||||
<IndicatorDot system={info.row.original} />
|
||||
<Button
|
||||
data-nolink
|
||||
variant={"ghost"}
|
||||
className="text-primary/90 h-7 px-1.5 gap-1.5"
|
||||
onClick={() => copyToClipboard(info.getValue() as string)}
|
||||
>
|
||||
{info.getValue() as string}
|
||||
<CopyIcon className="h-2.5 w-2.5" />
|
||||
</Button>
|
||||
</span>
|
||||
),
|
||||
header: sortableHeader,
|
||||
},
|
||||
{
|
||||
accessorFn: (originalRow) => originalRow.info.cpu,
|
||||
id: "cpu",
|
||||
name: () => t`CPU`,
|
||||
cell: CellFormatter,
|
||||
Icon: CpuIcon,
|
||||
header: sortableHeader,
|
||||
},
|
||||
{
|
||||
// accessorKey: "info.mp",
|
||||
accessorFn: (originalRow) => originalRow.info.mp,
|
||||
id: "memory",
|
||||
name: () => t`Memory`,
|
||||
cell: CellFormatter,
|
||||
Icon: MemoryStickIcon,
|
||||
header: sortableHeader,
|
||||
},
|
||||
{
|
||||
accessorFn: (originalRow) => originalRow.info.dp,
|
||||
id: "disk",
|
||||
name: () => t`Disk`,
|
||||
cell: CellFormatter,
|
||||
Icon: HardDriveIcon,
|
||||
header: sortableHeader,
|
||||
},
|
||||
{
|
||||
accessorFn: (originalRow) => originalRow.info.g,
|
||||
id: "gpu",
|
||||
name: () => "GPU",
|
||||
cell: CellFormatter,
|
||||
Icon: GpuIcon,
|
||||
header: sortableHeader,
|
||||
},
|
||||
{
|
||||
accessorFn: (originalRow) => originalRow.info.b || 0,
|
||||
id: "net",
|
||||
name: () => t`Net`,
|
||||
size: 0,
|
||||
Icon: EthernetIcon,
|
||||
header: sortableHeader,
|
||||
cell(info) {
|
||||
const userSettings = useStore($userSettings)
|
||||
const { value, unit } = formatBytes(info.getValue() as number, true, userSettings.unitNet, true)
|
||||
return (
|
||||
<span className="tabular-nums whitespace-nowrap">
|
||||
{decimalString(value, value >= 100 ? 1 : 2)} {unit}
|
||||
</span>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "loadAverage",
|
||||
name: () => t`Load Average`,
|
||||
size: 0,
|
||||
hideSort: true,
|
||||
Icon: HourglassIcon,
|
||||
header: sortableHeader,
|
||||
cell(info: CellContext<SystemRecord, unknown>) {
|
||||
const system = info.row.original;
|
||||
const l1 = system.info?.l1;
|
||||
const l5 = system.info?.l5;
|
||||
const l15 = system.info?.l15;
|
||||
const cores = system.info?.c || 1;
|
||||
|
||||
// If no load average data, return null
|
||||
if (!l1 && !l5 && !l15) return null;
|
||||
|
||||
const loadAverages = [
|
||||
{ name: "1m", value: l1 },
|
||||
{ name: "5m", value: l5 },
|
||||
{ name: "15m", value: l15 }
|
||||
].filter(la => la.value !== undefined);
|
||||
|
||||
if (!loadAverages.length) return null;
|
||||
|
||||
function getDotColor(value: number) {
|
||||
const normalized = value / cores;
|
||||
if (normalized < 0.7) return "bg-green-500";
|
||||
if (normalized < 1.0) return "bg-orange-500";
|
||||
return "bg-red-600";
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-2 w-full">
|
||||
{loadAverages.map((la, idx) => (
|
||||
<TooltipProvider key={la.name}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<span className="flex items-center cursor-pointer">
|
||||
<span className={cn("inline-block w-2 h-2 rounded-full mr-1", getDotColor(la.value || 0))} />
|
||||
<span className="tabular-nums">
|
||||
{decimalString(la.value || 0, 2)}
|
||||
</span>
|
||||
{idx < loadAverages.length - 1 && <span className="mx-1 text-muted-foreground">/</span>}
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="top">
|
||||
<div className="text-center">
|
||||
<div className="font-medium">{t`${la.name}`}</div>
|
||||
</div>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorFn: (originalRow) => originalRow.info.dt,
|
||||
id: "temp",
|
||||
name: () => t({ message: "Temp", comment: "Temperature label in systems table" }),
|
||||
size: 50,
|
||||
hideSort: true,
|
||||
Icon: ThermometerIcon,
|
||||
header: sortableHeader,
|
||||
cell(info) {
|
||||
const val = info.getValue() as number
|
||||
if (!val) {
|
||||
return null
|
||||
}
|
||||
const userSettings = useStore($userSettings)
|
||||
const { value, unit } = formatTemperature(val, userSettings.unitTemp)
|
||||
return (
|
||||
<span className={cn("tabular-nums whitespace-nowrap", viewMode === "table" && "ps-0.5")}>
|
||||
{decimalString(value, value >= 100 ? 1 : 2)} {unit}
|
||||
</span>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorFn: (originalRow) => originalRow.info.v,
|
||||
id: "agent",
|
||||
name: () => t`Agent`,
|
||||
// invertSorting: true,
|
||||
size: 50,
|
||||
Icon: WifiIcon,
|
||||
hideSort: true,
|
||||
header: sortableHeader,
|
||||
cell(info) {
|
||||
const version = info.getValue() as string
|
||||
if (!version) {
|
||||
return null
|
||||
}
|
||||
const system = info.row.original
|
||||
return (
|
||||
<span className={cn("flex gap-2 items-center md:pe-5 tabular-nums", viewMode === "table" && "ps-0.5")}>
|
||||
<IndicatorDot
|
||||
system={system}
|
||||
className={
|
||||
(system.status !== "up" && "bg-primary/30") ||
|
||||
(version === globalThis.BESZEL.HUB_VERSION && "bg-green-500") ||
|
||||
"bg-yellow-500"
|
||||
}
|
||||
/>
|
||||
<span className="truncate max-w-14">{info.getValue() as string}</span>
|
||||
</span>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "actions",
|
||||
// @ts-ignore
|
||||
name: () => t({ message: "Actions", comment: "Table column" }),
|
||||
size: 50,
|
||||
cell: ({ row }) => (
|
||||
<div className="flex justify-end items-center gap-1 -ms-3">
|
||||
<AlertsButton system={row.original} />
|
||||
<ActionsButton system={row.original} />
|
||||
</div>
|
||||
),
|
||||
},
|
||||
] as ColumnDef<SystemRecord>[]
|
||||
}, [])
|
||||
const columnDefs = useMemo(() => SystemsTableColumns(viewMode), [viewMode])
|
||||
|
||||
const table = useReactTable({
|
||||
data,
|
||||
@@ -565,7 +271,7 @@ function SystemsTableHead({ table, colLength }: { table: TableType<SystemRecord>
|
||||
<TableRow key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => {
|
||||
return (
|
||||
<TableHead className="px-1" key={header.id}>
|
||||
<TableHead className="px-1.5" key={header.id}>
|
||||
{flexRender(header.column.columnDef.header, header.getContext())}
|
||||
</TableHead>
|
||||
)
|
||||
@@ -640,7 +346,7 @@ const SystemCard = memo(
|
||||
</CardTitle>
|
||||
{table.getColumn("actions")?.getIsVisible() && (
|
||||
<div className="flex gap-1 flex-shrink-0 relative z-10">
|
||||
<AlertsButton system={system} />
|
||||
<AlertButton system={system} />
|
||||
<ActionsButton system={system} />
|
||||
</div>
|
||||
)}
|
||||
@@ -675,116 +381,3 @@ const SystemCard = memo(
|
||||
}, [system, colLength, t])
|
||||
}
|
||||
)
|
||||
|
||||
const ActionsButton = memo(({ system }: { system: SystemRecord }) => {
|
||||
const [deleteOpen, setDeleteOpen] = useState(false)
|
||||
const [editOpen, setEditOpen] = useState(false)
|
||||
let editOpened = useRef(false)
|
||||
const { t } = useLingui()
|
||||
const { id, status, host, name } = system
|
||||
|
||||
return useMemo(() => {
|
||||
return (
|
||||
<>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost" size={"icon"} data-nolink>
|
||||
<span className="sr-only">
|
||||
<Trans>Open menu</Trans>
|
||||
</span>
|
||||
<MoreHorizontalIcon className="w-5" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
{!isReadOnlyUser() && (
|
||||
<DropdownMenuItem
|
||||
onSelect={() => {
|
||||
editOpened.current = true
|
||||
setEditOpen(true)
|
||||
}}
|
||||
>
|
||||
<PenBoxIcon className="me-2.5 size-4" />
|
||||
<Trans>Edit</Trans>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
<DropdownMenuItem
|
||||
className={cn(isReadOnlyUser() && "hidden")}
|
||||
onClick={() => {
|
||||
pb.collection("systems").update(id, {
|
||||
status: status === "paused" ? "pending" : "paused",
|
||||
})
|
||||
}}
|
||||
>
|
||||
{status === "paused" ? (
|
||||
<>
|
||||
<PlayCircleIcon className="me-2.5 size-4" />
|
||||
<Trans>Resume</Trans>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<PauseCircleIcon className="me-2.5 size-4" />
|
||||
<Trans>Pause</Trans>
|
||||
</>
|
||||
)}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => copyToClipboard(host)}>
|
||||
<CopyIcon className="me-2.5 size-4" />
|
||||
<Trans>Copy host</Trans>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator className={cn(isReadOnlyUser() && "hidden")} />
|
||||
<DropdownMenuItem className={cn(isReadOnlyUser() && "hidden")} onSelect={() => setDeleteOpen(true)}>
|
||||
<Trash2Icon className="me-2.5 size-4" />
|
||||
<Trans>Delete</Trans>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
{/* edit dialog */}
|
||||
<Dialog open={editOpen} onOpenChange={setEditOpen}>
|
||||
{editOpened.current && <SystemDialog system={system} setOpen={setEditOpen} />}
|
||||
</Dialog>
|
||||
{/* deletion dialog */}
|
||||
<AlertDialog open={deleteOpen} onOpenChange={(open) => setDeleteOpen(open)}>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>
|
||||
<Trans>Are you sure you want to delete {name}?</Trans>
|
||||
</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
<Trans>
|
||||
This action cannot be undone. This will permanently delete all current records for {name} from the
|
||||
database.
|
||||
</Trans>
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>
|
||||
<Trans>Cancel</Trans>
|
||||
</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
className={cn(buttonVariants({ variant: "destructive" }))}
|
||||
onClick={() => pb.collection("systems").delete(id)}
|
||||
>
|
||||
<Trans>Continue</Trans>
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
</>
|
||||
)
|
||||
}, [id, status, host, name, t, deleteOpen, editOpen])
|
||||
})
|
||||
|
||||
function IndicatorDot({ system, className }: { system: SystemRecord; className?: ClassValue }) {
|
||||
className ||= {
|
||||
"bg-green-500": system.status === "up",
|
||||
"bg-red-500": system.status === "down",
|
||||
"bg-primary/40": system.status === "paused",
|
||||
"bg-yellow-500": system.status === "pending",
|
||||
}
|
||||
return (
|
||||
<span
|
||||
className={cn("flex-shrink-0 size-2 rounded-full", className)}
|
||||
// style={{ marginBottom: "-1px" }}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ const buttonVariants = cva(
|
||||
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
||||
outline: "border bg-background hover:bg-accent/70 dark:hover:bg-accent/50 hover:text-accent-foreground",
|
||||
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||
ghost: "hover:bg-accent hover:text-accent-foreground",
|
||||
ghost: "hover:bg-accent/70 hover:text-accent-foreground",
|
||||
link: "text-primary underline-offset-4 hover:underline",
|
||||
},
|
||||
size: {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
|
||||
import { Check } from "lucide-react"
|
||||
@@ -11,13 +13,14 @@ const Checkbox = React.forwardRef<
|
||||
<CheckboxPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"peer h-4 w-4 shrink-0 rounded-[.3em] border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
|
||||
"peer size-4 flex items-center justify-center shrink-0 rounded-[.3em] border border-input ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
|
||||
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<CheckboxPrimitive.Indicator className={cn("flex items-center justify-center text-current")}>
|
||||
<Check className="h-4 w-4" />
|
||||
<Check className="size-4" />
|
||||
</CheckboxPrimitive.Indicator>
|
||||
</CheckboxPrimitive.Root>
|
||||
))
|
||||
|
||||
@@ -105,7 +105,7 @@ const CommandItem = React.forwardRef<
|
||||
<CommandPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative flex cursor-default opacity-70 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none aria-selected:bg-accent aria-selected:opacity-90 data-[disabled='true']:pointer-events-none data-[disabled='true']:opacity-50",
|
||||
"relative flex cursor-default opacity-70 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none aria-selected:bg-accent/70 aria-selected:opacity-90 data-[disabled='true']:pointer-events-none data-[disabled='true']:opacity-50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -25,7 +25,7 @@ const DropdownMenuSubTrigger = React.forwardRef<
|
||||
<DropdownMenuPrimitive.SubTrigger
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex cursor-default select-none items-center rounded-sm px-2.5 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent",
|
||||
"flex cursor-default select-none items-center rounded-sm px-2.5 py-1.5 text-sm outline-none focus:bg-accent/70 data-[state=open]:bg-accent/70",
|
||||
inset && "ps-8",
|
||||
className
|
||||
)}
|
||||
@@ -79,7 +79,7 @@ const DropdownMenuItem = React.forwardRef<
|
||||
<DropdownMenuPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative flex cursor-default select-none items-center rounded-sm px-2.5 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
"relative flex cursor-default select-none items-center rounded-sm px-2.5 py-1.5 text-sm outline-none focus:bg-accent/70 focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
inset && "ps-8",
|
||||
className
|
||||
)}
|
||||
@@ -95,7 +95,7 @@ const DropdownMenuCheckboxItem = React.forwardRef<
|
||||
<DropdownMenuPrimitive.CheckboxItem
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative flex cursor-default select-none items-center rounded-sm py-1.5 ps-8 pe-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
"relative flex cursor-default select-none items-center rounded-sm py-1.5 ps-8 pe-2 text-sm outline-none focus:bg-accent/70 focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
className
|
||||
)}
|
||||
checked={checked}
|
||||
@@ -118,7 +118,7 @@ const DropdownMenuRadioItem = React.forwardRef<
|
||||
<DropdownMenuPrimitive.RadioItem
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative flex cursor-default select-none items-center rounded-sm py-1.5 ps-8 pe-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
"relative flex cursor-default select-none items-center rounded-sm py-1.5 ps-8 pe-2 text-sm outline-none focus:bg-accent/70 focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -105,7 +105,7 @@ const SelectItem = React.forwardRef<
|
||||
<SelectPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 ps-8 pe-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 ps-8 pe-2 text-sm outline-none focus:bg-accent/70 focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -37,7 +37,7 @@ const TableRow = React.forwardRef<HTMLTableRowElement, React.HTMLAttributes<HTML
|
||||
<tr
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"border-b border-border/60 hover:bg-muted/40 dark:hover:bg-muted/20 data-[state=selected]:bg-muted",
|
||||
"border-b border-border/60 hover:bg-muted/40 dark:hover:bg-muted/20 data-[state=selected]:!bg-muted",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
--background: 30 8% 98.5%;
|
||||
--background: 30 8% 98%;
|
||||
--foreground: 30 0% 0%;
|
||||
--card: 30 0% 100%;
|
||||
--card-foreground: 240 6.67% 2.94%;
|
||||
|
||||
@@ -21,3 +21,10 @@ export enum Unit {
|
||||
Celsius,
|
||||
Fahrenheit,
|
||||
}
|
||||
|
||||
/** Meter state for color */
|
||||
export enum MeterState {
|
||||
Good,
|
||||
Warn,
|
||||
Crit,
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import PocketBase from "pocketbase"
|
||||
import { atom, map, PreinitializedWritableAtom } from "nanostores"
|
||||
import { AlertRecord, ChartTimes, SystemRecord, UserSettings } from "@/types"
|
||||
import { atom, map } from "nanostores"
|
||||
import { AlertMap, ChartTimes, SystemRecord, UserSettings } from "@/types"
|
||||
import { basePath } from "@/components/router"
|
||||
import { Unit } from "./enums"
|
||||
|
||||
/** PocketBase JS Client */
|
||||
export const pb = new PocketBase(basePath)
|
||||
@@ -10,33 +11,40 @@ export const pb = new PocketBase(basePath)
|
||||
export const $authenticated = atom(pb.authStore.isValid)
|
||||
|
||||
/** List of system records */
|
||||
export const $systems = atom([] as SystemRecord[])
|
||||
export const $systems = atom<SystemRecord[]>([])
|
||||
|
||||
/** List of alert records */
|
||||
export const $alerts = atom([] as AlertRecord[])
|
||||
/** Map of alert records by system id and alert name */
|
||||
export const $alerts = map<AlertMap>({})
|
||||
|
||||
/** SSH public key */
|
||||
export const $publicKey = atom("")
|
||||
|
||||
/** Chart time period */
|
||||
export const $chartTime = atom("1h") as PreinitializedWritableAtom<ChartTimes>
|
||||
export const $chartTime = atom<ChartTimes>("1h")
|
||||
|
||||
/** Whether to display average or max chart values */
|
||||
export const $maxValues = atom(false)
|
||||
|
||||
// export const UserSettingsSchema = v.object({
|
||||
// chartTime: v.picklist(["1h", "12h", "24h", "1w", "30d"]),
|
||||
// emails: v.optional(v.array(v.pipe(v.string(), v.email())), [pb?.authStore?.record?.email ?? ""]),
|
||||
// webhooks: v.optional(v.array(v.string())),
|
||||
// colorWarn: v.optional(v.pipe(v.number(), v.minValue(1), v.maxValue(100))),
|
||||
// colorDanger: v.optional(v.pipe(v.number(), v.minValue(1), v.maxValue(100))),
|
||||
// unitTemp: v.optional(v.enum(Unit)),
|
||||
// unitNet: v.optional(v.enum(Unit)),
|
||||
// unitDisk: v.optional(v.enum(Unit)),
|
||||
// })
|
||||
|
||||
/** User settings */
|
||||
export const $userSettings = map<UserSettings>({
|
||||
chartTime: "1h",
|
||||
emails: [pb.authStore.record?.email || ""],
|
||||
// unitTemp: "celsius",
|
||||
// unitNet: "mbps",
|
||||
// unitDisk: "mbps",
|
||||
})
|
||||
// update local storage on change
|
||||
$userSettings.subscribe((value) => {
|
||||
// console.log('user settings changed', value)
|
||||
$chartTime.set(value.chartTime)
|
||||
unitNet: Unit.Bytes,
|
||||
unitTemp: Unit.Celsius,
|
||||
})
|
||||
// update chart time on change
|
||||
$userSettings.subscribe((value) => $chartTime.set(value.chartTime))
|
||||
|
||||
/** Container chart filter */
|
||||
export const $containerFilter = atom("")
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
ChartTimeData,
|
||||
ChartTimes,
|
||||
FingerprintRecord,
|
||||
SemVer,
|
||||
SystemRecord,
|
||||
UserSettings,
|
||||
} from "@/types"
|
||||
@@ -19,7 +20,7 @@ import { useEffect, useState } from "react"
|
||||
import { CpuIcon, HardDriveIcon, MemoryStickIcon, ServerIcon } from "lucide-react"
|
||||
import { EthernetIcon, HourglassIcon, ThermometerIcon } from "@/components/ui/icons"
|
||||
import { prependBasePath } from "@/components/router"
|
||||
import { Unit } from "./enums"
|
||||
import { MeterState, Unit } from "./enums"
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
@@ -83,21 +84,13 @@ export const updateSystemList = (() => {
|
||||
/** Logs the user out by clearing the auth store and unsubscribing from realtime updates. */
|
||||
export async function logOut() {
|
||||
$systems.set([])
|
||||
$alerts.set([])
|
||||
$alerts.set({})
|
||||
$userSettings.set({} as UserSettings)
|
||||
sessionStorage.setItem("lo", "t") // prevent auto login on logout
|
||||
pb.authStore.clear()
|
||||
pb.realtime.unsubscribe()
|
||||
}
|
||||
|
||||
export const updateAlerts = () => {
|
||||
pb.collection("alerts")
|
||||
.getFullList<AlertRecord>({ fields: "id,name,system,value,min,triggered", sort: "updated" })
|
||||
.then((records) => {
|
||||
$alerts.set(records)
|
||||
})
|
||||
}
|
||||
|
||||
const hourWithMinutesFormatter = new Intl.DateTimeFormat(undefined, {
|
||||
hour: "numeric",
|
||||
minute: "numeric",
|
||||
@@ -367,6 +360,7 @@ export async function updateUserSettings() {
|
||||
|
||||
export const chartMargin = { top: 12 }
|
||||
|
||||
/** Alert info for each alert type */
|
||||
export const alertInfo: Record<string, AlertInfo> = {
|
||||
Status: {
|
||||
name: () => t`Status`,
|
||||
@@ -437,7 +431,7 @@ export const alertInfo: Record<string, AlertInfo> = {
|
||||
step: 0.1,
|
||||
desc: () => t`Triggers when 15 minute load average exceeds a threshold`,
|
||||
},
|
||||
}
|
||||
} as const
|
||||
|
||||
/**
|
||||
* Retuns value of system host, truncating full path if socket.
|
||||
@@ -456,26 +450,158 @@ export const getHubURL = () => BESZEL?.HUB_URL || window.location.origin
|
||||
/** Map of system IDs to their corresponding tokens (used to avoid fetching in add-system dialog) */
|
||||
export const tokenMap = new Map<SystemRecord["id"], FingerprintRecord["token"]>()
|
||||
|
||||
/**
|
||||
* Calculate load average percentage relative to CPU cores
|
||||
* @param loadAverage - The load average value (1m, 5m, or 15m)
|
||||
* @param cores - Number of CPU cores
|
||||
* @returns Percentage (0-100) representing CPU utilization
|
||||
*/
|
||||
export const calculateLoadAveragePercent = (loadAverage: number, cores: number): number => {
|
||||
if (!loadAverage || !cores) return 0
|
||||
return Math.min((loadAverage / cores) * 100, 100)
|
||||
/** Calculate duration between two dates and format as human-readable string */
|
||||
export function formatDuration(
|
||||
createdDate: string | null | undefined,
|
||||
resolvedDate: string | null | undefined
|
||||
): string {
|
||||
const created = createdDate ? new Date(createdDate) : null
|
||||
const resolved = resolvedDate ? new Date(resolvedDate) : null
|
||||
|
||||
if (!created || !resolved) return ""
|
||||
|
||||
const diffMs = resolved.getTime() - created.getTime()
|
||||
if (diffMs < 0) return ""
|
||||
|
||||
const totalSeconds = Math.floor(diffMs / 1000)
|
||||
let hours = Math.floor(totalSeconds / 3600)
|
||||
let minutes = Math.floor((totalSeconds % 3600) / 60)
|
||||
let seconds = totalSeconds % 60
|
||||
|
||||
// if seconds are close to 60, round up to next minute
|
||||
// if minutes are close to 60, round up to next hour
|
||||
if (seconds >= 58) {
|
||||
minutes += 1
|
||||
seconds = 0
|
||||
}
|
||||
if (minutes >= 60) {
|
||||
hours += 1
|
||||
minutes = 0
|
||||
}
|
||||
|
||||
// For durations over 1 hour, omit seconds for cleaner display
|
||||
if (hours > 0) {
|
||||
return [hours ? `${hours}h` : null, minutes ? `${minutes}m` : null].filter(Boolean).join(" ")
|
||||
}
|
||||
|
||||
return [hours ? `${hours}h` : null, minutes ? `${minutes}m` : null, seconds ? `${seconds}s` : null]
|
||||
.filter(Boolean)
|
||||
.join(" ")
|
||||
}
|
||||
|
||||
/**
|
||||
* Get load average opacity based on utilization relative to cores
|
||||
* @param loadAverage - The load average value
|
||||
* @param cores - Number of CPU cores
|
||||
* @returns Opacity value (0.6, 0.8, or 1.0)
|
||||
*/
|
||||
export const getLoadAverageOpacity = (loadAverage: number, cores: number): number => {
|
||||
if (!loadAverage || !cores) return 0.6
|
||||
if (loadAverage < cores * 0.5) return 0.6
|
||||
if (loadAverage < cores) return 0.8
|
||||
return 1.0
|
||||
}
|
||||
export const parseSemVer = (semVer = ""): SemVer => {
|
||||
// if (semVer.startsWith("v")) {
|
||||
// semVer = semVer.slice(1)
|
||||
// }
|
||||
if (semVer.includes("-")) {
|
||||
semVer = semVer.slice(0, semVer.indexOf("-"))
|
||||
}
|
||||
const parts = semVer.split(".").map(Number)
|
||||
return { major: parts?.[0] ?? 0, minor: parts?.[1] ?? 0, patch: parts?.[2] ?? 0 }
|
||||
}
|
||||
|
||||
/** Get meter state from 0-100 value. Used for color coding meters. */
|
||||
export function getMeterState(value: number): MeterState {
|
||||
const { colorWarn = 65, colorCrit = 90 } = $userSettings.get()
|
||||
return value >= colorCrit ? MeterState.Crit : value >= colorWarn ? MeterState.Warn : MeterState.Good
|
||||
}
|
||||
|
||||
export function debounce<T extends (...args: any[]) => any>(func: T, wait: number): (...args: Parameters<T>) => void {
|
||||
let timeout: ReturnType<typeof setTimeout>
|
||||
return (...args: Parameters<T>) => {
|
||||
clearTimeout(timeout)
|
||||
timeout = setTimeout(() => func(...args), wait)
|
||||
}
|
||||
}
|
||||
|
||||
/* returns the name of a system from its id */
|
||||
export const getSystemNameFromId = (() => {
|
||||
const cache = new Map<string, string>()
|
||||
return (systemId: string): string => {
|
||||
if (cache.has(systemId)) {
|
||||
return cache.get(systemId)!
|
||||
}
|
||||
const sysName = $systems.get().find((s) => s.id === systemId)?.name ?? ""
|
||||
cache.set(systemId, sysName)
|
||||
return sysName
|
||||
}
|
||||
})()
|
||||
|
||||
// TODO: reorganize this utils file into more specific files
|
||||
/** Helper to manage user alerts */
|
||||
export const alertManager = (() => {
|
||||
const collection = pb.collection<AlertRecord>("alerts")
|
||||
|
||||
/** Fields to fetch from alerts collection */
|
||||
const fields = "id,name,system,value,min,triggered"
|
||||
|
||||
/** Fetch alerts from collection */
|
||||
async function fetchAlerts(): Promise<AlertRecord[]> {
|
||||
return await collection.getFullList<AlertRecord>({ fields, sort: "updated" })
|
||||
}
|
||||
|
||||
/** Format alerts into a map of system id to alert name to alert record */
|
||||
function add(alerts: AlertRecord[]) {
|
||||
for (const alert of alerts) {
|
||||
const systemId = alert.system
|
||||
const systemAlerts = $alerts.get()[systemId] ?? new Map()
|
||||
const newAlerts = new Map(systemAlerts)
|
||||
newAlerts.set(alert.name, alert)
|
||||
$alerts.setKey(systemId, newAlerts)
|
||||
}
|
||||
}
|
||||
|
||||
function remove(alerts: Pick<AlertRecord, "name" | "system">[]) {
|
||||
for (const alert of alerts) {
|
||||
const systemId = alert.system
|
||||
const systemAlerts = $alerts.get()[systemId]
|
||||
const newAlerts = new Map(systemAlerts)
|
||||
newAlerts.delete(alert.name)
|
||||
$alerts.setKey(systemId, newAlerts)
|
||||
}
|
||||
}
|
||||
|
||||
const actionFns = {
|
||||
create: add,
|
||||
update: add,
|
||||
delete: remove,
|
||||
}
|
||||
|
||||
// batch alert updates to prevent unnecessary re-renders when adding many alerts at once
|
||||
const batchUpdate = (() => {
|
||||
const batch = new Map<string, RecordSubscription<AlertRecord>>()
|
||||
let timeout: ReturnType<typeof setTimeout>
|
||||
|
||||
return (data: RecordSubscription<AlertRecord>) => {
|
||||
const { record } = data
|
||||
batch.set(`${record.system}${record.name}`, data)
|
||||
clearTimeout(timeout!)
|
||||
timeout = setTimeout(() => {
|
||||
const groups = { create: [], update: [], delete: [] } as Record<string, AlertRecord[]>
|
||||
for (const { action, record } of batch.values()) {
|
||||
groups[action]?.push(record)
|
||||
}
|
||||
for (const key in groups) {
|
||||
if (groups[key].length) {
|
||||
actionFns[key as keyof typeof actionFns]?.(groups[key])
|
||||
}
|
||||
}
|
||||
batch.clear()
|
||||
}, 50)
|
||||
}
|
||||
})()
|
||||
|
||||
collection.subscribe("*", batchUpdate, { fields })
|
||||
|
||||
return {
|
||||
/** Add alerts to store */
|
||||
add,
|
||||
/** Remove alerts from store */
|
||||
remove,
|
||||
/** Refresh alerts with latest data from hub */
|
||||
async refresh() {
|
||||
const records = await fetchAlerts()
|
||||
add(records)
|
||||
},
|
||||
}
|
||||
})()
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: ar\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-03-06 07:27\n"
|
||||
"PO-Revision-Date: 2025-07-25 22:44\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"
|
||||
@@ -23,6 +23,12 @@ msgstr ""
|
||||
msgid "{0, plural, one {# day} other {# days}}"
|
||||
msgstr "{0, plural, one {# يوم} other {# أيام}}"
|
||||
|
||||
#. placeholder {0}: table.getFilteredSelectedRowModel().rows.length
|
||||
#. placeholder {1}: table.getFilteredRowModel().rows.length
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "{0} of {1} row(s) selected."
|
||||
msgstr "تم تحديد {0} من {1} صف"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "{hours, plural, one {# hour} other {# hours}}"
|
||||
msgstr "{hours, plural, one {# ساعة} other {# ساعات}}"
|
||||
@@ -31,6 +37,11 @@ msgstr "{hours, plural, one {# ساعة} other {# ساعات}}"
|
||||
msgid "1 hour"
|
||||
msgstr "1 ساعة"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "1 min"
|
||||
msgstr "دقيقة واحدة"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 week"
|
||||
msgstr "1 أسبوع"
|
||||
@@ -39,6 +50,11 @@ msgstr "1 أسبوع"
|
||||
msgid "12 hours"
|
||||
msgstr "12 ساعة"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "15 min"
|
||||
msgstr "15 دقيقة"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "24 hours"
|
||||
msgstr "24 ساعة"
|
||||
@@ -47,12 +63,22 @@ msgstr "24 ساعة"
|
||||
msgid "30 days"
|
||||
msgstr "30 يومًا"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "5 min"
|
||||
msgstr "5 دقائق"
|
||||
|
||||
#. Table column
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Actions"
|
||||
msgstr "إجراءات"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Active"
|
||||
msgstr "نشط"
|
||||
|
||||
#: src/components/routes/home.tsx
|
||||
msgid "Active Alerts"
|
||||
msgstr "التنبيهات النشطة"
|
||||
@@ -71,7 +97,7 @@ msgstr "إضافة نظام"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Add URL"
|
||||
msgstr "إضافة عنوان URL"
|
||||
msgstr "إضافة رابط"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Adjust display options for charts."
|
||||
@@ -82,10 +108,16 @@ msgstr "تعديل خيارات العرض للرسوم البيانية."
|
||||
msgid "Admin"
|
||||
msgstr "مسؤول"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "وكيل"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Alert History"
|
||||
msgstr "سجل التنبيهات"
|
||||
|
||||
#: src/components/alerts/alert-button.tsx
|
||||
#: src/components/alerts/alert-button.tsx
|
||||
msgid "Alerts"
|
||||
@@ -96,10 +128,14 @@ msgstr "التنبيهات"
|
||||
msgid "All Systems"
|
||||
msgstr "جميع الأنظمة"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Are you sure you want to delete {name}?"
|
||||
msgstr "هل أنت متأكد أنك تريد حذف {name}؟"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Are you sure?"
|
||||
msgstr "هل أنت متأكد؟"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Automatic copy requires a secure context."
|
||||
msgstr "النسخ التلقائي يتطلب سياقًا آمنًا."
|
||||
@@ -119,7 +155,7 @@ msgstr "المتوسط يتجاوز <0>{value}{0}</0>"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Average power consumption of GPUs"
|
||||
msgstr "متوسط استهلاك طاقة GPUs"
|
||||
msgstr "متوسط استهلاك طاقة وحدة معالجة الرسوميات"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Average system-wide CPU utilization"
|
||||
@@ -142,7 +178,7 @@ msgstr "عرض النطاق الترددي"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Beszel supports OpenID Connect and many OAuth2 authentication providers."
|
||||
msgstr "يدعم Beszel OpenID Connect والعديد من مزودي المصادقة OAuth2."
|
||||
msgstr "يدعم بيزيل بروتوكول OpenID Connect والعديد من مزوّدي المصادقة عبر بروتوكول OAuth2."
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Beszel uses <0>Shoutrrr</0> to integrate with popular notification services."
|
||||
@@ -152,11 +188,22 @@ msgstr "يستخدم بيزيل <0>Shoutrrr</0> للتكامل مع خدمات
|
||||
msgid "Binary"
|
||||
msgstr "ثنائي"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bits (Kbps, Mbps, Gbps)"
|
||||
msgstr "بت (كيلوبت/ثانية، ميجابت/ثانية، جيجابت/ثانية)"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bytes (KB/s, MB/s, GB/s)"
|
||||
msgstr "بايت (كيلوبايت/ثانية، ميجابايت/ثانية، جيجابايت/ثانية)"
|
||||
|
||||
#: src/components/charts/mem-chart.tsx
|
||||
msgid "Cache / Buffers"
|
||||
msgstr "ذاكرة التخزين المؤقت / المخازن المؤقتة"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Cancel"
|
||||
msgstr "إلغاء"
|
||||
|
||||
@@ -164,6 +211,14 @@ msgstr "إلغاء"
|
||||
msgid "Caution - potential data loss"
|
||||
msgstr "تحذير - فقدان محتمل للبيانات"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Celsius (°C)"
|
||||
msgstr "درجة مئوية (°م)"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change display units for metrics."
|
||||
msgstr "تغيير وحدات عرض المقاييس."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change general application options."
|
||||
msgstr "تغيير خيارات التطبيق العامة."
|
||||
@@ -195,14 +250,19 @@ msgstr "تعليمات سطر الأوامر"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Configure how you receive alert notifications."
|
||||
msgstr "قم بتكوين كيفية تلقي إشعارات التنبيه."
|
||||
msgstr "هيئ التنبيهات الواردة"
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Confirm password"
|
||||
msgstr "تأكيد كلمة المرور"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/routes/home.tsx
|
||||
msgid "Connection is down"
|
||||
msgstr "الاتصال مقطوع"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Continue"
|
||||
msgstr "متابعة"
|
||||
|
||||
@@ -214,20 +274,20 @@ msgstr "تم النسخ إلى الحافظة"
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy docker compose file content"
|
||||
msgid "Copy docker compose"
|
||||
msgstr "نسخ docker compose"
|
||||
msgstr "نسخ أمر تركيب الدوكر"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy docker run command"
|
||||
msgid "Copy docker run"
|
||||
msgstr "نسخ docker run"
|
||||
msgstr "نسخ أمر تشغيل الدوكر"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Environment variables"
|
||||
msgid "Copy env"
|
||||
msgstr "نسخ متغيرات البيئة"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Copy host"
|
||||
msgstr "نسخ المضيف"
|
||||
|
||||
@@ -236,6 +296,10 @@ msgstr "نسخ المضيف"
|
||||
msgid "Copy Linux command"
|
||||
msgstr "نسخ أمر لينكس"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Copy name"
|
||||
msgstr "نسخ الاسم"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
msgstr "نسخ النص"
|
||||
@@ -252,13 +316,13 @@ msgstr "انسخ محتوى <0>docker-compose.yml</0> للوكيل أدناه،
|
||||
msgid "Copy YAML"
|
||||
msgstr "نسخ YAML"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "CPU"
|
||||
msgstr "المعالج"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "CPU Usage"
|
||||
msgstr "استخدام وحدة المعالجة المركزية"
|
||||
|
||||
@@ -266,6 +330,15 @@ msgstr "استخدام وحدة المعالجة المركزية"
|
||||
msgid "Create account"
|
||||
msgstr "إنشاء حساب"
|
||||
|
||||
#. Context: date created
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Created"
|
||||
msgstr "أنشئت"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Critical (%)"
|
||||
msgstr "حرج (%)"
|
||||
|
||||
#. Dark theme
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Dark"
|
||||
@@ -280,7 +353,8 @@ msgstr "لوحة التحكم"
|
||||
msgid "Default time period"
|
||||
msgstr "الفترة الزمنية الافتراضية"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Delete"
|
||||
msgstr "حذف"
|
||||
|
||||
@@ -288,7 +362,7 @@ msgstr "حذف"
|
||||
msgid "Delete fingerprint"
|
||||
msgstr "حذف البصمة"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Disk"
|
||||
msgstr "القرص"
|
||||
|
||||
@@ -296,6 +370,10 @@ msgstr "القرص"
|
||||
msgid "Disk I/O"
|
||||
msgstr "إدخال/إخراج القرص"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Disk unit"
|
||||
msgstr "وحدة القرص"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/disk-chart.tsx
|
||||
@@ -308,15 +386,15 @@ msgstr "استخدام القرص لـ {extraFsName}"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Docker CPU Usage"
|
||||
msgstr "استخدام المعالج لـ Docker"
|
||||
msgstr "استخدام المعالج للدوكر"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Docker Memory Usage"
|
||||
msgstr "استخدام الذاكرة لـ Docker"
|
||||
msgstr "استخدام الذاكرة للدوكر"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Docker Network I/O"
|
||||
msgstr "إدخال/إخراج الشبكة لـ Docker"
|
||||
msgstr "إدخال/إخراج الشبكة للدوكر"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Documentation"
|
||||
@@ -324,13 +402,18 @@ msgstr "التوثيق"
|
||||
|
||||
#. Context: System is down
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Down"
|
||||
msgstr "معطل"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Duration"
|
||||
msgstr "المدة"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Edit"
|
||||
msgstr "تعديل"
|
||||
|
||||
@@ -354,6 +437,7 @@ msgstr "أدخل عنوان البريد الإشباكي..."
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Error"
|
||||
msgstr "خطأ"
|
||||
@@ -369,6 +453,10 @@ msgstr "يتجاوز {0}{1} في آخر {2, plural, one {# دقيقة} other {#
|
||||
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
|
||||
msgstr "سيتم حذف الأنظمة الحالية غير المعرفة في <0>config.yml</0>. يرجى عمل نسخ احتياطية بانتظام."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr "تصدير"
|
||||
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
msgid "Export configuration"
|
||||
msgstr "تصدير التكوين"
|
||||
@@ -377,6 +465,10 @@ msgstr "تصدير التكوين"
|
||||
msgid "Export your current systems configuration."
|
||||
msgstr "تصدير تكوين الأنظمة الحالية الخاصة بك."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Fahrenheit (°F)"
|
||||
msgstr "فهرنهايت (°ف)"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Failed to authenticate"
|
||||
msgstr "فشل في المصادقة"
|
||||
@@ -396,12 +488,13 @@ msgstr "فشل في تحديث التنبيه"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Filter..."
|
||||
msgstr "تصفية..."
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Fingerprint"
|
||||
msgstr ""
|
||||
msgstr "البصمة"
|
||||
|
||||
#: src/components/alerts/alerts-system.tsx
|
||||
msgid "For <0>{min}</0> {min, plural, one {minute} other {minutes}}"
|
||||
@@ -419,7 +512,7 @@ msgstr "عام"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "GPU Power Draw"
|
||||
msgstr "استهلاك طاقة GPU"
|
||||
msgstr "استهلاك طاقة وحدة معالجة الرسوميات"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Grid"
|
||||
@@ -448,16 +541,6 @@ msgstr "عنوان البريد الإشباكي غير صالح."
|
||||
msgid "Kernel"
|
||||
msgstr "النواة"
|
||||
|
||||
#. Load average 15 minutes
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "L15"
|
||||
msgstr ""
|
||||
|
||||
#. Load average 5 minutes
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "L5"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "اللغة"
|
||||
@@ -471,13 +554,26 @@ msgstr "التخطيط"
|
||||
msgid "Light"
|
||||
msgstr "فاتح"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Load Average"
|
||||
msgstr "متوسط التحميل"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 15m"
|
||||
msgstr ""
|
||||
msgstr "متوسط التحميل 15 دقيقة"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 1m"
|
||||
msgstr "متوسط التحميل 1 دقيقة"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 5m"
|
||||
msgstr ""
|
||||
msgstr "متوسط التحميل 5 دقائق"
|
||||
|
||||
#. Short label for load average
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Load Avg"
|
||||
msgstr "متوسط التحميل"
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Log Out"
|
||||
@@ -512,9 +608,9 @@ msgstr "تعليمات الإعداد اليدوي"
|
||||
#. Chart select field. Please try to keep this short.
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Max 1 min"
|
||||
msgstr "1 دقيقة كحد"
|
||||
msgstr "الحد الأقصى دقيقة"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Memory"
|
||||
msgstr "الذاكرة"
|
||||
|
||||
@@ -525,28 +621,38 @@ msgstr "استخدام الذاكرة"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Memory usage of docker containers"
|
||||
msgstr "استخدام الذاكرة لحاويات Docker"
|
||||
msgstr "استخدام الذاكرة لحاويات دوكر"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Name"
|
||||
msgstr "الاسم"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Net"
|
||||
msgstr "الشبكة"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Network traffic of docker containers"
|
||||
msgstr "حركة مرور الشبكة لحاويات Docker"
|
||||
msgstr "حركة مرور الشبكة لحاويات الدوكر"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Network traffic of public interfaces"
|
||||
msgstr "حركة مرور الشبكة للواجهات العامة"
|
||||
|
||||
#. Context: Bytes or bits
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Network unit"
|
||||
msgstr "وحدة الشبكة"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "No results found."
|
||||
msgstr "لم يتم العثور على نتائج."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "No results."
|
||||
msgstr "لا توجد نتائج."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "No systems found."
|
||||
@@ -566,7 +672,7 @@ msgstr "دعم OAuth 2 / OIDC"
|
||||
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
|
||||
msgstr "في كل إعادة تشغيل، سيتم تحديث الأنظمة في قاعدة البيانات لتتطابق مع الأنظمة المعرفة في الملف."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Open menu"
|
||||
@@ -584,6 +690,12 @@ msgstr "الكتابة فوق التنبيهات الحالية"
|
||||
msgid "Page"
|
||||
msgstr "صفحة"
|
||||
|
||||
#. placeholder {0}: table.getState().pagination.pageIndex + 1
|
||||
#. placeholder {1}: table.getPageCount()
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Page {0} of {1}"
|
||||
msgstr "صفحة {0} من {1}"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Pages / Settings"
|
||||
msgstr "الصفحات / الإعدادات"
|
||||
@@ -605,13 +717,13 @@ msgstr "يجب أن تكون كلمة المرور أقل من 72 بايت."
|
||||
msgid "Password reset request received"
|
||||
msgstr "تم استلام طلب إعادة تعيين كلمة المرور"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Pause"
|
||||
msgstr "إيقاف مؤقت"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Paused"
|
||||
msgstr "متوقف مؤقتًا"
|
||||
msgstr "متوقف مؤقتا"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
@@ -665,13 +777,12 @@ msgid "Public Key"
|
||||
msgstr "المفتاح العام"
|
||||
|
||||
#. Disk read
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Read"
|
||||
msgstr "قراءة"
|
||||
|
||||
#. Network bytes received (download)
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Received"
|
||||
msgstr "تم الاستلام"
|
||||
|
||||
@@ -679,7 +790,13 @@ msgstr "تم الاستلام"
|
||||
msgid "Reset Password"
|
||||
msgstr "إعادة تعيين كلمة المرور"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Resolved"
|
||||
msgstr "تم حلها"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Resume"
|
||||
msgstr "استئناف"
|
||||
|
||||
@@ -687,6 +804,10 @@ msgstr "استئناف"
|
||||
msgid "Rotate token"
|
||||
msgstr "تدوير الرمز المميز"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Rows per page"
|
||||
msgstr "صفوف لكل صفحة"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Save address using enter key or comma. Leave blank to disable email notifications."
|
||||
msgstr "احفظ العنوان باستخدام مفتاح الإدخال أو الفاصلة. اتركه فارغًا لتعطيل إشعارات البريد الإشباكي."
|
||||
@@ -712,11 +833,14 @@ msgstr "البحث عن الأنظمة أو الإعدادات..."
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "راجع <0>إعدادات الإشعارات</0> لتكوين كيفية تلقي التنبيهات."
|
||||
|
||||
#. Network bytes sent (upload)
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "تم الإرسال"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "تعيين عتبات النسبة المئوية لألوان العداد."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Sets the default time range for charts when a system is viewed."
|
||||
msgstr "يحدد النطاق الزمني الافتراضي للرسوم البيانية عند عرض النظام."
|
||||
@@ -744,6 +868,11 @@ msgstr "إعدادات SMTP"
|
||||
msgid "Sort By"
|
||||
msgstr "الترتيب حسب"
|
||||
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "الحالة"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Status"
|
||||
msgstr "الحالة"
|
||||
@@ -759,11 +888,16 @@ msgstr "استخدام التبديل"
|
||||
#. System theme
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "System"
|
||||
msgstr "النظام"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "System load averages over time"
|
||||
msgstr "متوسط تحميل النظام مع مرور الوقت"
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Systems"
|
||||
msgstr "الأنظمة"
|
||||
@@ -777,7 +911,7 @@ msgid "Table"
|
||||
msgstr "جدول"
|
||||
|
||||
#. Temperature label in systems table
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Temp"
|
||||
msgstr "درجة الحرارة"
|
||||
|
||||
@@ -786,6 +920,10 @@ msgstr "درجة الحرارة"
|
||||
msgid "Temperature"
|
||||
msgstr "درجة الحرارة"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Temperature unit"
|
||||
msgstr "وحدة درجة الحرارة"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Temperatures of system sensors"
|
||||
msgstr "درجات حرارة مستشعرات النظام"
|
||||
@@ -802,10 +940,14 @@ msgstr "تم إرسال إشعار الاختبار"
|
||||
msgid "Then log into the backend and reset your user account password in the users table."
|
||||
msgstr "ثم قم بتسجيل الدخول إلى الواجهة الخلفية وأعد تعيين كلمة مرور حساب المستخدم الخاص بك في جدول المستخدمين."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "This action cannot be undone. This will permanently delete all current records for {name} from the database."
|
||||
msgstr "لا يمكن التراجع عن هذا الإجراء. سيؤدي ذلك إلى حذف جميع السجلات الحالية لـ {name} من قاعدة البيانات بشكل دائم."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "This will permanently delete all selected records from the database."
|
||||
msgstr "سيؤدي هذا إلى حذف جميع السجلات المحددة من قاعدة البيانات بشكل دائم."
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Throughput of {extraFsName}"
|
||||
msgstr "معدل نقل {extraFsName}"
|
||||
@@ -846,13 +988,17 @@ msgstr "تسمح الرموز المميزة للوكلاء بالاتصال و
|
||||
msgid "Tokens and fingerprints are used to authenticate WebSocket connections to the hub."
|
||||
msgstr "تُستخدم الرموز المميزة والبصمات للمصادقة على اتصالات WebSocket إلى المحور."
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when 1 minute load average exceeds a threshold"
|
||||
msgstr "يتم التفعيل عندما يتجاوز متوسط التحميل لمدة دقيقة واحدة عتبة معينة"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when 15 minute load average exceeds a threshold"
|
||||
msgstr ""
|
||||
msgstr "يتم التفعيل عندما يتجاوز متوسط التحميل لمدة 15 دقيقة عتبة معينة"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when 5 minute load average exceeds a threshold"
|
||||
msgstr ""
|
||||
msgstr "يتم التفعيل عندما يتجاوز متوسط التحميل لمدة 5 دقائق عتبة معينة"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when any sensor exceeds a threshold"
|
||||
@@ -878,12 +1024,17 @@ msgstr "يتم التفعيل عندما يتغير الحالة بين التش
|
||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||
msgstr "يتم التفعيل عندما يتجاوز استخدام أي قرص عتبة معينة"
|
||||
|
||||
#. Temperature / network units
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Unit preferences"
|
||||
msgstr "تفضيلات الوحدة"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Universal token"
|
||||
msgstr "رمز مميز عالمي"
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Up"
|
||||
msgstr "قيد التشغيل"
|
||||
@@ -898,7 +1049,8 @@ msgstr "مدة التشغيل"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Usage"
|
||||
msgstr "الاستخدام"
|
||||
|
||||
@@ -908,7 +1060,6 @@ msgstr "استخدام القسم الجذر"
|
||||
|
||||
#: src/components/charts/swap-chart.tsx
|
||||
#: src/components/charts/mem-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
msgid "Used"
|
||||
msgstr "مستخدم"
|
||||
|
||||
@@ -917,10 +1068,18 @@ msgstr "مستخدم"
|
||||
msgid "Users"
|
||||
msgstr "المستخدمون"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Value"
|
||||
msgstr "القيمة"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "View"
|
||||
msgstr "عرض"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "View your 200 most recent alerts."
|
||||
msgstr "عرض أحدث 200 تنبيه."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Visible Fields"
|
||||
msgstr "الأعمدة الظاهرة"
|
||||
@@ -933,6 +1092,14 @@ msgstr "في انتظار وجود سجلات كافية للعرض"
|
||||
msgid "Want to help improve our translations? Check <0>Crowdin</0> for details."
|
||||
msgstr "هل تريد مساعدتنا في تحسين ترجماتنا؟ تحقق من <0>Crowdin</0> لمزيد من التفاصيل."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Warning (%)"
|
||||
msgstr "تحذير (%)"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Warning thresholds"
|
||||
msgstr "عتبات التحذير"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Webhook / Push notifications"
|
||||
msgstr "إشعارات Webhook / Push"
|
||||
@@ -945,11 +1112,11 @@ msgstr "عند التفعيل، يسمح هذا الرمز المميز للوك
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
msgid "Windows command"
|
||||
msgstr "أمر Windows"
|
||||
msgstr "أمر ويندوز"
|
||||
|
||||
#. Disk write
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Write"
|
||||
msgstr "كتابة"
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: bg\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-03-06 07:27\n"
|
||||
"PO-Revision-Date: 2025-07-25 22:44\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Bulgarian\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -23,6 +23,12 @@ msgstr ""
|
||||
msgid "{0, plural, one {# day} other {# days}}"
|
||||
msgstr "{0, plural, one {# ден} other {# дни}}"
|
||||
|
||||
#. placeholder {0}: table.getFilteredSelectedRowModel().rows.length
|
||||
#. placeholder {1}: table.getFilteredRowModel().rows.length
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "{0} of {1} row(s) selected."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "{hours, plural, one {# hour} other {# hours}}"
|
||||
msgstr "{hours, plural, one {# час} other {# часа}}"
|
||||
@@ -31,6 +37,11 @@ msgstr "{hours, plural, one {# час} other {# часа}}"
|
||||
msgid "1 hour"
|
||||
msgstr "1 час"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "1 min"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 week"
|
||||
msgstr "1 седмица"
|
||||
@@ -39,6 +50,11 @@ msgstr "1 седмица"
|
||||
msgid "12 hours"
|
||||
msgstr "12 часа"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "15 min"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "24 hours"
|
||||
msgstr "24 часа"
|
||||
@@ -47,12 +63,22 @@ msgstr "24 часа"
|
||||
msgid "30 days"
|
||||
msgstr "30 дни"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "5 min"
|
||||
msgstr ""
|
||||
|
||||
#. Table column
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Actions"
|
||||
msgstr "Действия"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Active"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/home.tsx
|
||||
msgid "Active Alerts"
|
||||
msgstr "Активни тревоги"
|
||||
@@ -82,10 +108,16 @@ msgstr "Настрой опциите за показване на диагра
|
||||
msgid "Admin"
|
||||
msgstr "Администратор"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Агент"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Alert History"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/alerts/alert-button.tsx
|
||||
#: src/components/alerts/alert-button.tsx
|
||||
msgid "Alerts"
|
||||
@@ -96,10 +128,14 @@ msgstr "Тревоги"
|
||||
msgid "All Systems"
|
||||
msgstr "Всички системи"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Are you sure you want to delete {name}?"
|
||||
msgstr "Сигурен ли си, че искаш да изтриеш {name}?"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Are you sure?"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Automatic copy requires a secure context."
|
||||
msgstr "Автоматичното копиране изисква защитен контескт."
|
||||
@@ -152,11 +188,22 @@ msgstr "Beszel ползва <0>Shoutrrr</0> за да се интегрира с
|
||||
msgid "Binary"
|
||||
msgstr "Двоичен код"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bits (Kbps, Mbps, Gbps)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bytes (KB/s, MB/s, GB/s)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/charts/mem-chart.tsx
|
||||
msgid "Cache / Buffers"
|
||||
msgstr "Кеш / Буфери"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Cancel"
|
||||
msgstr "Откажи"
|
||||
|
||||
@@ -164,6 +211,14 @@ msgstr "Откажи"
|
||||
msgid "Caution - potential data loss"
|
||||
msgstr "Внимание - възможност за загуба на данни"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Celsius (°C)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change display units for metrics."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change general application options."
|
||||
msgstr "Смени общите опции на приложението."
|
||||
@@ -202,7 +257,12 @@ msgstr "Настрой как получаваш нотификации за т
|
||||
msgid "Confirm password"
|
||||
msgstr "Потвърди парола"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/routes/home.tsx
|
||||
msgid "Connection is down"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Continue"
|
||||
msgstr "Продължи"
|
||||
|
||||
@@ -227,7 +287,7 @@ msgctxt "Environment variables"
|
||||
msgid "Copy env"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Copy host"
|
||||
msgstr "Копирай хоста"
|
||||
|
||||
@@ -236,6 +296,10 @@ msgstr "Копирай хоста"
|
||||
msgid "Copy Linux command"
|
||||
msgstr "Копирай linux командата"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Copy name"
|
||||
msgstr "Копирай име"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
msgstr "Копирай текста"
|
||||
@@ -252,13 +316,13 @@ msgstr ""
|
||||
msgid "Copy YAML"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "CPU"
|
||||
msgstr "Процесор"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "CPU Usage"
|
||||
msgstr "Употреба на процесор"
|
||||
|
||||
@@ -266,6 +330,15 @@ msgstr "Употреба на процесор"
|
||||
msgid "Create account"
|
||||
msgstr "Създай акаунт"
|
||||
|
||||
#. Context: date created
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Created"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Critical (%)"
|
||||
msgstr "Критично (%)"
|
||||
|
||||
#. Dark theme
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Dark"
|
||||
@@ -280,7 +353,8 @@ msgstr "Табло"
|
||||
msgid "Default time period"
|
||||
msgstr "Времеви диапазон по подразбиране"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Delete"
|
||||
msgstr "Изтрий"
|
||||
|
||||
@@ -288,7 +362,7 @@ msgstr "Изтрий"
|
||||
msgid "Delete fingerprint"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Disk"
|
||||
msgstr "Диск"
|
||||
|
||||
@@ -296,6 +370,10 @@ msgstr "Диск"
|
||||
msgid "Disk I/O"
|
||||
msgstr "Диск I/O"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Disk unit"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/disk-chart.tsx
|
||||
@@ -324,13 +402,18 @@ msgstr "Документация"
|
||||
|
||||
#. Context: System is down
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Down"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Duration"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Edit"
|
||||
msgstr ""
|
||||
|
||||
@@ -354,6 +437,7 @@ msgstr "Въведи имейл адрес..."
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Error"
|
||||
msgstr "Грешка"
|
||||
@@ -369,6 +453,10 @@ msgstr "Надвишава {0}{1} в последните {2, plural, one {# м
|
||||
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
|
||||
msgstr "Съществуващи системи които не са дефинирани в <0>config.yml</0> ще бъдат изтрити. Моля прави чести архиви."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
msgid "Export configuration"
|
||||
msgstr "Експортирай конфигурация"
|
||||
@@ -377,6 +465,10 @@ msgstr "Експортирай конфигурация"
|
||||
msgid "Export your current systems configuration."
|
||||
msgstr "Експортирай конфигурацията на системите."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Fahrenheit (°F)"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Failed to authenticate"
|
||||
msgstr "Неуспешно удостоверяване"
|
||||
@@ -396,6 +488,7 @@ msgstr "Неуспешно обнови тревога"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Filter..."
|
||||
msgstr "Филтрирай..."
|
||||
|
||||
@@ -448,16 +541,6 @@ msgstr "Невалиден имейл адрес."
|
||||
msgid "Kernel"
|
||||
msgstr "Linux Kernel"
|
||||
|
||||
#. Load average 15 minutes
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "L15"
|
||||
msgstr ""
|
||||
|
||||
#. Load average 5 minutes
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "L5"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "Език"
|
||||
@@ -471,14 +554,27 @@ msgstr "Подреждане"
|
||||
msgid "Light"
|
||||
msgstr "Светъл"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Load Average"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 15m"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 1m"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 5m"
|
||||
msgstr ""
|
||||
|
||||
#. Short label for load average
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Load Avg"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Log Out"
|
||||
msgstr "Изход"
|
||||
@@ -514,7 +610,7 @@ msgstr ""
|
||||
msgid "Max 1 min"
|
||||
msgstr "Максимум 1 минута"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Memory"
|
||||
msgstr "Памет"
|
||||
|
||||
@@ -527,11 +623,12 @@ msgstr "Употреба на паметта"
|
||||
msgid "Memory usage of docker containers"
|
||||
msgstr "Използването на памет от docker контейнерите"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Name"
|
||||
msgstr "Име"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Net"
|
||||
msgstr "Мрежа"
|
||||
|
||||
@@ -543,10 +640,19 @@ msgstr "Мрежов трафик на docker контейнери"
|
||||
msgid "Network traffic of public interfaces"
|
||||
msgstr "Мрежов трафик на публични интерфейси"
|
||||
|
||||
#. Context: Bytes or bits
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Network unit"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "No results found."
|
||||
msgstr "Няма намерени резултати."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "No results."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "No systems found."
|
||||
@@ -566,7 +672,7 @@ msgstr "Поддръжка на OAuth 2 / OIDC"
|
||||
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
|
||||
msgstr "На всеки рестарт, системите в датабазата ще бъдат обновени да съвпадат със системите зададени във файла."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Open menu"
|
||||
@@ -584,6 +690,12 @@ msgstr "Презапиши съществуващи тревоги"
|
||||
msgid "Page"
|
||||
msgstr "Страница"
|
||||
|
||||
#. placeholder {0}: table.getState().pagination.pageIndex + 1
|
||||
#. placeholder {1}: table.getPageCount()
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Page {0} of {1}"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Pages / Settings"
|
||||
msgstr "Страници / Настройки"
|
||||
@@ -605,11 +717,11 @@ msgstr ""
|
||||
msgid "Password reset request received"
|
||||
msgstr "Получено е искането за нулиране на паролата"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Pause"
|
||||
msgstr "Пауза"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Paused"
|
||||
msgstr "На пауза"
|
||||
|
||||
@@ -665,13 +777,12 @@ msgid "Public Key"
|
||||
msgstr "Публичен ключ"
|
||||
|
||||
#. Disk read
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Read"
|
||||
msgstr "Прочети"
|
||||
|
||||
#. Network bytes received (download)
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Received"
|
||||
msgstr "Получени"
|
||||
|
||||
@@ -679,7 +790,13 @@ msgstr "Получени"
|
||||
msgid "Reset Password"
|
||||
msgstr "Нулиране на парола"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Resolved"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Възобнови"
|
||||
|
||||
@@ -687,6 +804,10 @@ msgstr "Възобнови"
|
||||
msgid "Rotate token"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Rows per page"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Save address using enter key or comma. Leave blank to disable email notifications."
|
||||
msgstr "Запази адреса с enter или запетая. Остави празно за да изключиш нотификациите чрез имейл."
|
||||
@@ -712,11 +833,14 @@ msgstr "Търси за системи или настройки..."
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "Виж <0>настройките за нотификациите</0> за да конфигурираш как получаваш тревоги."
|
||||
|
||||
#. Network bytes sent (upload)
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "Изпратени"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "Задайте процентни прагове за цветовете на измервателните уреди."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Sets the default time range for charts when a system is viewed."
|
||||
msgstr "Задава диапазона за време за диаграмите, когато се разглежда система."
|
||||
@@ -744,6 +868,11 @@ msgstr "Настройки за SMTP"
|
||||
msgid "Sort By"
|
||||
msgstr "Сортиране по"
|
||||
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "State"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Status"
|
||||
msgstr "Статус"
|
||||
@@ -759,11 +888,16 @@ msgstr "Използване на swap"
|
||||
#. System theme
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "System"
|
||||
msgstr "Система"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "System load averages over time"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Systems"
|
||||
msgstr "Системи"
|
||||
@@ -777,7 +911,7 @@ msgid "Table"
|
||||
msgstr "Таблица"
|
||||
|
||||
#. Temperature label in systems table
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Temp"
|
||||
msgstr ""
|
||||
|
||||
@@ -786,6 +920,10 @@ msgstr ""
|
||||
msgid "Temperature"
|
||||
msgstr "Температура"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Temperature unit"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Temperatures of system sensors"
|
||||
msgstr "Температири на системни сензори"
|
||||
@@ -802,10 +940,14 @@ msgstr "Тестова нотификация изпратена"
|
||||
msgid "Then log into the backend and reset your user account password in the users table."
|
||||
msgstr "След това влез в backend-а и нулирай паролата за потребителския акаунт в таблицата за потребители."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "This action cannot be undone. This will permanently delete all current records for {name} from the database."
|
||||
msgstr "Това действие не може да бъде отменено. Това ще изтрие всички записи за {name} от датабазата."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "This will permanently delete all selected records from the database."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Throughput of {extraFsName}"
|
||||
msgstr "Пропускателна способност на {extraFsName}"
|
||||
@@ -846,6 +988,10 @@ msgstr ""
|
||||
msgid "Tokens and fingerprints are used to authenticate WebSocket connections to the hub."
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when 1 minute load average exceeds a threshold"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when 15 minute load average exceeds a threshold"
|
||||
msgstr ""
|
||||
@@ -878,12 +1024,17 @@ msgstr "Задейства се, когато статуса превключв
|
||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||
msgstr "Задейства се, когато употребата на някой диск надивши зададен праг"
|
||||
|
||||
#. Temperature / network units
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Unit preferences"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Universal token"
|
||||
msgstr ""
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Up"
|
||||
msgstr ""
|
||||
@@ -898,7 +1049,8 @@ msgstr "Време на работа"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Usage"
|
||||
msgstr "Употреба"
|
||||
|
||||
@@ -908,7 +1060,6 @@ msgstr "Употреба на root partition-а"
|
||||
|
||||
#: src/components/charts/swap-chart.tsx
|
||||
#: src/components/charts/mem-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
msgid "Used"
|
||||
msgstr "Използвани"
|
||||
|
||||
@@ -917,10 +1068,18 @@ msgstr "Използвани"
|
||||
msgid "Users"
|
||||
msgstr "Потребители"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Value"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "View"
|
||||
msgstr "Изглед"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "View your 200 most recent alerts."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Visible Fields"
|
||||
msgstr "Видими полета"
|
||||
@@ -933,6 +1092,14 @@ msgstr "Изчаква се за достатъчно записи за пока
|
||||
msgid "Want to help improve our translations? Check <0>Crowdin</0> for details."
|
||||
msgstr "Искаш да помогнеш да направиш преводите още по-добри? Провери нашия <0>Crowdin</0> за повече детайли."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Warning (%)"
|
||||
msgstr "Предупреждение (%)"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Warning thresholds"
|
||||
msgstr "Прагове за предупреждение"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Webhook / Push notifications"
|
||||
msgstr "Webhook / Пуш нотификации"
|
||||
@@ -948,8 +1115,8 @@ msgid "Windows command"
|
||||
msgstr "Команда Windows"
|
||||
|
||||
#. Disk write
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Write"
|
||||
msgstr "Запиши"
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: cs\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-03-14 00:50\n"
|
||||
"PO-Revision-Date: 2025-08-04 01:51\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Czech\n"
|
||||
"Plural-Forms: nplurals=4; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 3;\n"
|
||||
@@ -23,6 +23,12 @@ msgstr ""
|
||||
msgid "{0, plural, one {# day} other {# days}}"
|
||||
msgstr "{0, plural, one {# den} few {# dny} other {# dní}}"
|
||||
|
||||
#. placeholder {0}: table.getFilteredSelectedRowModel().rows.length
|
||||
#. placeholder {1}: table.getFilteredRowModel().rows.length
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "{0} of {1} row(s) selected."
|
||||
msgstr "{0} z {1} vybraných řádků."
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "{hours, plural, one {# hour} other {# hours}}"
|
||||
msgstr "{hours, plural, one {# Hodina} few {# Hodiny} many {# Hodin} other {# Hodin}}"
|
||||
@@ -31,6 +37,11 @@ msgstr "{hours, plural, one {# Hodina} few {# Hodiny} many {# Hodin} other {# Ho
|
||||
msgid "1 hour"
|
||||
msgstr "1 hodina"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "1 min"
|
||||
msgstr "1 min"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 week"
|
||||
msgstr "1 týden"
|
||||
@@ -39,6 +50,11 @@ msgstr "1 týden"
|
||||
msgid "12 hours"
|
||||
msgstr "12 hodin"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "15 min"
|
||||
msgstr "15 min"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "24 hours"
|
||||
msgstr "24 hodin"
|
||||
@@ -47,12 +63,22 @@ msgstr "24 hodin"
|
||||
msgid "30 days"
|
||||
msgstr "30 dní"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "5 min"
|
||||
msgstr "5 min"
|
||||
|
||||
#. Table column
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Actions"
|
||||
msgstr "Akce"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Active"
|
||||
msgstr "Aktivní"
|
||||
|
||||
#: src/components/routes/home.tsx
|
||||
msgid "Active Alerts"
|
||||
msgstr "Aktivní výstrahy"
|
||||
@@ -82,10 +108,16 @@ msgstr "Upravit možnosti zobrazení pro grafy."
|
||||
msgid "Admin"
|
||||
msgstr "Admin"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Agent"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Alert History"
|
||||
msgstr "Historie upozornění"
|
||||
|
||||
#: src/components/alerts/alert-button.tsx
|
||||
#: src/components/alerts/alert-button.tsx
|
||||
msgid "Alerts"
|
||||
@@ -96,10 +128,14 @@ msgstr "Výstrahy"
|
||||
msgid "All Systems"
|
||||
msgstr "Všechny systémy"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Are you sure you want to delete {name}?"
|
||||
msgstr "Opravdu chcete odstranit {name}?"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Are you sure?"
|
||||
msgstr "Jste si jistý?"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Automatic copy requires a secure context."
|
||||
msgstr "Automatická kopie vyžaduje zabezpečený kontext."
|
||||
@@ -152,11 +188,22 @@ msgstr "Beszel používá <0>Shoutrrr</0> k integraci s populárními notifikač
|
||||
msgid "Binary"
|
||||
msgstr "Binary"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bits (Kbps, Mbps, Gbps)"
|
||||
msgstr "Bits (Kbps, Mbps, Gbps)"
|
||||
|
||||
#: 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)"
|
||||
|
||||
#: src/components/charts/mem-chart.tsx
|
||||
msgid "Cache / Buffers"
|
||||
msgstr "Cache / vyrovnávací paměť"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Cancel"
|
||||
msgstr "Zrušit"
|
||||
|
||||
@@ -164,6 +211,14 @@ msgstr "Zrušit"
|
||||
msgid "Caution - potential data loss"
|
||||
msgstr "Upozornění - možná ztráta dat"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Celsius (°C)"
|
||||
msgstr "Celsia (°C)"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change display units for metrics."
|
||||
msgstr "Změnit jednotky zobrazení metrik."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change general application options."
|
||||
msgstr "Změnit obecné nastavení aplikace."
|
||||
@@ -202,7 +257,12 @@ msgstr "Konfigurace způsobu přijímání upozornění."
|
||||
msgid "Confirm password"
|
||||
msgstr "Potvrdit heslo"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/routes/home.tsx
|
||||
msgid "Connection is down"
|
||||
msgstr "Připojení je nedostupné"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Continue"
|
||||
msgstr "Pokračovat"
|
||||
|
||||
@@ -220,14 +280,14 @@ msgstr "Kopírovat docker compose"
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy docker run command"
|
||||
msgid "Copy docker run"
|
||||
msgstr ""
|
||||
msgstr "Zkopírovat příkaz na spuštění dockeru"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Environment variables"
|
||||
msgid "Copy env"
|
||||
msgstr ""
|
||||
msgstr "Kopírovat env"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Copy host"
|
||||
msgstr "Kopírovat hostitele"
|
||||
|
||||
@@ -236,29 +296,33 @@ msgstr "Kopírovat hostitele"
|
||||
msgid "Copy Linux command"
|
||||
msgstr "Kopírovat příkaz Linux"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Copy name"
|
||||
msgstr "Kopírovat název"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
msgstr "Kopírovat text"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Copy the installation command for the agent below, or register agents automatically with a <0>universal token</0>."
|
||||
msgstr ""
|
||||
msgstr "Zkopírujte instalační příkaz pro agenta níže nebo automaticky registrujte agenty s <0>univerzálním token</0>."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Copy the<0>docker-compose.yml</0> content for the agent below, or register agents automatically with a <1>universal token</1>."
|
||||
msgstr ""
|
||||
msgstr "Zkopírujte obsah <0>docker-compose.yml</0> pro agenta níže nebo automaticky registrujte agenty s <1>univerzálním token</1>."
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Copy YAML"
|
||||
msgstr ""
|
||||
msgstr "Kopírovat YAML"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "CPU"
|
||||
msgstr "Procesor"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "CPU Usage"
|
||||
msgstr "Využití procesoru"
|
||||
|
||||
@@ -266,6 +330,15 @@ msgstr "Využití procesoru"
|
||||
msgid "Create account"
|
||||
msgstr "Vytvořit účet"
|
||||
|
||||
#. Context: date created
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Created"
|
||||
msgstr "Vytvořeno"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Critical (%)"
|
||||
msgstr "Kritické (%)"
|
||||
|
||||
#. Dark theme
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Dark"
|
||||
@@ -280,15 +353,16 @@ msgstr "Přehled"
|
||||
msgid "Default time period"
|
||||
msgstr "Výchozí doba"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Delete"
|
||||
msgstr "Odstranit"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Delete fingerprint"
|
||||
msgstr ""
|
||||
msgstr "Smazat identifikátor"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Disk"
|
||||
msgstr "Disk"
|
||||
|
||||
@@ -296,6 +370,10 @@ msgstr "Disk"
|
||||
msgid "Disk I/O"
|
||||
msgstr "Disk I/O"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Disk unit"
|
||||
msgstr "Disková jednotka"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/disk-chart.tsx
|
||||
@@ -324,13 +402,18 @@ msgstr "Dokumentace"
|
||||
|
||||
#. Context: System is down
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Down"
|
||||
msgstr "Nefunkční"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Duration"
|
||||
msgstr "Doba trvání"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Edit"
|
||||
msgstr "Upravit"
|
||||
|
||||
@@ -354,6 +437,7 @@ msgstr "Zadejte e-mailovou adresu..."
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Error"
|
||||
msgstr "Chyba"
|
||||
@@ -369,6 +453,10 @@ msgstr "Překračuje {0}{1} za {2, plural, one {poslední # minutu} few {posledn
|
||||
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
|
||||
msgstr "Stávající systémy, které nejsou definovány v <0>config.yml</0>, budou odstraněny. Provádějte pravidelné zálohování."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr "Export"
|
||||
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
msgid "Export configuration"
|
||||
msgstr "Exportovat konfiguraci"
|
||||
@@ -377,6 +465,10 @@ msgstr "Exportovat konfiguraci"
|
||||
msgid "Export your current systems configuration."
|
||||
msgstr "Exportovat aktuální konfiguraci systémů."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Fahrenheit (°F)"
|
||||
msgstr "Fahrenheita (°F)"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Failed to authenticate"
|
||||
msgstr "Ověření se nezdařilo"
|
||||
@@ -396,12 +488,13 @@ msgstr "Nepodařilo se aktualizovat upozornění"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Filter..."
|
||||
msgstr "Filtr..."
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Fingerprint"
|
||||
msgstr ""
|
||||
msgstr "Otisk"
|
||||
|
||||
#: src/components/alerts/alerts-system.tsx
|
||||
msgid "For <0>{min}</0> {min, plural, one {minute} other {minutes}}"
|
||||
@@ -429,7 +522,7 @@ msgstr "Mřížka"
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
msgid "Homebrew command"
|
||||
msgstr ""
|
||||
msgstr "Homebrew příkaz"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Host / IP"
|
||||
@@ -448,16 +541,6 @@ msgstr "Neplatná e-mailová adresa."
|
||||
msgid "Kernel"
|
||||
msgstr "Kernel"
|
||||
|
||||
#. Load average 15 minutes
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "L15"
|
||||
msgstr ""
|
||||
|
||||
#. Load average 5 minutes
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "L5"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "Jazyk"
|
||||
@@ -471,13 +554,26 @@ msgstr "Rozvržení"
|
||||
msgid "Light"
|
||||
msgstr "Světlý"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Load Average"
|
||||
msgstr "Průměrné vytížení"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 15m"
|
||||
msgstr ""
|
||||
msgstr "Průměrná zátěž 15m"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 1m"
|
||||
msgstr "Průměrná zátěž 1m"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 5m"
|
||||
msgstr ""
|
||||
msgstr "Průměrná zátěž 5m"
|
||||
|
||||
#. Short label for load average
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Load Avg"
|
||||
msgstr "Prům. zatížení"
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Log Out"
|
||||
@@ -514,7 +610,7 @@ msgstr "Pokyny k manuálnímu nastavení"
|
||||
msgid "Max 1 min"
|
||||
msgstr "Max. 1 min"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Memory"
|
||||
msgstr "Paměť"
|
||||
|
||||
@@ -527,11 +623,12 @@ msgstr "Využití paměti"
|
||||
msgid "Memory usage of docker containers"
|
||||
msgstr "Využití paměti docker kontejnerů"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Name"
|
||||
msgstr "Název"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Net"
|
||||
msgstr "Síť"
|
||||
|
||||
@@ -543,10 +640,19 @@ msgstr "Síťový provoz kontejnerů docker"
|
||||
msgid "Network traffic of public interfaces"
|
||||
msgstr "Síťový provoz veřejných rozhraní"
|
||||
|
||||
#. Context: Bytes or bits
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Network unit"
|
||||
msgstr "Síťová jednotka"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "No results found."
|
||||
msgstr "Nenalezeny žádné výskyty."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "No results."
|
||||
msgstr "Žádné výsledky."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "No systems found."
|
||||
@@ -566,7 +672,7 @@ msgstr "Podpora OAuth 2 / OIDC"
|
||||
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
|
||||
msgstr "Při každém restartu budou systémy v databázi aktualizovány tak, aby odpovídaly systémům definovaným v souboru."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Open menu"
|
||||
@@ -584,6 +690,12 @@ msgstr "Přepsat existující upozornění"
|
||||
msgid "Page"
|
||||
msgstr "Stránka"
|
||||
|
||||
#. placeholder {0}: table.getState().pagination.pageIndex + 1
|
||||
#. placeholder {1}: table.getPageCount()
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Page {0} of {1}"
|
||||
msgstr "Stránka {0} z {1}"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Pages / Settings"
|
||||
msgstr "Stránky / Nastavení"
|
||||
@@ -605,11 +717,11 @@ msgstr "Heslo musí být menší než 72 bytů."
|
||||
msgid "Password reset request received"
|
||||
msgstr "Žádost o obnovu hesla byla přijata"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Pause"
|
||||
msgstr "Pozastavit"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Paused"
|
||||
msgstr "Pozastaveno"
|
||||
|
||||
@@ -665,13 +777,12 @@ msgid "Public Key"
|
||||
msgstr "Veřejný klíč"
|
||||
|
||||
#. Disk read
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Read"
|
||||
msgstr "Číst"
|
||||
|
||||
#. Network bytes received (download)
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Received"
|
||||
msgstr "Přijato"
|
||||
|
||||
@@ -679,13 +790,23 @@ msgstr "Přijato"
|
||||
msgid "Reset Password"
|
||||
msgstr "Obnovit heslo"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Resolved"
|
||||
msgstr "Vyřešeno"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Pokračovat"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Rotate token"
|
||||
msgstr ""
|
||||
msgstr "Změnit token"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Rows per page"
|
||||
msgstr "Řádků na stránku"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Save address using enter key or comma. Leave blank to disable email notifications."
|
||||
@@ -712,11 +833,14 @@ msgstr "Hledat systémy nebo nastavení..."
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "Podívejte se na <0>nastavení upozornění</0> pro nastavení toho, jak přijímáte upozornění."
|
||||
|
||||
#. Network bytes sent (upload)
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "Odeslat"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "Nastavte procentuální prahové hodnoty pro barvy měřičů."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Sets the default time range for charts when a system is viewed."
|
||||
msgstr "Nastaví výchozí časový rozsah grafů, když je systém zobrazen."
|
||||
@@ -744,6 +868,11 @@ msgstr "Nastavení SMTP"
|
||||
msgid "Sort By"
|
||||
msgstr "Seřadit podle"
|
||||
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "Stav"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Status"
|
||||
msgstr "Stav"
|
||||
@@ -759,11 +888,16 @@ msgstr "Swap využití"
|
||||
#. System theme
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "System"
|
||||
msgstr "Systém"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "System load averages over time"
|
||||
msgstr "Průměry zatížení systému v průběhu času"
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Systems"
|
||||
msgstr "Systémy"
|
||||
@@ -777,7 +911,7 @@ msgid "Table"
|
||||
msgstr "Tabulka"
|
||||
|
||||
#. Temperature label in systems table
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Temp"
|
||||
msgstr "Teplota"
|
||||
|
||||
@@ -786,6 +920,10 @@ msgstr "Teplota"
|
||||
msgid "Temperature"
|
||||
msgstr "Teplota"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Temperature unit"
|
||||
msgstr "Jednotky teploty"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Temperatures of system sensors"
|
||||
msgstr "Teploty systémových senzorů"
|
||||
@@ -802,10 +940,14 @@ msgstr "Testovací oznámení odesláno"
|
||||
msgid "Then log into the backend and reset your user account password in the users table."
|
||||
msgstr "Poté se přihlaste do backendu a obnovte heslo k uživatelskému účtu v tabulce uživatelů."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "This action cannot be undone. This will permanently delete all current records for {name} from the database."
|
||||
msgstr "Tuto akci nelze vzít zpět. Tím se z databáze trvale odstraní všechny aktuální záznamy pro {name}."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "This will permanently delete all selected records from the database."
|
||||
msgstr "Tímto trvale odstraníte všechny vybrané záznamy z databáze."
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Throughput of {extraFsName}"
|
||||
msgstr "Propustnost {extraFsName}"
|
||||
@@ -830,29 +972,33 @@ msgstr "Přepnout motiv"
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Token"
|
||||
msgstr ""
|
||||
msgstr "Token"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Tokens & Fingerprints"
|
||||
msgstr ""
|
||||
msgstr "Tokeny & Otisky"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Tokens allow agents to connect and register. Fingerprints are stable identifiers unique to each system, set on first connection."
|
||||
msgstr ""
|
||||
msgstr "Tokeny umožňují agentům připojení a registraci. Otisky jsou stabilní identifikátory jedinečné pro každý systém, nastavené na první připojení."
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Tokens and fingerprints are used to authenticate WebSocket connections to the hub."
|
||||
msgstr ""
|
||||
msgstr "Tokeny a otisky slouží k ověření připojení WebSocket k uzlu."
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when 1 minute load average exceeds a threshold"
|
||||
msgstr "Spustí se, když využití paměti během 1 minuty překročí prahovou hodnotu"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when 15 minute load average exceeds a threshold"
|
||||
msgstr ""
|
||||
msgstr "Spustí se, když využití paměti během 15 minut překročí prahovou hodnotu"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when 5 minute load average exceeds a threshold"
|
||||
msgstr ""
|
||||
msgstr "Spustí se, když využití paměti během 5 minut překročí prahovou hodnotu"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when any sensor exceeds a threshold"
|
||||
@@ -878,12 +1024,17 @@ msgstr "Spouští se, když se změní dostupnost"
|
||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||
msgstr "Spustí se, když využití disku překročí prahovou hodnotu"
|
||||
|
||||
#. Temperature / network units
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Unit preferences"
|
||||
msgstr "Předvolby jednotek"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Universal token"
|
||||
msgstr ""
|
||||
msgstr "Univerzální token"
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Up"
|
||||
msgstr "Funkční"
|
||||
@@ -898,7 +1049,8 @@ msgstr "Doba provozu"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Usage"
|
||||
msgstr "Využití"
|
||||
|
||||
@@ -908,7 +1060,6 @@ msgstr "Využití kořenového oddílu"
|
||||
|
||||
#: src/components/charts/swap-chart.tsx
|
||||
#: src/components/charts/mem-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
msgid "Used"
|
||||
msgstr "Využito"
|
||||
|
||||
@@ -917,10 +1068,18 @@ msgstr "Využito"
|
||||
msgid "Users"
|
||||
msgstr "Uživatelé"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Value"
|
||||
msgstr "Hodnota"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "View"
|
||||
msgstr "Zobrazení"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "View your 200 most recent alerts."
|
||||
msgstr "Zobrazit vašich 200 nejnovějších upozornění."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Visible Fields"
|
||||
msgstr "Viditelné sloupce"
|
||||
@@ -933,23 +1092,31 @@ msgstr "Čeká se na dostatek záznamů k zobrazení"
|
||||
msgid "Want to help improve our translations? Check <0>Crowdin</0> for details."
|
||||
msgstr "Chcete nám pomoci s našimi překlady ještě lépe? Podívejte se na <0>Crowdin</0> pro více informací."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Warning (%)"
|
||||
msgstr "Varování (%)"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Warning thresholds"
|
||||
msgstr "Prahové hodnoty pro varování"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Webhook / Push notifications"
|
||||
msgstr "Webhook / Push oznámení"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation. Expires after one hour or on hub restart."
|
||||
msgstr ""
|
||||
msgstr "Pokud je povoleno, tento token umožňuje agentům, aby se sami zaregistrovali bez předchozího vytvoření systému. Vyprší po jedné hodině nebo po restartu uzlu."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
msgid "Windows command"
|
||||
msgstr ""
|
||||
msgstr "Windows příkaz"
|
||||
|
||||
#. Disk write
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Write"
|
||||
msgstr "Psát"
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: da\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-03-06 07:27\n"
|
||||
"PO-Revision-Date: 2025-07-25 22:44\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Danish\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -23,6 +23,12 @@ msgstr ""
|
||||
msgid "{0, plural, one {# day} other {# days}}"
|
||||
msgstr "{0, plural, one {# day} other {# days}}"
|
||||
|
||||
#. placeholder {0}: table.getFilteredSelectedRowModel().rows.length
|
||||
#. placeholder {1}: table.getFilteredRowModel().rows.length
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "{0} of {1} row(s) selected."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "{hours, plural, one {# hour} other {# hours}}"
|
||||
msgstr "{hours, plural, one {# hour} other {# hours}}"
|
||||
@@ -31,6 +37,11 @@ msgstr "{hours, plural, one {# hour} other {# hours}}"
|
||||
msgid "1 hour"
|
||||
msgstr "1 time"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "1 min"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 week"
|
||||
msgstr "1 uge"
|
||||
@@ -39,6 +50,11 @@ msgstr "1 uge"
|
||||
msgid "12 hours"
|
||||
msgstr "12 timer"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "15 min"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "24 hours"
|
||||
msgstr "24 timer"
|
||||
@@ -47,12 +63,22 @@ msgstr "24 timer"
|
||||
msgid "30 days"
|
||||
msgstr "30 dage"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "5 min"
|
||||
msgstr ""
|
||||
|
||||
#. Table column
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Actions"
|
||||
msgstr "Handlinger"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Active"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/home.tsx
|
||||
msgid "Active Alerts"
|
||||
msgstr "Aktive Alarmer"
|
||||
@@ -82,10 +108,16 @@ msgstr "Juster visningsindstillinger for diagrammer."
|
||||
msgid "Admin"
|
||||
msgstr "Admin"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Agent"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Alert History"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/alerts/alert-button.tsx
|
||||
#: src/components/alerts/alert-button.tsx
|
||||
msgid "Alerts"
|
||||
@@ -96,10 +128,14 @@ msgstr "Alarmer"
|
||||
msgid "All Systems"
|
||||
msgstr "Alle systemer"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Are you sure you want to delete {name}?"
|
||||
msgstr "Er du sikker på, at du vil slette {name}?"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Are you sure?"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Automatic copy requires a secure context."
|
||||
msgstr "Automatisk kopiering kræver en sikker kontekst."
|
||||
@@ -152,11 +188,22 @@ msgstr "Beszel bruger <0>Shoutrrr</0> til at integrere med populære notifikatio
|
||||
msgid "Binary"
|
||||
msgstr "Binær"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bits (Kbps, Mbps, Gbps)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bytes (KB/s, MB/s, GB/s)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/charts/mem-chart.tsx
|
||||
msgid "Cache / Buffers"
|
||||
msgstr "Cache / Buffere"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Cancel"
|
||||
msgstr "Fortryd"
|
||||
|
||||
@@ -164,6 +211,14 @@ msgstr "Fortryd"
|
||||
msgid "Caution - potential data loss"
|
||||
msgstr "Forsigtig - muligt tab af data"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Celsius (°C)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change display units for metrics."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change general application options."
|
||||
msgstr "Skift generelle applikationsindstillinger."
|
||||
@@ -202,7 +257,12 @@ msgstr "Konfigurer hvordan du modtager advarselsmeddelelser."
|
||||
msgid "Confirm password"
|
||||
msgstr "Bekræft adgangskode"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/routes/home.tsx
|
||||
msgid "Connection is down"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Continue"
|
||||
msgstr "Forsæt"
|
||||
|
||||
@@ -227,7 +287,7 @@ msgctxt "Environment variables"
|
||||
msgid "Copy env"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Copy host"
|
||||
msgstr "Kopier host"
|
||||
|
||||
@@ -236,6 +296,10 @@ msgstr "Kopier host"
|
||||
msgid "Copy Linux command"
|
||||
msgstr "Kopier Linux kommando"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Copy name"
|
||||
msgstr "Kopier navn"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
msgstr "Kopier tekst"
|
||||
@@ -252,13 +316,13 @@ msgstr ""
|
||||
msgid "Copy YAML"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "CPU"
|
||||
msgstr "CPU"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "CPU Usage"
|
||||
msgstr "CPU forbrug"
|
||||
|
||||
@@ -266,6 +330,15 @@ msgstr "CPU forbrug"
|
||||
msgid "Create account"
|
||||
msgstr "Opret konto"
|
||||
|
||||
#. Context: date created
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Created"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Critical (%)"
|
||||
msgstr "Kritisk (%)"
|
||||
|
||||
#. Dark theme
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Dark"
|
||||
@@ -280,7 +353,8 @@ msgstr "Oversigtspanel"
|
||||
msgid "Default time period"
|
||||
msgstr "Standard tidsperiode"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Delete"
|
||||
msgstr "Slet"
|
||||
|
||||
@@ -288,7 +362,7 @@ msgstr "Slet"
|
||||
msgid "Delete fingerprint"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Disk"
|
||||
msgstr "Disk"
|
||||
|
||||
@@ -296,6 +370,10 @@ msgstr "Disk"
|
||||
msgid "Disk I/O"
|
||||
msgstr "Disk I/O"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Disk unit"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/disk-chart.tsx
|
||||
@@ -324,15 +402,20 @@ msgstr "Dokumentation"
|
||||
|
||||
#. Context: System is down
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Down"
|
||||
msgstr "Nede"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Duration"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Edit"
|
||||
msgstr ""
|
||||
msgstr "Rediger"
|
||||
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
#: src/components/login/auth-form.tsx
|
||||
@@ -354,6 +437,7 @@ msgstr "Indtast e-mailadresse..."
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Error"
|
||||
msgstr "Fejl"
|
||||
@@ -369,6 +453,10 @@ msgstr "Overskrider {0}{1} i sidste {2, plural, one {# minut} other {# minutter}
|
||||
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
|
||||
msgstr "Eksisterende systemer ikke defineret i <0>config.yml</0> vil blive slettet. Opret venligst regelmæssige sikkerhedskopier."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
msgid "Export configuration"
|
||||
msgstr "Eksporter konfiguration"
|
||||
@@ -377,6 +465,10 @@ msgstr "Eksporter konfiguration"
|
||||
msgid "Export your current systems configuration."
|
||||
msgstr "Eksporter din nuværende systemkonfiguration."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Fahrenheit (°F)"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Failed to authenticate"
|
||||
msgstr "Kunne ikke godkende"
|
||||
@@ -396,6 +488,7 @@ msgstr "Kunne ikke opdatere alarm"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Filter..."
|
||||
msgstr "Filter..."
|
||||
|
||||
@@ -448,16 +541,6 @@ msgstr "Ugyldig email adresse."
|
||||
msgid "Kernel"
|
||||
msgstr "Kernel"
|
||||
|
||||
#. Load average 15 minutes
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "L15"
|
||||
msgstr ""
|
||||
|
||||
#. Load average 5 minutes
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "L5"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "Sprog"
|
||||
@@ -471,14 +554,27 @@ msgstr "Layout"
|
||||
msgid "Light"
|
||||
msgstr "Lys"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Load Average"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 15m"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 1m"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 5m"
|
||||
msgstr ""
|
||||
|
||||
#. Short label for load average
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Load Avg"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Log Out"
|
||||
msgstr "Log ud"
|
||||
@@ -507,14 +603,14 @@ msgstr "Administrer display og notifikationsindstillinger."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Manual setup instructions"
|
||||
msgstr ""
|
||||
msgstr "Manuel opsætningsvejledning"
|
||||
|
||||
#. Chart select field. Please try to keep this short.
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Max 1 min"
|
||||
msgstr "Maks. 1 min"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Memory"
|
||||
msgstr "Hukommelse"
|
||||
|
||||
@@ -527,11 +623,12 @@ msgstr "Hukommelsesforbrug"
|
||||
msgid "Memory usage of docker containers"
|
||||
msgstr "Hukommelsesforbrug af dockercontainere"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Name"
|
||||
msgstr "Navn"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Net"
|
||||
msgstr "Net"
|
||||
|
||||
@@ -543,10 +640,19 @@ msgstr "Netværkstrafik af dockercontainere"
|
||||
msgid "Network traffic of public interfaces"
|
||||
msgstr "Netværkstrafik af offentlige grænseflader"
|
||||
|
||||
#. Context: Bytes or bits
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Network unit"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "No results found."
|
||||
msgstr "Ingen resultater fundet."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "No results."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "No systems found."
|
||||
@@ -566,7 +672,7 @@ msgstr "OAuth 2 / OIDC understøttelse"
|
||||
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
|
||||
msgstr "Ved hver genstart vil systemer i databasen blive opdateret til at matche de systemer, der er defineret i filen."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Open menu"
|
||||
@@ -584,6 +690,12 @@ msgstr "Overskriv eksisterende alarmer"
|
||||
msgid "Page"
|
||||
msgstr "Side"
|
||||
|
||||
#. placeholder {0}: table.getState().pagination.pageIndex + 1
|
||||
#. placeholder {1}: table.getPageCount()
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Page {0} of {1}"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Pages / Settings"
|
||||
msgstr "Sider / Indstillinger"
|
||||
@@ -599,17 +711,17 @@ msgstr "Adgangskoden skal være på mindst 8 tegn."
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Password must be less than 72 bytes."
|
||||
msgstr ""
|
||||
msgstr "Adgangskoden skal være mindre end 72 bytes."
|
||||
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
msgid "Password reset request received"
|
||||
msgstr "Anmodning om nulstilling af adgangskode modtaget"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Pause"
|
||||
msgstr "Pause"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Paused"
|
||||
msgstr "Sat på pause"
|
||||
|
||||
@@ -665,13 +777,12 @@ msgid "Public Key"
|
||||
msgstr "Offentlig nøgle"
|
||||
|
||||
#. Disk read
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Read"
|
||||
msgstr "Læs"
|
||||
|
||||
#. Network bytes received (download)
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Received"
|
||||
msgstr "Modtaget"
|
||||
|
||||
@@ -679,7 +790,13 @@ msgstr "Modtaget"
|
||||
msgid "Reset Password"
|
||||
msgstr "Nulstil adgangskode"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Resolved"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Genoptag"
|
||||
|
||||
@@ -687,6 +804,10 @@ msgstr "Genoptag"
|
||||
msgid "Rotate token"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Rows per page"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Save address using enter key or comma. Leave blank to disable email notifications."
|
||||
msgstr "Gem adresse ved hjælp af enter eller komma. Lad feltet stå tomt for at deaktivere e-mail-meddelelser."
|
||||
@@ -698,7 +819,7 @@ msgstr "Gem indstillinger"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Save system"
|
||||
msgstr ""
|
||||
msgstr "Gem system"
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Search"
|
||||
@@ -712,11 +833,14 @@ msgstr "Søg efter systemer eller indstillinger..."
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "Se <0>meddelelsesindstillinger</0> for at konfigurere, hvordan du modtager alarmer."
|
||||
|
||||
#. Network bytes sent (upload)
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "Sendt"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "Indstil procentvise tærskler for målerfarver."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Sets the default time range for charts when a system is viewed."
|
||||
msgstr "Sætter standardtidsintervallet for diagrammer når et system vises."
|
||||
@@ -744,6 +868,11 @@ msgstr "SMTP-indstillinger"
|
||||
msgid "Sort By"
|
||||
msgstr "Sorter efter"
|
||||
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "State"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Status"
|
||||
msgstr "Status"
|
||||
@@ -759,11 +888,16 @@ msgstr "Swap forbrug"
|
||||
#. System theme
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "System"
|
||||
msgstr "System"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "System load averages over time"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Systems"
|
||||
msgstr "Systemer"
|
||||
@@ -777,15 +911,19 @@ msgid "Table"
|
||||
msgstr "Tabel"
|
||||
|
||||
#. Temperature label in systems table
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Temp"
|
||||
msgstr ""
|
||||
msgstr "Temperatur"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Temperature"
|
||||
msgstr "Temperatur"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Temperature unit"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Temperatures of system sensors"
|
||||
msgstr "Temperaturer i systemsensorer"
|
||||
@@ -802,10 +940,14 @@ msgstr "Test notifikation sendt"
|
||||
msgid "Then log into the backend and reset your user account password in the users table."
|
||||
msgstr "Log derefter ind på backend og nulstil adgangskoden til din brugerkonto i tabellen brugere."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "This action cannot be undone. This will permanently delete all current records for {name} from the database."
|
||||
msgstr "Denne handling kan ikke fortrydes. Dette vil permanent slette alle aktuelle elementer for {name} fra databasen."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "This will permanently delete all selected records from the database."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Throughput of {extraFsName}"
|
||||
msgstr "Gennemløb af {extraFsName}"
|
||||
@@ -846,6 +988,10 @@ msgstr ""
|
||||
msgid "Tokens and fingerprints are used to authenticate WebSocket connections to the hub."
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when 1 minute load average exceeds a threshold"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when 15 minute load average exceeds a threshold"
|
||||
msgstr ""
|
||||
@@ -878,15 +1024,20 @@ msgstr "Udløser når status skifter mellem op og ned"
|
||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||
msgstr "Udløser når brugen af en disk overstiger en tærskel"
|
||||
|
||||
#. Temperature / network units
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Unit preferences"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Universal token"
|
||||
msgstr ""
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Up"
|
||||
msgstr ""
|
||||
msgstr "Oppe"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Updated in real time. Click on a system to view information."
|
||||
@@ -898,7 +1049,8 @@ msgstr "Oppetid"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Usage"
|
||||
msgstr "Forbrug"
|
||||
|
||||
@@ -908,7 +1060,6 @@ msgstr "Brug af rodpartition"
|
||||
|
||||
#: src/components/charts/swap-chart.tsx
|
||||
#: src/components/charts/mem-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
msgid "Used"
|
||||
msgstr "Brugt"
|
||||
|
||||
@@ -917,10 +1068,18 @@ msgstr "Brugt"
|
||||
msgid "Users"
|
||||
msgstr "Brugere"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Value"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "View"
|
||||
msgstr "Vis"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "View your 200 most recent alerts."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Visible Fields"
|
||||
msgstr "Synlige felter"
|
||||
@@ -933,6 +1092,14 @@ msgstr "Venter på nok posteringer til at vise"
|
||||
msgid "Want to help improve our translations? Check <0>Crowdin</0> for details."
|
||||
msgstr "Vil du hjælpe os med at gøre vores oversættelser endnu bedre? Tjek <0>Crowdin</0> for flere detaljer."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Warning (%)"
|
||||
msgstr "Advarsel (%)"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Warning thresholds"
|
||||
msgstr "Advarselstærskler"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Webhook / Push notifications"
|
||||
msgstr "Webhook / Push notifikationer"
|
||||
@@ -948,8 +1115,8 @@ msgid "Windows command"
|
||||
msgstr "Windows-kommando"
|
||||
|
||||
#. Disk write
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Write"
|
||||
msgstr "Skriv"
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: de\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-03-06 07:27\n"
|
||||
"PO-Revision-Date: 2025-07-25 22:44\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: German\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -23,6 +23,12 @@ msgstr ""
|
||||
msgid "{0, plural, one {# day} other {# days}}"
|
||||
msgstr "{0, plural, one {# Tag} other {# Tage}}"
|
||||
|
||||
#. placeholder {0}: table.getFilteredSelectedRowModel().rows.length
|
||||
#. placeholder {1}: table.getFilteredRowModel().rows.length
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "{0} of {1} row(s) selected."
|
||||
msgstr "{0} von {1} Zeile(n) ausgewählt."
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "{hours, plural, one {# hour} other {# hours}}"
|
||||
msgstr "{hours, plural, one {# Stunde} other {# Stunden}}"
|
||||
@@ -31,6 +37,11 @@ msgstr "{hours, plural, one {# Stunde} other {# Stunden}}"
|
||||
msgid "1 hour"
|
||||
msgstr "1 Stunde"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "1 min"
|
||||
msgstr "1 Min"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 week"
|
||||
msgstr "1 Woche"
|
||||
@@ -39,6 +50,11 @@ msgstr "1 Woche"
|
||||
msgid "12 hours"
|
||||
msgstr "12 Stunden"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "15 min"
|
||||
msgstr "15 Min"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "24 hours"
|
||||
msgstr "24 Stunden"
|
||||
@@ -47,12 +63,22 @@ msgstr "24 Stunden"
|
||||
msgid "30 days"
|
||||
msgstr "30 Tage"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "5 min"
|
||||
msgstr "5 Min"
|
||||
|
||||
#. Table column
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Actions"
|
||||
msgstr "Aktionen"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Active"
|
||||
msgstr "Aktiv"
|
||||
|
||||
#: src/components/routes/home.tsx
|
||||
msgid "Active Alerts"
|
||||
msgstr "Aktive Warnungen"
|
||||
@@ -82,10 +108,16 @@ msgstr "Anzeigeoptionen für Diagramme anpassen."
|
||||
msgid "Admin"
|
||||
msgstr "Admin"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Agent"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Alert History"
|
||||
msgstr "Alarm-Verlauf"
|
||||
|
||||
#: src/components/alerts/alert-button.tsx
|
||||
#: src/components/alerts/alert-button.tsx
|
||||
msgid "Alerts"
|
||||
@@ -96,10 +128,14 @@ msgstr "Warnungen"
|
||||
msgid "All Systems"
|
||||
msgstr "Alle Systeme"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Are you sure you want to delete {name}?"
|
||||
msgstr "Möchtest du {name} wirklich löschen?"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Are you sure?"
|
||||
msgstr "Bist du sicher?"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Automatic copy requires a secure context."
|
||||
msgstr "Automatisches Kopieren erfordert einen sicheren Kontext."
|
||||
@@ -152,11 +188,22 @@ msgstr "Beszel verwendet <0>Shoutrrr</0>, um sich mit beliebten Benachrichtigung
|
||||
msgid "Binary"
|
||||
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)"
|
||||
|
||||
#: 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)"
|
||||
|
||||
#: src/components/charts/mem-chart.tsx
|
||||
msgid "Cache / Buffers"
|
||||
msgstr "Cache / Puffer"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Cancel"
|
||||
msgstr "Abbrechen"
|
||||
|
||||
@@ -164,6 +211,14 @@ msgstr "Abbrechen"
|
||||
msgid "Caution - potential data loss"
|
||||
msgstr "Vorsicht - potenzieller Datenverlust"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Celsius (°C)"
|
||||
msgstr "Celsius (°C)"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change display units for metrics."
|
||||
msgstr "Anzeigeeinheiten der Werte ändern."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change general application options."
|
||||
msgstr "Allgemeine Anwendungsoptionen ändern."
|
||||
@@ -202,7 +257,12 @@ msgstr "Konfiguriere, wie du Warnbenachrichtigungen erhältst."
|
||||
msgid "Confirm password"
|
||||
msgstr "Passwort bestätigen"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/routes/home.tsx
|
||||
msgid "Connection is down"
|
||||
msgstr "Verbindung unterbrochen"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Continue"
|
||||
msgstr "Fortfahren"
|
||||
|
||||
@@ -227,7 +287,7 @@ msgctxt "Environment variables"
|
||||
msgid "Copy env"
|
||||
msgstr "Umgebungsvariablen kopieren"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Copy host"
|
||||
msgstr "Host kopieren"
|
||||
|
||||
@@ -236,6 +296,10 @@ msgstr "Host kopieren"
|
||||
msgid "Copy Linux command"
|
||||
msgstr "Linux-Befehl kopieren"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Copy name"
|
||||
msgstr "Name kopieren"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
msgstr "Text kopieren"
|
||||
@@ -252,13 +316,13 @@ msgstr "Kopieren Sie den<0>docker-compose.yml</0> Inhalt für den Agent unten od
|
||||
msgid "Copy YAML"
|
||||
msgstr "YAML kopieren"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "CPU"
|
||||
msgstr "CPU"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "CPU Usage"
|
||||
msgstr "CPU-Auslastung"
|
||||
|
||||
@@ -266,6 +330,15 @@ msgstr "CPU-Auslastung"
|
||||
msgid "Create account"
|
||||
msgstr "Konto erstellen"
|
||||
|
||||
#. Context: date created
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Created"
|
||||
msgstr "Erstellt"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Critical (%)"
|
||||
msgstr "Kritisch (%)"
|
||||
|
||||
#. Dark theme
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Dark"
|
||||
@@ -280,7 +353,8 @@ msgstr "Dashboard"
|
||||
msgid "Default time period"
|
||||
msgstr "Standardzeitraum"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Delete"
|
||||
msgstr "Löschen"
|
||||
|
||||
@@ -288,7 +362,7 @@ msgstr "Löschen"
|
||||
msgid "Delete fingerprint"
|
||||
msgstr "Fingerabdruck löschen"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Disk"
|
||||
msgstr "Festplatte"
|
||||
|
||||
@@ -296,6 +370,10 @@ msgstr "Festplatte"
|
||||
msgid "Disk I/O"
|
||||
msgstr "Festplatten-I/O"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Disk unit"
|
||||
msgstr "Festplatteneinheit"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/disk-chart.tsx
|
||||
@@ -324,13 +402,18 @@ msgstr "Dokumentation"
|
||||
|
||||
#. Context: System is down
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Down"
|
||||
msgstr "Offline"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Duration"
|
||||
msgstr "Dauer"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Edit"
|
||||
msgstr "Bearbeiten"
|
||||
|
||||
@@ -354,6 +437,7 @@ msgstr "E-Mail-Adresse eingeben..."
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Error"
|
||||
msgstr "Fehler"
|
||||
@@ -369,6 +453,10 @@ msgstr "Überschreitet {0}{1} in den letzten {2, plural, one {# Minute} other {#
|
||||
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
|
||||
msgstr "Bestehende Systeme, die nicht in der <0>config.yml</0> definiert sind, werden gelöscht. Bitte mache regelmäßige Backups."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr "Exportieren"
|
||||
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
msgid "Export configuration"
|
||||
msgstr "Konfiguration exportieren"
|
||||
@@ -377,6 +465,10 @@ msgstr "Konfiguration exportieren"
|
||||
msgid "Export your current systems configuration."
|
||||
msgstr "Exportiere die aktuelle Systemkonfiguration."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Fahrenheit (°F)"
|
||||
msgstr "Fahrenheit (°F)"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Failed to authenticate"
|
||||
msgstr "Authentifizierung fehlgeschlagen"
|
||||
@@ -396,12 +488,13 @@ msgstr "Warnung konnte nicht aktualisiert werden"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Filter..."
|
||||
msgstr "Filter..."
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Fingerprint"
|
||||
msgstr ""
|
||||
msgstr "Fingerabdruck"
|
||||
|
||||
#: src/components/alerts/alerts-system.tsx
|
||||
msgid "For <0>{min}</0> {min, plural, one {minute} other {minutes}}"
|
||||
@@ -448,16 +541,6 @@ msgstr "Ungültige E-Mail-Adresse."
|
||||
msgid "Kernel"
|
||||
msgstr "Kernel"
|
||||
|
||||
#. Load average 15 minutes
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "L15"
|
||||
msgstr ""
|
||||
|
||||
#. Load average 5 minutes
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "L5"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "Sprache"
|
||||
@@ -471,13 +554,26 @@ msgstr "Anordnung"
|
||||
msgid "Light"
|
||||
msgstr "Hell"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Load Average"
|
||||
msgstr "Durchschnittliche Systemlast"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 15m"
|
||||
msgstr ""
|
||||
msgstr "Durchschnittliche Systemlast 15 Min"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 1m"
|
||||
msgstr "Durchschnittliche Systemlast 1 Min"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 5m"
|
||||
msgstr ""
|
||||
msgstr "Durchschnittliche Systemlast 5 Min"
|
||||
|
||||
#. Short label for load average
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Load Avg"
|
||||
msgstr "Durchschnittliche Last"
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Log Out"
|
||||
@@ -514,7 +610,7 @@ msgstr "Anleitung zur manuellen Einrichtung"
|
||||
msgid "Max 1 min"
|
||||
msgstr "Max 1 Min"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Memory"
|
||||
msgstr "Arbeitsspeicher"
|
||||
|
||||
@@ -527,11 +623,12 @@ msgstr "Arbeitsspeichernutzung"
|
||||
msgid "Memory usage of docker containers"
|
||||
msgstr "Arbeitsspeichernutzung der Docker-Container"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Name"
|
||||
msgstr "Name"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Net"
|
||||
msgstr "Netz"
|
||||
|
||||
@@ -543,10 +640,19 @@ msgstr "Netzwerkverkehr der Docker-Container"
|
||||
msgid "Network traffic of public interfaces"
|
||||
msgstr "Netzwerkverkehr der öffentlichen Schnittstellen"
|
||||
|
||||
#. Context: Bytes or bits
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Network unit"
|
||||
msgstr "Netzwerkeinheit"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "No results found."
|
||||
msgstr "Keine Ergebnisse gefunden."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "No results."
|
||||
msgstr "Keine Ergebnisse."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "No systems found."
|
||||
@@ -566,7 +672,7 @@ msgstr "OAuth 2 / OIDC-Unterstützung"
|
||||
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
|
||||
msgstr "Bei jedem Neustart werden die Systeme in der Datenbank aktualisiert, um den in der Datei definierten Systemen zu entsprechen."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Open menu"
|
||||
@@ -584,6 +690,12 @@ msgstr "Bestehende Warnungen überschreiben"
|
||||
msgid "Page"
|
||||
msgstr "Seite"
|
||||
|
||||
#. placeholder {0}: table.getState().pagination.pageIndex + 1
|
||||
#. placeholder {1}: table.getPageCount()
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Page {0} of {1}"
|
||||
msgstr "Seite {0} von {1}"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Pages / Settings"
|
||||
msgstr "Seiten / Einstellungen"
|
||||
@@ -605,11 +717,11 @@ msgstr "Das Passwort muss weniger als 72 Bytes lang sein."
|
||||
msgid "Password reset request received"
|
||||
msgstr "Anfrage zum Zurücksetzen des Passworts erhalten"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Pause"
|
||||
msgstr "Pause"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Paused"
|
||||
msgstr "Pausiert"
|
||||
|
||||
@@ -665,13 +777,12 @@ msgid "Public Key"
|
||||
msgstr "Schlüssel"
|
||||
|
||||
#. Disk read
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Read"
|
||||
msgstr "Lesen"
|
||||
|
||||
#. Network bytes received (download)
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Received"
|
||||
msgstr "Empfangen"
|
||||
|
||||
@@ -679,7 +790,13 @@ msgstr "Empfangen"
|
||||
msgid "Reset Password"
|
||||
msgstr "Passwort zurücksetzen"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Resolved"
|
||||
msgstr "Gelöst"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Fortsetzen"
|
||||
|
||||
@@ -687,6 +804,10 @@ msgstr "Fortsetzen"
|
||||
msgid "Rotate token"
|
||||
msgstr "Token rotieren"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Rows per page"
|
||||
msgstr "Zeilen pro Seite"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Save address using enter key or comma. Leave blank to disable email notifications."
|
||||
msgstr "Adresse mit der Enter-Taste oder Komma speichern. Leer lassen, um E-Mail-Benachrichtigungen zu deaktivieren."
|
||||
@@ -698,7 +819,7 @@ msgstr "Einstellungen speichern"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Save system"
|
||||
msgstr ""
|
||||
msgstr "System sichern"
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Search"
|
||||
@@ -712,11 +833,14 @@ msgstr "Nach Systemen oder Einstellungen suchen..."
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "Siehe <0>Benachrichtigungseinstellungen</0>, um zu konfigurieren, wie du Warnungen erhältst."
|
||||
|
||||
#. Network bytes sent (upload)
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "Gesendet"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "Prozentuale Schwellenwerte für Zählerfarben festlegen."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Sets the default time range for charts when a system is viewed."
|
||||
msgstr "Legt den Standardzeitraum für Diagramme fest, wenn ein System angezeigt wird."
|
||||
@@ -744,6 +868,11 @@ msgstr "SMTP-Einstellungen"
|
||||
msgid "Sort By"
|
||||
msgstr "Sortieren nach"
|
||||
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "Status"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Status"
|
||||
msgstr "Status"
|
||||
@@ -759,11 +888,16 @@ msgstr "Swap-Nutzung"
|
||||
#. System theme
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "System"
|
||||
msgstr "System"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "System load averages over time"
|
||||
msgstr "Systemlastdurchschnitt im Zeitverlauf"
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Systems"
|
||||
msgstr "Systeme"
|
||||
@@ -777,7 +911,7 @@ msgid "Table"
|
||||
msgstr "Tabelle"
|
||||
|
||||
#. Temperature label in systems table
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Temp"
|
||||
msgstr "Temperatur"
|
||||
|
||||
@@ -786,6 +920,10 @@ msgstr "Temperatur"
|
||||
msgid "Temperature"
|
||||
msgstr "Temperatur"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Temperature unit"
|
||||
msgstr "Temperatureinheit"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Temperatures of system sensors"
|
||||
msgstr "Temperaturen der Systemsensoren"
|
||||
@@ -802,10 +940,14 @@ msgstr "Testbenachrichtigung gesendet"
|
||||
msgid "Then log into the backend and reset your user account password in the users table."
|
||||
msgstr "Melde dich dann im Backend an und setze dein Benutzerkontopasswort in der Benutzertabelle zurück."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "This action cannot be undone. This will permanently delete all current records for {name} from the database."
|
||||
msgstr "Diese Aktion kann nicht rückgängig gemacht werden. Dadurch werden alle aktuellen Datensätze für {name} dauerhaft aus der Datenbank gelöscht."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "This will permanently delete all selected records from the database."
|
||||
msgstr "Dadurch werden alle ausgewählten Datensätze dauerhaft aus der Datenbank gelöscht."
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Throughput of {extraFsName}"
|
||||
msgstr "Durchsatz von {extraFsName}"
|
||||
@@ -846,13 +988,17 @@ msgstr "Tokens ermöglichen es Agents, sich zu verbinden und zu registrieren. Fi
|
||||
msgid "Tokens and fingerprints are used to authenticate WebSocket connections to the hub."
|
||||
msgstr "Tokens und Fingerabdrücke werden verwendet, um WebSocket-Verbindungen zum Hub zu authentifizieren."
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when 1 minute load average exceeds a threshold"
|
||||
msgstr "Löst aus, wenn der Lastdurchschnitt der letzten Minute einen Schwellenwert überschreitet"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when 15 minute load average exceeds a threshold"
|
||||
msgstr ""
|
||||
msgstr "Löst aus, wenn der Lastdurchschnitt der letzten 15 Minuten einen Schwellenwert überschreitet"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when 5 minute load average exceeds a threshold"
|
||||
msgstr ""
|
||||
msgstr "Löst aus, wenn der Lastdurchschnitt der letzten 5 Minuten einen Schwellenwert überschreitet"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when any sensor exceeds a threshold"
|
||||
@@ -878,15 +1024,20 @@ msgstr "Löst aus, wenn der Status zwischen online und offline wechselt"
|
||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||
msgstr "Löst aus, wenn die Nutzung einer Festplatte einen Schwellenwert überschreitet"
|
||||
|
||||
#. Temperature / network units
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Unit preferences"
|
||||
msgstr "Einheiten"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Universal token"
|
||||
msgstr "Universeller Token"
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Up"
|
||||
msgstr ""
|
||||
msgstr "aktiv"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Updated in real time. Click on a system to view information."
|
||||
@@ -898,7 +1049,8 @@ msgstr "Betriebszeit"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Usage"
|
||||
msgstr "Nutzung"
|
||||
|
||||
@@ -908,7 +1060,6 @@ msgstr "Nutzung der Root-Partition"
|
||||
|
||||
#: src/components/charts/swap-chart.tsx
|
||||
#: src/components/charts/mem-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
msgid "Used"
|
||||
msgstr "Verwendet"
|
||||
|
||||
@@ -917,10 +1068,18 @@ msgstr "Verwendet"
|
||||
msgid "Users"
|
||||
msgstr "Benutzer"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Value"
|
||||
msgstr "Wert"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "View"
|
||||
msgstr "Ansicht"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "View your 200 most recent alerts."
|
||||
msgstr "Sieh dir die neusten 200 Alarme an."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Visible Fields"
|
||||
msgstr "Sichtbare Spalten"
|
||||
@@ -933,6 +1092,14 @@ msgstr "Warten auf genügend Datensätze zur Anzeige"
|
||||
msgid "Want to help improve our translations? Check <0>Crowdin</0> for details."
|
||||
msgstr "Möchtest du uns helfen, unsere Übersetzungen noch besser zu machen? Schau dir <0>Crowdin</0> für weitere Details an."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Warning (%)"
|
||||
msgstr "Warnung (%)"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Warning thresholds"
|
||||
msgstr "Warnschwellen"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Webhook / Push notifications"
|
||||
msgstr "Webhook / Push-Benachrichtigungen"
|
||||
@@ -948,8 +1115,8 @@ msgid "Windows command"
|
||||
msgstr "Windows-Befehl"
|
||||
|
||||
#. Disk write
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Write"
|
||||
msgstr "Schreiben"
|
||||
|
||||
|
||||
@@ -18,6 +18,12 @@ msgstr ""
|
||||
msgid "{0, plural, one {# day} other {# days}}"
|
||||
msgstr "{0, plural, one {# day} other {# days}}"
|
||||
|
||||
#. placeholder {0}: table.getFilteredSelectedRowModel().rows.length
|
||||
#. placeholder {1}: table.getFilteredRowModel().rows.length
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "{0} of {1} row(s) selected."
|
||||
msgstr "{0} of {1} row(s) selected."
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "{hours, plural, one {# hour} other {# hours}}"
|
||||
msgstr "{hours, plural, one {# hour} other {# hours}}"
|
||||
@@ -26,6 +32,11 @@ msgstr "{hours, plural, one {# hour} other {# hours}}"
|
||||
msgid "1 hour"
|
||||
msgstr "1 hour"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "1 min"
|
||||
msgstr "1 min"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 week"
|
||||
msgstr "1 week"
|
||||
@@ -34,6 +45,11 @@ msgstr "1 week"
|
||||
msgid "12 hours"
|
||||
msgstr "12 hours"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "15 min"
|
||||
msgstr "15 min"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "24 hours"
|
||||
msgstr "24 hours"
|
||||
@@ -42,12 +58,22 @@ msgstr "24 hours"
|
||||
msgid "30 days"
|
||||
msgstr "30 days"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "5 min"
|
||||
msgstr "5 min"
|
||||
|
||||
#. Table column
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Actions"
|
||||
msgstr "Actions"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Active"
|
||||
msgstr "Active"
|
||||
|
||||
#: src/components/routes/home.tsx
|
||||
msgid "Active Alerts"
|
||||
msgstr "Active Alerts"
|
||||
@@ -77,10 +103,16 @@ msgstr "Adjust display options for charts."
|
||||
msgid "Admin"
|
||||
msgstr "Admin"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Agent"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Alert History"
|
||||
msgstr "Alert History"
|
||||
|
||||
#: src/components/alerts/alert-button.tsx
|
||||
#: src/components/alerts/alert-button.tsx
|
||||
msgid "Alerts"
|
||||
@@ -91,10 +123,14 @@ msgstr "Alerts"
|
||||
msgid "All Systems"
|
||||
msgstr "All Systems"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Are you sure you want to delete {name}?"
|
||||
msgstr "Are you sure you want to delete {name}?"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Are you sure?"
|
||||
msgstr "Are you sure?"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Automatic copy requires a secure context."
|
||||
msgstr "Automatic copy requires a secure context."
|
||||
@@ -147,11 +183,22 @@ msgstr "Beszel uses <0>Shoutrrr</0> to integrate with popular notification servi
|
||||
msgid "Binary"
|
||||
msgstr "Binary"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bits (Kbps, Mbps, Gbps)"
|
||||
msgstr "Bits (Kbps, Mbps, Gbps)"
|
||||
|
||||
#: 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)"
|
||||
|
||||
#: src/components/charts/mem-chart.tsx
|
||||
msgid "Cache / Buffers"
|
||||
msgstr "Cache / Buffers"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Cancel"
|
||||
msgstr "Cancel"
|
||||
|
||||
@@ -159,6 +206,14 @@ msgstr "Cancel"
|
||||
msgid "Caution - potential data loss"
|
||||
msgstr "Caution - potential data loss"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Celsius (°C)"
|
||||
msgstr "Celsius (°C)"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change display units for metrics."
|
||||
msgstr "Change display units for metrics."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change general application options."
|
||||
msgstr "Change general application options."
|
||||
@@ -197,7 +252,12 @@ msgstr "Configure how you receive alert notifications."
|
||||
msgid "Confirm password"
|
||||
msgstr "Confirm password"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/routes/home.tsx
|
||||
msgid "Connection is down"
|
||||
msgstr "Connection is down"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Continue"
|
||||
msgstr "Continue"
|
||||
|
||||
@@ -222,7 +282,7 @@ msgctxt "Environment variables"
|
||||
msgid "Copy env"
|
||||
msgstr "Copy env"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Copy host"
|
||||
msgstr "Copy host"
|
||||
|
||||
@@ -231,6 +291,10 @@ msgstr "Copy host"
|
||||
msgid "Copy Linux command"
|
||||
msgstr "Copy Linux command"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Copy name"
|
||||
msgstr "Copy name"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
msgstr "Copy text"
|
||||
@@ -247,13 +311,13 @@ msgstr "Copy the<0>docker-compose.yml</0> content for the agent below, or regist
|
||||
msgid "Copy YAML"
|
||||
msgstr "Copy YAML"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "CPU"
|
||||
msgstr "CPU"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "CPU Usage"
|
||||
msgstr "CPU Usage"
|
||||
|
||||
@@ -261,6 +325,15 @@ msgstr "CPU Usage"
|
||||
msgid "Create account"
|
||||
msgstr "Create account"
|
||||
|
||||
#. Context: date created
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Created"
|
||||
msgstr "Created"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Critical (%)"
|
||||
msgstr "Critical (%)"
|
||||
|
||||
#. Dark theme
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Dark"
|
||||
@@ -275,7 +348,8 @@ msgstr "Dashboard"
|
||||
msgid "Default time period"
|
||||
msgstr "Default time period"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Delete"
|
||||
msgstr "Delete"
|
||||
|
||||
@@ -283,7 +357,7 @@ msgstr "Delete"
|
||||
msgid "Delete fingerprint"
|
||||
msgstr "Delete fingerprint"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Disk"
|
||||
msgstr "Disk"
|
||||
|
||||
@@ -291,6 +365,10 @@ msgstr "Disk"
|
||||
msgid "Disk I/O"
|
||||
msgstr "Disk I/O"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Disk unit"
|
||||
msgstr "Disk unit"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/disk-chart.tsx
|
||||
@@ -319,13 +397,18 @@ msgstr "Documentation"
|
||||
|
||||
#. Context: System is down
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Down"
|
||||
msgstr "Down"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Duration"
|
||||
msgstr "Duration"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Edit"
|
||||
msgstr "Edit"
|
||||
|
||||
@@ -349,6 +432,7 @@ msgstr "Enter email address..."
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Error"
|
||||
msgstr "Error"
|
||||
@@ -364,6 +448,10 @@ msgstr "Exceeds {0}{1} in last {2, plural, one {# minute} other {# minutes}}"
|
||||
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
|
||||
msgstr "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr "Export"
|
||||
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
msgid "Export configuration"
|
||||
msgstr "Export configuration"
|
||||
@@ -372,6 +460,10 @@ msgstr "Export configuration"
|
||||
msgid "Export your current systems configuration."
|
||||
msgstr "Export your current systems configuration."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Fahrenheit (°F)"
|
||||
msgstr "Fahrenheit (°F)"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Failed to authenticate"
|
||||
msgstr "Failed to authenticate"
|
||||
@@ -391,6 +483,7 @@ msgstr "Failed to update alert"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Filter..."
|
||||
msgstr "Filter..."
|
||||
|
||||
@@ -443,16 +536,6 @@ msgstr "Invalid email address."
|
||||
msgid "Kernel"
|
||||
msgstr "Kernel"
|
||||
|
||||
#. Load average 15 minutes
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "L15"
|
||||
msgstr "L15"
|
||||
|
||||
#. Load average 5 minutes
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "L5"
|
||||
msgstr "L5"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "Language"
|
||||
@@ -466,14 +549,27 @@ msgstr "Layout"
|
||||
msgid "Light"
|
||||
msgstr "Light"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Load Average"
|
||||
msgstr "Load Average"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 15m"
|
||||
msgstr "Load Average 15m"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 1m"
|
||||
msgstr "Load Average 1m"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 5m"
|
||||
msgstr "Load Average 5m"
|
||||
|
||||
#. Short label for load average
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Load Avg"
|
||||
msgstr "Load Avg"
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Log Out"
|
||||
msgstr "Log Out"
|
||||
@@ -509,7 +605,7 @@ msgstr "Manual setup instructions"
|
||||
msgid "Max 1 min"
|
||||
msgstr "Max 1 min"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Memory"
|
||||
msgstr "Memory"
|
||||
|
||||
@@ -522,11 +618,12 @@ msgstr "Memory Usage"
|
||||
msgid "Memory usage of docker containers"
|
||||
msgstr "Memory usage of docker containers"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Name"
|
||||
msgstr "Name"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Net"
|
||||
msgstr "Net"
|
||||
|
||||
@@ -538,10 +635,19 @@ msgstr "Network traffic of docker containers"
|
||||
msgid "Network traffic of public interfaces"
|
||||
msgstr "Network traffic of public interfaces"
|
||||
|
||||
#. Context: Bytes or bits
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Network unit"
|
||||
msgstr "Network unit"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "No results found."
|
||||
msgstr "No results found."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "No results."
|
||||
msgstr "No results."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "No systems found."
|
||||
@@ -561,7 +667,7 @@ msgstr "OAuth 2 / OIDC support"
|
||||
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
|
||||
msgstr "On each restart, systems in the database will be updated to match the systems defined in the file."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Open menu"
|
||||
@@ -579,6 +685,12 @@ msgstr "Overwrite existing alerts"
|
||||
msgid "Page"
|
||||
msgstr "Page"
|
||||
|
||||
#. placeholder {0}: table.getState().pagination.pageIndex + 1
|
||||
#. placeholder {1}: table.getPageCount()
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Page {0} of {1}"
|
||||
msgstr "Page {0} of {1}"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Pages / Settings"
|
||||
msgstr "Pages / Settings"
|
||||
@@ -600,11 +712,11 @@ msgstr "Password must be less than 72 bytes."
|
||||
msgid "Password reset request received"
|
||||
msgstr "Password reset request received"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Pause"
|
||||
msgstr "Pause"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Paused"
|
||||
msgstr "Paused"
|
||||
|
||||
@@ -660,13 +772,12 @@ msgid "Public Key"
|
||||
msgstr "Public Key"
|
||||
|
||||
#. Disk read
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Read"
|
||||
msgstr "Read"
|
||||
|
||||
#. Network bytes received (download)
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Received"
|
||||
msgstr "Received"
|
||||
|
||||
@@ -674,7 +785,13 @@ msgstr "Received"
|
||||
msgid "Reset Password"
|
||||
msgstr "Reset Password"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Resolved"
|
||||
msgstr "Resolved"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Resume"
|
||||
|
||||
@@ -682,6 +799,10 @@ msgstr "Resume"
|
||||
msgid "Rotate token"
|
||||
msgstr "Rotate token"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Rows per page"
|
||||
msgstr "Rows per page"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Save address using enter key or comma. Leave blank to disable email notifications."
|
||||
msgstr "Save address using enter key or comma. Leave blank to disable email notifications."
|
||||
@@ -707,11 +828,14 @@ msgstr "Search for systems or settings..."
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "See <0>notification settings</0> to configure how you receive alerts."
|
||||
|
||||
#. Network bytes sent (upload)
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "Sent"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "Set percentage thresholds for meter colors."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Sets the default time range for charts when a system is viewed."
|
||||
msgstr "Sets the default time range for charts when a system is viewed."
|
||||
@@ -739,6 +863,11 @@ msgstr "SMTP settings"
|
||||
msgid "Sort By"
|
||||
msgstr "Sort By"
|
||||
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "State"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Status"
|
||||
msgstr "Status"
|
||||
@@ -754,11 +883,16 @@ msgstr "Swap Usage"
|
||||
#. System theme
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "System"
|
||||
msgstr "System"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "System load averages over time"
|
||||
msgstr "System load averages over time"
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Systems"
|
||||
msgstr "Systems"
|
||||
@@ -772,7 +906,7 @@ msgid "Table"
|
||||
msgstr "Table"
|
||||
|
||||
#. Temperature label in systems table
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Temp"
|
||||
msgstr "Temp"
|
||||
|
||||
@@ -781,6 +915,10 @@ msgstr "Temp"
|
||||
msgid "Temperature"
|
||||
msgstr "Temperature"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Temperature unit"
|
||||
msgstr "Temperature unit"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Temperatures of system sensors"
|
||||
msgstr "Temperatures of system sensors"
|
||||
@@ -797,10 +935,14 @@ msgstr "Test notification sent"
|
||||
msgid "Then log into the backend and reset your user account password in the users table."
|
||||
msgstr "Then log into the backend and reset your user account password in the users table."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "This action cannot be undone. This will permanently delete all current records for {name} from the database."
|
||||
msgstr "This action cannot be undone. This will permanently delete all current records for {name} from the database."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "This will permanently delete all selected records from the database."
|
||||
msgstr "This will permanently delete all selected records from the database."
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Throughput of {extraFsName}"
|
||||
msgstr "Throughput of {extraFsName}"
|
||||
@@ -841,6 +983,10 @@ msgstr "Tokens allow agents to connect and register. Fingerprints are stable ide
|
||||
msgid "Tokens and fingerprints are used to authenticate WebSocket connections to the hub."
|
||||
msgstr "Tokens and fingerprints are used to authenticate WebSocket connections to the hub."
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when 1 minute load average exceeds a threshold"
|
||||
msgstr "Triggers when 1 minute load average exceeds a threshold"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when 15 minute load average exceeds a threshold"
|
||||
msgstr "Triggers when 15 minute load average exceeds a threshold"
|
||||
@@ -873,12 +1019,17 @@ msgstr "Triggers when status switches between up and down"
|
||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||
msgstr "Triggers when usage of any disk exceeds a threshold"
|
||||
|
||||
#. Temperature / network units
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Unit preferences"
|
||||
msgstr "Unit preferences"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Universal token"
|
||||
msgstr "Universal token"
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Up"
|
||||
msgstr "Up"
|
||||
@@ -893,7 +1044,8 @@ msgstr "Uptime"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Usage"
|
||||
msgstr "Usage"
|
||||
|
||||
@@ -903,7 +1055,6 @@ msgstr "Usage of root partition"
|
||||
|
||||
#: src/components/charts/swap-chart.tsx
|
||||
#: src/components/charts/mem-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
msgid "Used"
|
||||
msgstr "Used"
|
||||
|
||||
@@ -912,10 +1063,18 @@ msgstr "Used"
|
||||
msgid "Users"
|
||||
msgstr "Users"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Value"
|
||||
msgstr "Value"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "View"
|
||||
msgstr "View"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "View your 200 most recent alerts."
|
||||
msgstr "View your 200 most recent alerts."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Visible Fields"
|
||||
msgstr "Visible Fields"
|
||||
@@ -928,6 +1087,14 @@ msgstr "Waiting for enough records to display"
|
||||
msgid "Want to help improve our translations? Check <0>Crowdin</0> for details."
|
||||
msgstr "Want to help improve our translations? Check <0>Crowdin</0> for details."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Warning (%)"
|
||||
msgstr "Warning (%)"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Warning thresholds"
|
||||
msgstr "Warning thresholds"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Webhook / Push notifications"
|
||||
msgstr "Webhook / Push notifications"
|
||||
@@ -943,8 +1110,8 @@ msgid "Windows command"
|
||||
msgstr "Windows command"
|
||||
|
||||
#. Disk write
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Write"
|
||||
msgstr "Write"
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: es\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-03-06 07:27\n"
|
||||
"PO-Revision-Date: 2025-07-25 22:44\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Spanish\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -23,6 +23,12 @@ msgstr ""
|
||||
msgid "{0, plural, one {# day} other {# days}}"
|
||||
msgstr "{0, plural, one {# día} other {# días}}"
|
||||
|
||||
#. placeholder {0}: table.getFilteredSelectedRowModel().rows.length
|
||||
#. placeholder {1}: table.getFilteredRowModel().rows.length
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "{0} of {1} row(s) selected."
|
||||
msgstr "{0} de {1} fila(s) seleccionada(s)."
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "{hours, plural, one {# hour} other {# hours}}"
|
||||
msgstr "{hours, plural, one {# hora} other {# horas}}"
|
||||
@@ -31,6 +37,11 @@ msgstr "{hours, plural, one {# hora} other {# horas}}"
|
||||
msgid "1 hour"
|
||||
msgstr "1 hora"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "1 min"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 week"
|
||||
msgstr "1 semana"
|
||||
@@ -39,6 +50,11 @@ msgstr "1 semana"
|
||||
msgid "12 hours"
|
||||
msgstr "12 horas"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "15 min"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "24 hours"
|
||||
msgstr "24 horas"
|
||||
@@ -47,12 +63,22 @@ msgstr "24 horas"
|
||||
msgid "30 days"
|
||||
msgstr "30 días"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "5 min"
|
||||
msgstr ""
|
||||
|
||||
#. Table column
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Actions"
|
||||
msgstr "Acciones"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Active"
|
||||
msgstr "Activo"
|
||||
|
||||
#: src/components/routes/home.tsx
|
||||
msgid "Active Alerts"
|
||||
msgstr "Alertas Activas"
|
||||
@@ -82,10 +108,16 @@ msgstr "Ajustar las opciones de visualización para los gráficos."
|
||||
msgid "Admin"
|
||||
msgstr "Administrador"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Agente"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Alert History"
|
||||
msgstr "Historial de Alertas"
|
||||
|
||||
#: src/components/alerts/alert-button.tsx
|
||||
#: src/components/alerts/alert-button.tsx
|
||||
msgid "Alerts"
|
||||
@@ -96,10 +128,14 @@ msgstr "Alertas"
|
||||
msgid "All Systems"
|
||||
msgstr "Todos los Sistemas"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Are you sure you want to delete {name}?"
|
||||
msgstr "¿Está seguro de que desea eliminar {name}?"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Are you sure?"
|
||||
msgstr "¿Estás seguro?"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Automatic copy requires a secure context."
|
||||
msgstr "La copia automática requiere un contexto seguro."
|
||||
@@ -152,11 +188,22 @@ msgstr "Beszel utiliza <0>Shoutrrr</0> para integrarse con servicios populares d
|
||||
msgid "Binary"
|
||||
msgstr "Binario"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bits (Kbps, Mbps, Gbps)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bytes (KB/s, MB/s, GB/s)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/charts/mem-chart.tsx
|
||||
msgid "Cache / Buffers"
|
||||
msgstr "Caché / Buffers"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Cancel"
|
||||
msgstr "Cancelar"
|
||||
|
||||
@@ -164,6 +211,14 @@ msgstr "Cancelar"
|
||||
msgid "Caution - potential data loss"
|
||||
msgstr "Precaución - posible pérdida de datos"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Celsius (°C)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change display units for metrics."
|
||||
msgstr "Cambiar las unidades de visualización de las métricas."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change general application options."
|
||||
msgstr "Cambiar las opciones generales de la aplicación."
|
||||
@@ -202,7 +257,12 @@ msgstr "Configure cómo recibe las notificaciones de alertas."
|
||||
msgid "Confirm password"
|
||||
msgstr "Confirmar contraseña"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/routes/home.tsx
|
||||
msgid "Connection is down"
|
||||
msgstr "La conexión está caída"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Continue"
|
||||
msgstr "Continuar"
|
||||
|
||||
@@ -227,7 +287,7 @@ msgctxt "Environment variables"
|
||||
msgid "Copy env"
|
||||
msgstr "Copiar env"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Copy host"
|
||||
msgstr "Copiar host"
|
||||
|
||||
@@ -236,6 +296,10 @@ msgstr "Copiar host"
|
||||
msgid "Copy Linux command"
|
||||
msgstr "Copiar comando de Linux"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Copy name"
|
||||
msgstr "Copiar nombre"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
msgstr "Copiar texto"
|
||||
@@ -252,13 +316,13 @@ msgstr "Copia el contenido del<0>docker-compose.yml</0> para el agente a continu
|
||||
msgid "Copy YAML"
|
||||
msgstr "Copiar YAML"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "CPU"
|
||||
msgstr "CPU"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "CPU Usage"
|
||||
msgstr "Uso de CPU"
|
||||
|
||||
@@ -266,6 +330,15 @@ msgstr "Uso de CPU"
|
||||
msgid "Create account"
|
||||
msgstr "Crear cuenta"
|
||||
|
||||
#. Context: date created
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Created"
|
||||
msgstr "Creado"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Critical (%)"
|
||||
msgstr "Crítico (%)"
|
||||
|
||||
#. Dark theme
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Dark"
|
||||
@@ -280,7 +353,8 @@ msgstr "Tablero"
|
||||
msgid "Default time period"
|
||||
msgstr "Período de tiempo predeterminado"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Delete"
|
||||
msgstr "Eliminar"
|
||||
|
||||
@@ -288,7 +362,7 @@ msgstr "Eliminar"
|
||||
msgid "Delete fingerprint"
|
||||
msgstr "Eliminar huella digital"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Disk"
|
||||
msgstr "Disco"
|
||||
|
||||
@@ -296,6 +370,10 @@ msgstr "Disco"
|
||||
msgid "Disk I/O"
|
||||
msgstr "E/S de Disco"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Disk unit"
|
||||
msgstr "Unidad de disco"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/disk-chart.tsx
|
||||
@@ -324,13 +402,18 @@ msgstr "Documentación"
|
||||
|
||||
#. Context: System is down
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Down"
|
||||
msgstr "Abajo"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Duration"
|
||||
msgstr "Duración"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Edit"
|
||||
msgstr "Editar"
|
||||
|
||||
@@ -354,6 +437,7 @@ msgstr "Ingrese dirección de correo..."
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Error"
|
||||
msgstr "Error"
|
||||
@@ -369,6 +453,10 @@ msgstr "Excede {0}{1} en el último {2, plural, one {# minuto} other {# minutos}
|
||||
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
|
||||
msgstr "Los sistemas existentes no definidos en <0>config.yml</0> serán eliminados. Por favor, haga copias de seguridad regularmente."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr "Exportar"
|
||||
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
msgid "Export configuration"
|
||||
msgstr "Exportar configuración"
|
||||
@@ -377,6 +465,10 @@ msgstr "Exportar configuración"
|
||||
msgid "Export your current systems configuration."
|
||||
msgstr "Exporte la configuración actual de sus sistemas."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Fahrenheit (°F)"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Failed to authenticate"
|
||||
msgstr "Error al autenticar"
|
||||
@@ -396,12 +488,13 @@ msgstr "Error al actualizar la alerta"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Filter..."
|
||||
msgstr "Filtrar..."
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Fingerprint"
|
||||
msgstr ""
|
||||
msgstr "Huella dactilar"
|
||||
|
||||
#: src/components/alerts/alerts-system.tsx
|
||||
msgid "For <0>{min}</0> {min, plural, one {minute} other {minutes}}"
|
||||
@@ -448,16 +541,6 @@ msgstr "Dirección de correo electrónico no válida."
|
||||
msgid "Kernel"
|
||||
msgstr "Kernel"
|
||||
|
||||
#. Load average 15 minutes
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "L15"
|
||||
msgstr ""
|
||||
|
||||
#. Load average 5 minutes
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "L5"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "Idioma"
|
||||
@@ -471,13 +554,26 @@ msgstr "Diseño"
|
||||
msgid "Light"
|
||||
msgstr "Claro"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Load Average"
|
||||
msgstr "Carga Media"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 15m"
|
||||
msgstr ""
|
||||
msgstr "Carga media 15m"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 1m"
|
||||
msgstr "Carga media 1m"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 5m"
|
||||
msgstr ""
|
||||
msgstr "Carga media 5m"
|
||||
|
||||
#. Short label for load average
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Load Avg"
|
||||
msgstr "Carga media"
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Log Out"
|
||||
@@ -514,7 +610,7 @@ msgstr "Instrucciones manuales de configuración"
|
||||
msgid "Max 1 min"
|
||||
msgstr "Máx 1 min"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Memory"
|
||||
msgstr "Memoria"
|
||||
|
||||
@@ -527,11 +623,12 @@ msgstr "Uso de Memoria"
|
||||
msgid "Memory usage of docker containers"
|
||||
msgstr "Uso de memoria de los contenedores de Docker"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Name"
|
||||
msgstr "Nombre"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Net"
|
||||
msgstr "Red"
|
||||
|
||||
@@ -543,10 +640,19 @@ msgstr "Tráfico de red de los contenedores de Docker"
|
||||
msgid "Network traffic of public interfaces"
|
||||
msgstr "Tráfico de red de interfaces públicas"
|
||||
|
||||
#. Context: Bytes or bits
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Network unit"
|
||||
msgstr "Unidad de red"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "No results found."
|
||||
msgstr "No se encontraron resultados."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "No results."
|
||||
msgstr "Sin resultados."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "No systems found."
|
||||
@@ -566,7 +672,7 @@ msgstr "Soporte para OAuth 2 / OIDC"
|
||||
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
|
||||
msgstr "En cada reinicio, los sistemas en la base de datos se actualizarán para coincidir con los sistemas definidos en el archivo."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Open menu"
|
||||
@@ -584,6 +690,12 @@ msgstr "Sobrescribir alertas existentes"
|
||||
msgid "Page"
|
||||
msgstr "Página"
|
||||
|
||||
#. placeholder {0}: table.getState().pagination.pageIndex + 1
|
||||
#. placeholder {1}: table.getPageCount()
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Page {0} of {1}"
|
||||
msgstr "Página {0} de {1}"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Pages / Settings"
|
||||
msgstr "Páginas / Configuraciones"
|
||||
@@ -605,11 +717,11 @@ msgstr "La contraseña debe ser menor de 72 bytes."
|
||||
msgid "Password reset request received"
|
||||
msgstr "Solicitud de restablecimiento de contraseña recibida"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Pause"
|
||||
msgstr "Pausar"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Paused"
|
||||
msgstr "Pausado"
|
||||
|
||||
@@ -665,13 +777,12 @@ msgid "Public Key"
|
||||
msgstr "Clave Pública"
|
||||
|
||||
#. Disk read
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Read"
|
||||
msgstr "Lectura"
|
||||
|
||||
#. Network bytes received (download)
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Received"
|
||||
msgstr "Recibido"
|
||||
|
||||
@@ -679,7 +790,13 @@ msgstr "Recibido"
|
||||
msgid "Reset Password"
|
||||
msgstr "Restablecer Contraseña"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Resolved"
|
||||
msgstr "Resuelto"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Reanudar"
|
||||
|
||||
@@ -687,6 +804,10 @@ msgstr "Reanudar"
|
||||
msgid "Rotate token"
|
||||
msgstr "Rotar token"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Rows per page"
|
||||
msgstr "Filas por página"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Save address using enter key or comma. Leave blank to disable email notifications."
|
||||
msgstr "Guarde la dirección usando la tecla enter o coma. Deje en blanco para desactivar las notificaciones por correo."
|
||||
@@ -712,11 +833,14 @@ msgstr "Buscar sistemas o configuraciones..."
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "Consulte <0>configuración de notificaciones</0> para configurar cómo recibe alertas."
|
||||
|
||||
#. Network bytes sent (upload)
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "Enviado"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "Establecer umbrales de porcentaje para los colores de los medidores."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Sets the default time range for charts when a system is viewed."
|
||||
msgstr "Establece el rango de tiempo predeterminado para los gráficos cuando se visualiza un sistema."
|
||||
@@ -744,6 +868,11 @@ msgstr "Configuración SMTP"
|
||||
msgid "Sort By"
|
||||
msgstr "Ordenar por"
|
||||
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "Estado"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Status"
|
||||
msgstr "Estado"
|
||||
@@ -759,11 +888,16 @@ msgstr "Uso de Swap"
|
||||
#. System theme
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "System"
|
||||
msgstr "Sistema"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "System load averages over time"
|
||||
msgstr "Promedios de carga del sistema a lo largo del tiempo"
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Systems"
|
||||
msgstr "Sistemas"
|
||||
@@ -777,7 +911,7 @@ msgid "Table"
|
||||
msgstr "Tabla"
|
||||
|
||||
#. Temperature label in systems table
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Temp"
|
||||
msgstr "Temperatura"
|
||||
|
||||
@@ -786,6 +920,10 @@ msgstr "Temperatura"
|
||||
msgid "Temperature"
|
||||
msgstr "Temperatura"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Temperature unit"
|
||||
msgstr "Unidad de temperatura"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Temperatures of system sensors"
|
||||
msgstr "Temperaturas de los sensores del sistema"
|
||||
@@ -802,10 +940,14 @@ msgstr "Notificación de prueba enviada"
|
||||
msgid "Then log into the backend and reset your user account password in the users table."
|
||||
msgstr "Luego inicie sesión en el backend y restablezca la contraseña de su cuenta de usuario en la tabla de usuarios."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "This action cannot be undone. This will permanently delete all current records for {name} from the database."
|
||||
msgstr "Esta acción no se puede deshacer. Esto eliminará permanentemente todos los registros actuales de {name} de la base de datos."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "This will permanently delete all selected records from the database."
|
||||
msgstr "Esto eliminará permanentemente todos los registros seleccionados de la base de datos."
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Throughput of {extraFsName}"
|
||||
msgstr "Rendimiento de {extraFsName}"
|
||||
@@ -830,7 +972,7 @@ msgstr "Alternar tema"
|
||||
#: 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/tokens-fingerprints.tsx
|
||||
@@ -846,13 +988,17 @@ msgstr "Los tokens permiten que los agentes se conecten y registren. Las huellas
|
||||
msgid "Tokens and fingerprints are used to authenticate WebSocket connections to the hub."
|
||||
msgstr "Los tokens y las huellas digitales se utilizan para autenticar las conexiones WebSocket al hub."
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when 1 minute load average exceeds a threshold"
|
||||
msgstr "Se activa cuando la carga media de 1 minuto supera un umbral"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when 15 minute load average exceeds a threshold"
|
||||
msgstr ""
|
||||
msgstr "Se activa cuando la carga media de 15 minutos supera un umbral"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when 5 minute load average exceeds a threshold"
|
||||
msgstr ""
|
||||
msgstr "Se activa cuando la carga media de 5 minutos supera un umbral"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when any sensor exceeds a threshold"
|
||||
@@ -878,12 +1024,17 @@ msgstr "Se activa cuando el estado cambia entre activo e inactivo"
|
||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||
msgstr "Se activa cuando el uso de cualquier disco supera un umbral"
|
||||
|
||||
#. Temperature / network units
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Unit preferences"
|
||||
msgstr "Preferencias de unidad"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Universal token"
|
||||
msgstr "Token universal"
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Up"
|
||||
msgstr "Activo"
|
||||
@@ -898,7 +1049,8 @@ msgstr "Tiempo de actividad"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Usage"
|
||||
msgstr "Uso"
|
||||
|
||||
@@ -908,7 +1060,6 @@ msgstr "Uso de la partición raíz"
|
||||
|
||||
#: src/components/charts/swap-chart.tsx
|
||||
#: src/components/charts/mem-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
msgid "Used"
|
||||
msgstr "Usado"
|
||||
|
||||
@@ -917,10 +1068,18 @@ msgstr "Usado"
|
||||
msgid "Users"
|
||||
msgstr "Usuarios"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Value"
|
||||
msgstr "Valor"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "View"
|
||||
msgstr "Vista"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "View your 200 most recent alerts."
|
||||
msgstr "Ver sus 200 alertas más recientes."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Visible Fields"
|
||||
msgstr "Columnas visibles"
|
||||
@@ -933,6 +1092,14 @@ msgstr "Esperando suficientes registros para mostrar"
|
||||
msgid "Want to help improve our translations? Check <0>Crowdin</0> for details."
|
||||
msgstr "¿Quieres ayudarnos a mejorar nuestras traducciones? Consulta <0>Crowdin</0> para más detalles."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Warning (%)"
|
||||
msgstr "Advertencia (%)"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Warning thresholds"
|
||||
msgstr "Umbrales de advertencia"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Webhook / Push notifications"
|
||||
msgstr "Notificaciones Webhook / Push"
|
||||
@@ -948,8 +1115,8 @@ msgid "Windows command"
|
||||
msgstr "Comando Windows"
|
||||
|
||||
#. Disk write
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Write"
|
||||
msgstr "Escritura"
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: fa\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-03-06 07:27\n"
|
||||
"PO-Revision-Date: 2025-07-25 22:44\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Persian\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -23,6 +23,12 @@ msgstr ""
|
||||
msgid "{0, plural, one {# day} other {# days}}"
|
||||
msgstr "{0, plural, one {# روز} other {# روز}}"
|
||||
|
||||
#. placeholder {0}: table.getFilteredSelectedRowModel().rows.length
|
||||
#. placeholder {1}: table.getFilteredRowModel().rows.length
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "{0} of {1} row(s) selected."
|
||||
msgstr "{0} از {1} ردیف انتخاب شده است."
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "{hours, plural, one {# hour} other {# hours}}"
|
||||
msgstr "{hours, plural, one {# ساعت} other {# ساعت}}"
|
||||
@@ -31,6 +37,11 @@ msgstr "{hours, plural, one {# ساعت} other {# ساعت}}"
|
||||
msgid "1 hour"
|
||||
msgstr "۱ ساعت"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "1 min"
|
||||
msgstr "۱ دقیقه"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 week"
|
||||
msgstr "۱ هفته"
|
||||
@@ -39,6 +50,11 @@ msgstr "۱ هفته"
|
||||
msgid "12 hours"
|
||||
msgstr "۱۲ ساعت"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "15 min"
|
||||
msgstr "۱۵ دقیقه"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "24 hours"
|
||||
msgstr "۲۴ ساعت"
|
||||
@@ -47,12 +63,22 @@ msgstr "۲۴ ساعت"
|
||||
msgid "30 days"
|
||||
msgstr "۳۰ روز"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "5 min"
|
||||
msgstr "۵ دقیقه"
|
||||
|
||||
#. Table column
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Actions"
|
||||
msgstr "عملیات"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Active"
|
||||
msgstr "فعال"
|
||||
|
||||
#: src/components/routes/home.tsx
|
||||
msgid "Active Alerts"
|
||||
msgstr " هشدارهای فعال"
|
||||
@@ -82,10 +108,16 @@ msgstr "تنظیم گزینههای نمایش برای نمودارها."
|
||||
msgid "Admin"
|
||||
msgstr "مدیر"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "عامل"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Alert History"
|
||||
msgstr "تاریخچه هشدارها"
|
||||
|
||||
#: src/components/alerts/alert-button.tsx
|
||||
#: src/components/alerts/alert-button.tsx
|
||||
msgid "Alerts"
|
||||
@@ -96,10 +128,14 @@ msgstr "هشدارها"
|
||||
msgid "All Systems"
|
||||
msgstr "همه سیستمها"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Are you sure you want to delete {name}?"
|
||||
msgstr "آیا مطمئن هستید که میخواهید {name} را حذف کنید؟"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Are you sure?"
|
||||
msgstr "آیا مطمئن هستید؟"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Automatic copy requires a secure context."
|
||||
msgstr "کپی خودکار نیاز به یک زمینه امن دارد."
|
||||
@@ -115,7 +151,7 @@ msgstr "میانگین استفاده از CPU کانتینرها"
|
||||
#. placeholder {0}: data.alert.unit
|
||||
#: src/components/alerts/alerts-system.tsx
|
||||
msgid "Average exceeds <0>{value}{0}</0>"
|
||||
msgstr ""
|
||||
msgstr "میانگین از <0>{value}{0}</0> فراتر رفته است"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Average power consumption of GPUs"
|
||||
@@ -152,11 +188,22 @@ msgstr "بِزل از <0>Shoutrrr</0> برای ادغام با سرویسها
|
||||
msgid "Binary"
|
||||
msgstr "دودویی"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bits (Kbps, Mbps, Gbps)"
|
||||
msgstr "بیت (کیلوبیت بر ثانیه، مگابیت بر ثانیه، گیگابیت بر ثانیه)"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bytes (KB/s, MB/s, GB/s)"
|
||||
msgstr "بایت (کیلوبایت بر ثانیه، مگابایت بر ثانیه، گیگابایت بر ثانیه)"
|
||||
|
||||
#: src/components/charts/mem-chart.tsx
|
||||
msgid "Cache / Buffers"
|
||||
msgstr "حافظه پنهان / بافرها"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Cancel"
|
||||
msgstr "لغو"
|
||||
|
||||
@@ -164,6 +211,14 @@ msgstr "لغو"
|
||||
msgid "Caution - potential data loss"
|
||||
msgstr "احتیاط - احتمال از دست رفتن دادهها"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Celsius (°C)"
|
||||
msgstr "سلسیوس (°C)"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change display units for metrics."
|
||||
msgstr "تغییر واحدهای نمایش برای معیارها."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change general application options."
|
||||
msgstr "تغییر گزینههای کلی برنامه."
|
||||
@@ -202,7 +257,12 @@ msgstr "نحوه دریافت هشدارهای اطلاعرسانی را پی
|
||||
msgid "Confirm password"
|
||||
msgstr "تأیید رمز عبور"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/routes/home.tsx
|
||||
msgid "Connection is down"
|
||||
msgstr "اتصال قطع است"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Continue"
|
||||
msgstr "ادامه"
|
||||
|
||||
@@ -227,7 +287,7 @@ msgctxt "Environment variables"
|
||||
msgid "Copy env"
|
||||
msgstr "کپی متغیرهای محیط"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Copy host"
|
||||
msgstr "کپی میزبان"
|
||||
|
||||
@@ -236,6 +296,10 @@ msgstr "کپی میزبان"
|
||||
msgid "Copy Linux command"
|
||||
msgstr "کپی دستور لینوکس"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Copy name"
|
||||
msgstr "کپی نام"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
msgstr "کپی متن"
|
||||
@@ -252,13 +316,13 @@ msgstr "محتوای <0>docker-compose.yml</0> عامل زیر را کپی کن
|
||||
msgid "Copy YAML"
|
||||
msgstr "کپی YAML"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "CPU"
|
||||
msgstr "پردازنده"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "CPU Usage"
|
||||
msgstr "میزان استفاده از پردازنده"
|
||||
|
||||
@@ -266,6 +330,15 @@ msgstr "میزان استفاده از پردازنده"
|
||||
msgid "Create account"
|
||||
msgstr "ایجاد حساب کاربری"
|
||||
|
||||
#. Context: date created
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Created"
|
||||
msgstr "ایجاد شده"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Critical (%)"
|
||||
msgstr "بحرانی (%)"
|
||||
|
||||
#. Dark theme
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Dark"
|
||||
@@ -280,7 +353,8 @@ msgstr "داشبورد"
|
||||
msgid "Default time period"
|
||||
msgstr "بازه زمانی پیشفرض"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Delete"
|
||||
msgstr "حذف"
|
||||
|
||||
@@ -288,7 +362,7 @@ msgstr "حذف"
|
||||
msgid "Delete fingerprint"
|
||||
msgstr "حذف اثر انگشت"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Disk"
|
||||
msgstr "دیسک"
|
||||
|
||||
@@ -296,6 +370,10 @@ msgstr "دیسک"
|
||||
msgid "Disk I/O"
|
||||
msgstr "ورودی/خروجی دیسک"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Disk unit"
|
||||
msgstr "واحد دیسک"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/disk-chart.tsx
|
||||
@@ -324,15 +402,20 @@ msgstr "مستندات"
|
||||
|
||||
#. Context: System is down
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Down"
|
||||
msgstr ""
|
||||
msgstr "قطع"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Duration"
|
||||
msgstr "مدت زمان"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Edit"
|
||||
msgstr ""
|
||||
msgstr "ویرایش"
|
||||
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
#: src/components/login/auth-form.tsx
|
||||
@@ -354,6 +437,7 @@ msgstr "آدرس ایمیل را وارد کنید..."
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Error"
|
||||
msgstr "خطا"
|
||||
@@ -369,6 +453,10 @@ msgstr "در {2, plural, one {# دقیقه} other {# دقیقه}} گذشته ا
|
||||
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
|
||||
msgstr "سیستمهای موجود که در <0>config.yml</0> تعریف نشدهاند حذف خواهند شد. لطفاً به طور منظم پشتیبانگیری کنید."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr "خروجی گرفتن"
|
||||
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
msgid "Export configuration"
|
||||
msgstr "خارج کردن پیکربندی"
|
||||
@@ -377,6 +465,10 @@ msgstr "خارج کردن پیکربندی"
|
||||
msgid "Export your current systems configuration."
|
||||
msgstr "پیکربندی سیستمهای فعلی خود را خارج کنید."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Fahrenheit (°F)"
|
||||
msgstr "فارنهایت (°F)"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Failed to authenticate"
|
||||
msgstr "احراز هویت ناموفق بود"
|
||||
@@ -396,12 +488,13 @@ msgstr "بهروزرسانی هشدار ناموفق بود"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Filter..."
|
||||
msgstr "فیلتر..."
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Fingerprint"
|
||||
msgstr ""
|
||||
msgstr "اثر انگشت"
|
||||
|
||||
#: src/components/alerts/alerts-system.tsx
|
||||
msgid "For <0>{min}</0> {min, plural, one {minute} other {minutes}}"
|
||||
@@ -448,16 +541,6 @@ msgstr "آدرس ایمیل نامعتبر است."
|
||||
msgid "Kernel"
|
||||
msgstr "هسته"
|
||||
|
||||
#. Load average 15 minutes
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "L15"
|
||||
msgstr ""
|
||||
|
||||
#. Load average 5 minutes
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "L5"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "زبان"
|
||||
@@ -471,13 +554,26 @@ msgstr "طرحبندی"
|
||||
msgid "Light"
|
||||
msgstr "روشن"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Load Average"
|
||||
msgstr "میانگین بار"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 15m"
|
||||
msgstr ""
|
||||
msgstr "میانگین بار ۱۵ دقیقه"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 1m"
|
||||
msgstr "میانگین بار ۱ دقیقه"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 5m"
|
||||
msgstr ""
|
||||
msgstr "میانگین بار ۵ دقیقه"
|
||||
|
||||
#. Short label for load average
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Load Avg"
|
||||
msgstr "میانگین بار"
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Log Out"
|
||||
@@ -507,14 +603,14 @@ msgstr "مدیریت تنظیمات نمایش و اعلانها."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Manual setup instructions"
|
||||
msgstr ""
|
||||
msgstr "دستورالعملهای راهاندازی دستی"
|
||||
|
||||
#. Chart select field. Please try to keep this short.
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Max 1 min"
|
||||
msgstr "حداکثر ۱ دقیقه"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Memory"
|
||||
msgstr "حافظه"
|
||||
|
||||
@@ -527,11 +623,12 @@ msgstr "میزان استفاده از حافظه"
|
||||
msgid "Memory usage of docker containers"
|
||||
msgstr "میزان استفاده از حافظه کانتینرهای داکر"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Name"
|
||||
msgstr "نام"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Net"
|
||||
msgstr "شبکه"
|
||||
|
||||
@@ -543,10 +640,19 @@ msgstr "ترافیک شبکه کانتینرهای داکر"
|
||||
msgid "Network traffic of public interfaces"
|
||||
msgstr "ترافیک شبکه رابطهای عمومی"
|
||||
|
||||
#. Context: Bytes or bits
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Network unit"
|
||||
msgstr "واحد شبکه"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "No results found."
|
||||
msgstr "هیچ نتیجهای یافت نشد."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "No results."
|
||||
msgstr "نتیجهای یافت نشد."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "No systems found."
|
||||
@@ -566,7 +672,7 @@ msgstr "پشتیبانی از OAuth 2 / OIDC"
|
||||
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
|
||||
msgstr "در هر بار راهاندازی مجدد، سیستمهای موجود در پایگاه داده با سیستمهای تعریف شده در فایل مطابقت داده میشوند."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Open menu"
|
||||
@@ -584,6 +690,12 @@ msgstr "بازنویسی هشدارهای موجود"
|
||||
msgid "Page"
|
||||
msgstr "صفحه"
|
||||
|
||||
#. placeholder {0}: table.getState().pagination.pageIndex + 1
|
||||
#. placeholder {1}: table.getPageCount()
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Page {0} of {1}"
|
||||
msgstr "صفحه {0} از {1}"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Pages / Settings"
|
||||
msgstr "صفحات / تنظیمات"
|
||||
@@ -599,17 +711,17 @@ msgstr "رمز عبور باید حداقل ۸ کاراکتر باشد."
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Password must be less than 72 bytes."
|
||||
msgstr ""
|
||||
msgstr "رمز عبور باید کمتر از ۷۲ بایت باشد."
|
||||
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
msgid "Password reset request received"
|
||||
msgstr "درخواست بازنشانی رمز عبور دریافت شد"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Pause"
|
||||
msgstr "توقف"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Paused"
|
||||
msgstr "مکث شده"
|
||||
|
||||
@@ -665,13 +777,12 @@ msgid "Public Key"
|
||||
msgstr "کلید عمومی"
|
||||
|
||||
#. Disk read
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Read"
|
||||
msgstr "خواندن"
|
||||
|
||||
#. Network bytes received (download)
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Received"
|
||||
msgstr "دریافت شد"
|
||||
|
||||
@@ -679,7 +790,13 @@ msgstr "دریافت شد"
|
||||
msgid "Reset Password"
|
||||
msgstr "بازنشانی رمز عبور"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Resolved"
|
||||
msgstr "حل شده"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Resume"
|
||||
msgstr "ادامه"
|
||||
|
||||
@@ -687,6 +804,10 @@ msgstr "ادامه"
|
||||
msgid "Rotate token"
|
||||
msgstr "چرخش توکن"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Rows per page"
|
||||
msgstr "ردیف در هر صفحه"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Save address using enter key or comma. Leave blank to disable email notifications."
|
||||
msgstr "آدرس را با استفاده از کلید Enter یا کاما ذخیره کنید. برای غیرفعال کردن اعلانهای ایمیلی، خالی بگذارید."
|
||||
@@ -698,7 +819,7 @@ msgstr "ذخیره تنظیمات"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Save system"
|
||||
msgstr ""
|
||||
msgstr "ذخیره سیستم"
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Search"
|
||||
@@ -712,11 +833,14 @@ msgstr "جستجو برای سیستمها یا تنظیمات..."
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "برای پیکربندی نحوه دریافت هشدارها، به <0>تنظیمات اعلان</0> مراجعه کنید."
|
||||
|
||||
#. Network bytes sent (upload)
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "ارسال شد"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "آستانه های درصدی را برای رنگ های متر تنظیم کنید."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Sets the default time range for charts when a system is viewed."
|
||||
msgstr "بازه زمانی پیشفرض برای نمودارها هنگام مشاهده یک سیستم را تعیین میکند."
|
||||
@@ -744,6 +868,11 @@ msgstr "تنظیمات SMTP"
|
||||
msgid "Sort By"
|
||||
msgstr "مرتبسازی بر اساس"
|
||||
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "وضعیت"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Status"
|
||||
msgstr "وضعیت"
|
||||
@@ -759,11 +888,16 @@ msgstr "میزان استفاده از Swap"
|
||||
#. System theme
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "System"
|
||||
msgstr "سیستم"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "System load averages over time"
|
||||
msgstr "میانگین بار سیستم در طول زمان"
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Systems"
|
||||
msgstr "سیستمها"
|
||||
@@ -777,15 +911,19 @@ msgid "Table"
|
||||
msgstr "جدول"
|
||||
|
||||
#. Temperature label in systems table
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Temp"
|
||||
msgstr ""
|
||||
msgstr "دما"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Temperature"
|
||||
msgstr "دما"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Temperature unit"
|
||||
msgstr "واحد دما"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Temperatures of system sensors"
|
||||
msgstr "دمای حسگرهای سیستم"
|
||||
@@ -802,10 +940,14 @@ msgstr "اعلان آزمایشی ارسال شد"
|
||||
msgid "Then log into the backend and reset your user account password in the users table."
|
||||
msgstr "سپس وارد بخش پشتیبان شوید و رمز عبور حساب کاربری خود را در جدول کاربران بازنشانی کنید."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "This action cannot be undone. This will permanently delete all current records for {name} from the database."
|
||||
msgstr "این عمل قابل برگشت نیست. این کار تمام رکوردهای فعلی {name} را برای همیشه از پایگاه داده حذف خواهد کرد."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "This will permanently delete all selected records from the database."
|
||||
msgstr "این کار تمام رکوردهای انتخاب شده را برای همیشه از پایگاه داده حذف خواهد کرد."
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Throughput of {extraFsName}"
|
||||
msgstr "توان عملیاتی {extraFsName}"
|
||||
@@ -846,13 +988,17 @@ msgstr "توکنها به عاملها اجازه اتصال و ثبت
|
||||
msgid "Tokens and fingerprints are used to authenticate WebSocket connections to the hub."
|
||||
msgstr "توکنها و اثرات انگشت برای احراز هویت اتصالات WebSocket به هاب استفاده میشوند."
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when 1 minute load average exceeds a threshold"
|
||||
msgstr "هنگامی که میانگین بار ۱ دقیقهای از یک آستانه فراتر رود، فعال میشود"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when 15 minute load average exceeds a threshold"
|
||||
msgstr ""
|
||||
msgstr "هنگامی که میانگین بار ۱۵ دقیقهای از یک آستانه فراتر رود، فعال میشود"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when 5 minute load average exceeds a threshold"
|
||||
msgstr ""
|
||||
msgstr "هنگامی که میانگین بار ۵ دقیقهای از یک آستانه فراتر رود، فعال میشود"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when any sensor exceeds a threshold"
|
||||
@@ -878,15 +1024,20 @@ msgstr "هنگامی که وضعیت بین بالا و پایین تغییر م
|
||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||
msgstr "هنگامی که استفاده از هر دیسکی از یک آستانه فراتر رود، فعال میشود"
|
||||
|
||||
#. Temperature / network units
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Unit preferences"
|
||||
msgstr "تنظیمات واحدها"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Universal token"
|
||||
msgstr "توکن جهانی"
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Up"
|
||||
msgstr ""
|
||||
msgstr "فعال"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Updated in real time. Click on a system to view information."
|
||||
@@ -898,7 +1049,8 @@ msgstr "آپتایم"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Usage"
|
||||
msgstr "میزان استفاده"
|
||||
|
||||
@@ -908,7 +1060,6 @@ msgstr "میزان استفاده از پارتیشن ریشه"
|
||||
|
||||
#: src/components/charts/swap-chart.tsx
|
||||
#: src/components/charts/mem-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
msgid "Used"
|
||||
msgstr "استفاده شده"
|
||||
|
||||
@@ -917,10 +1068,18 @@ msgstr "استفاده شده"
|
||||
msgid "Users"
|
||||
msgstr "کاربران"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Value"
|
||||
msgstr "مقدار"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "View"
|
||||
msgstr "مشاهده"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "View your 200 most recent alerts."
|
||||
msgstr "۲۰۰ هشدار اخیر خود را مشاهده کنید."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Visible Fields"
|
||||
msgstr "فیلدهای قابل مشاهده"
|
||||
@@ -933,6 +1092,14 @@ msgstr "در انتظار رکوردهای کافی برای نمایش"
|
||||
msgid "Want to help improve our translations? Check <0>Crowdin</0> for details."
|
||||
msgstr "میخواهید به ما کمک کنید تا ترجمههای خود را بهتر کنیم؟ برای جزئیات بیشتر به <0>Crowdin</0> مراجعه کنید."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Warning (%)"
|
||||
msgstr "هشدار (%)"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Warning thresholds"
|
||||
msgstr "آستانه های هشدار"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Webhook / Push notifications"
|
||||
msgstr "اعلانهای Webhook / Push"
|
||||
@@ -948,8 +1115,8 @@ msgid "Windows command"
|
||||
msgstr "دستور Windows"
|
||||
|
||||
#. Disk write
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Write"
|
||||
msgstr "نوشتن"
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: fr\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-03-06 07:27\n"
|
||||
"PO-Revision-Date: 2025-07-25 22:44\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: French\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
@@ -23,6 +23,12 @@ msgstr ""
|
||||
msgid "{0, plural, one {# day} other {# days}}"
|
||||
msgstr "{0, plural, one {# jour} other {# jours}}"
|
||||
|
||||
#. placeholder {0}: table.getFilteredSelectedRowModel().rows.length
|
||||
#. placeholder {1}: table.getFilteredRowModel().rows.length
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "{0} of {1} row(s) selected."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "{hours, plural, one {# hour} other {# hours}}"
|
||||
msgstr "{hours, plural, one {# heure} other {# heures}}"
|
||||
@@ -31,6 +37,11 @@ msgstr "{hours, plural, one {# heure} other {# heures}}"
|
||||
msgid "1 hour"
|
||||
msgstr "1 heure"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "1 min"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 week"
|
||||
msgstr "1 semaine"
|
||||
@@ -39,6 +50,11 @@ msgstr "1 semaine"
|
||||
msgid "12 hours"
|
||||
msgstr "12 heures"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "15 min"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "24 hours"
|
||||
msgstr "24 heures"
|
||||
@@ -47,19 +63,29 @@ msgstr "24 heures"
|
||||
msgid "30 days"
|
||||
msgstr "30 jours"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "5 min"
|
||||
msgstr ""
|
||||
|
||||
#. Table column
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Actions"
|
||||
msgstr "Actions"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Active"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/home.tsx
|
||||
msgid "Active Alerts"
|
||||
msgstr "Alertes actives"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add <0>System</0>"
|
||||
msgstr "Ajouter <0>Système</0>"
|
||||
msgstr "Ajouter <0>un Système</0>"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Add New System"
|
||||
@@ -71,7 +97,7 @@ msgstr "Ajouter un système"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Add URL"
|
||||
msgstr "Ajouter URL"
|
||||
msgstr "Ajouter l’URL"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Adjust display options for charts."
|
||||
@@ -82,10 +108,16 @@ msgstr "Ajuster les options d'affichage pour les graphiques."
|
||||
msgid "Admin"
|
||||
msgstr "Admin"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Agent"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Alert History"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/alerts/alert-button.tsx
|
||||
#: src/components/alerts/alert-button.tsx
|
||||
msgid "Alerts"
|
||||
@@ -96,10 +128,14 @@ msgstr "Alertes"
|
||||
msgid "All Systems"
|
||||
msgstr "Tous les systèmes"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Are you sure you want to delete {name}?"
|
||||
msgstr "Êtes-vous sûr de vouloir supprimer {name} ?"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Are you sure?"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Automatic copy requires a secure context."
|
||||
msgstr "La copie automatique nécessite un contexte sécurisé."
|
||||
@@ -152,11 +188,22 @@ msgstr "Beszel utilise <0>Shoutrrr</0> pour s'intégrer aux services de notifica
|
||||
msgid "Binary"
|
||||
msgstr "Binaire"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bits (Kbps, Mbps, Gbps)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bytes (KB/s, MB/s, GB/s)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/charts/mem-chart.tsx
|
||||
msgid "Cache / Buffers"
|
||||
msgstr "Cache / Tampons"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Cancel"
|
||||
msgstr "Annuler"
|
||||
|
||||
@@ -164,6 +211,14 @@ msgstr "Annuler"
|
||||
msgid "Caution - potential data loss"
|
||||
msgstr "Attention - perte de données potentielle"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Celsius (°C)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change display units for metrics."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change general application options."
|
||||
msgstr "Modifier les options générales de l'application."
|
||||
@@ -202,7 +257,12 @@ msgstr "Configurez comment vous recevez les notifications d'alerte."
|
||||
msgid "Confirm password"
|
||||
msgstr "Confirmer le mot de passe"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/routes/home.tsx
|
||||
msgid "Connection is down"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Continue"
|
||||
msgstr "Continuer"
|
||||
|
||||
@@ -227,7 +287,7 @@ msgctxt "Environment variables"
|
||||
msgid "Copy env"
|
||||
msgstr "Copier env"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Copy host"
|
||||
msgstr "Copier l'hôte"
|
||||
|
||||
@@ -236,6 +296,10 @@ msgstr "Copier l'hôte"
|
||||
msgid "Copy Linux command"
|
||||
msgstr "Copier la commande Linux"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Copy name"
|
||||
msgstr "Copier le nom"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
msgstr "Copier le texte"
|
||||
@@ -252,13 +316,13 @@ msgstr "Copiez le contenu du<0>docker-compose.yml</0> pour l'agent ci-dessous, o
|
||||
msgid "Copy YAML"
|
||||
msgstr "Copier YAML"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "CPU"
|
||||
msgstr "CPU"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "CPU Usage"
|
||||
msgstr "Utilisation du CPU"
|
||||
|
||||
@@ -266,6 +330,15 @@ msgstr "Utilisation du CPU"
|
||||
msgid "Create account"
|
||||
msgstr "Créer un compte"
|
||||
|
||||
#. Context: date created
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Created"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Critical (%)"
|
||||
msgstr "Critique (%)"
|
||||
|
||||
#. Dark theme
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Dark"
|
||||
@@ -280,7 +353,8 @@ msgstr "Tableau de bord"
|
||||
msgid "Default time period"
|
||||
msgstr "Période par défaut"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Delete"
|
||||
msgstr "Supprimer"
|
||||
|
||||
@@ -288,7 +362,7 @@ msgstr "Supprimer"
|
||||
msgid "Delete fingerprint"
|
||||
msgstr "Supprimer l'empreinte"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Disk"
|
||||
msgstr "Disque"
|
||||
|
||||
@@ -296,6 +370,10 @@ msgstr "Disque"
|
||||
msgid "Disk I/O"
|
||||
msgstr "Entrée/Sortie disque"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Disk unit"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/disk-chart.tsx
|
||||
@@ -324,13 +402,18 @@ msgstr "Documentation"
|
||||
|
||||
#. Context: System is down
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Down"
|
||||
msgstr "Injoignable"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Duration"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Edit"
|
||||
msgstr "Éditer"
|
||||
|
||||
@@ -354,6 +437,7 @@ msgstr "Entrez l'adresse email..."
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Error"
|
||||
msgstr "Erreur"
|
||||
@@ -369,6 +453,10 @@ msgstr "Dépasse {0}{1} dans {2, plural, one {la dernière # minute} other {les
|
||||
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
|
||||
msgstr "Les systèmes existants non définis dans <0>config.yml</0> seront supprimés. Veuillez faire des sauvegardes régulières."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
msgid "Export configuration"
|
||||
msgstr "Exporter la configuration"
|
||||
@@ -377,6 +465,10 @@ msgstr "Exporter la configuration"
|
||||
msgid "Export your current systems configuration."
|
||||
msgstr "Exportez la configuration actuelle de vos systèmes."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Fahrenheit (°F)"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Failed to authenticate"
|
||||
msgstr "Échec de l'authentification"
|
||||
@@ -396,6 +488,7 @@ msgstr "Échec de la mise à jour de l'alerte"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Filter..."
|
||||
msgstr "Filtrer..."
|
||||
|
||||
@@ -448,16 +541,6 @@ msgstr "Adresse email invalide."
|
||||
msgid "Kernel"
|
||||
msgstr "Noyau"
|
||||
|
||||
#. Load average 15 minutes
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "L15"
|
||||
msgstr ""
|
||||
|
||||
#. Load average 5 minutes
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "L5"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "Langue"
|
||||
@@ -471,14 +554,27 @@ msgstr "Disposition"
|
||||
msgid "Light"
|
||||
msgstr "Clair"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Load Average"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 15m"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 1m"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 5m"
|
||||
msgstr ""
|
||||
|
||||
#. Short label for load average
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Load Avg"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Log Out"
|
||||
msgstr "Déconnexion"
|
||||
@@ -514,7 +610,7 @@ msgstr "Guide pour une installation manuelle"
|
||||
msgid "Max 1 min"
|
||||
msgstr "Max 1 min"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Memory"
|
||||
msgstr "Mémoire"
|
||||
|
||||
@@ -527,11 +623,12 @@ msgstr "Utilisation de la mémoire"
|
||||
msgid "Memory usage of docker containers"
|
||||
msgstr "Utilisation de la mémoire des conteneurs Docker"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Name"
|
||||
msgstr "Nom"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Net"
|
||||
msgstr "Net"
|
||||
|
||||
@@ -543,10 +640,19 @@ msgstr "Trafic réseau des conteneurs Docker"
|
||||
msgid "Network traffic of public interfaces"
|
||||
msgstr "Trafic réseau des interfaces publiques"
|
||||
|
||||
#. Context: Bytes or bits
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Network unit"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "No results found."
|
||||
msgstr "Aucun résultat trouvé."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "No results."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "No systems found."
|
||||
@@ -566,7 +672,7 @@ msgstr "Support OAuth 2 / OIDC"
|
||||
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
|
||||
msgstr "À chaque redémarrage, les systèmes dans la base de données seront mis à jour pour correspondre aux systèmes définis dans le fichier."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Open menu"
|
||||
@@ -584,6 +690,12 @@ msgstr "Écraser les alertes existantes"
|
||||
msgid "Page"
|
||||
msgstr "Page"
|
||||
|
||||
#. placeholder {0}: table.getState().pagination.pageIndex + 1
|
||||
#. placeholder {1}: table.getPageCount()
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Page {0} of {1}"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Pages / Settings"
|
||||
msgstr "Pages / Paramètres"
|
||||
@@ -605,11 +717,11 @@ msgstr "Le mot de passe doit être inférieur à 72 Octets."
|
||||
msgid "Password reset request received"
|
||||
msgstr "Demande de réinitialisation du mot de passe reçue"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Pause"
|
||||
msgstr "En pause"
|
||||
msgstr "Pause"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Paused"
|
||||
msgstr "En pause"
|
||||
|
||||
@@ -665,13 +777,12 @@ msgid "Public Key"
|
||||
msgstr "Clé publique"
|
||||
|
||||
#. Disk read
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Read"
|
||||
msgstr "Lecture"
|
||||
|
||||
#. Network bytes received (download)
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Received"
|
||||
msgstr "Reçu"
|
||||
|
||||
@@ -679,7 +790,13 @@ msgstr "Reçu"
|
||||
msgid "Reset Password"
|
||||
msgstr "Réinitialiser le mot de passe"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Resolved"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Reprendre"
|
||||
|
||||
@@ -687,6 +804,10 @@ msgstr "Reprendre"
|
||||
msgid "Rotate token"
|
||||
msgstr "Faire tourner le token"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Rows per page"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Save address using enter key or comma. Leave blank to disable email notifications."
|
||||
msgstr "Enregistrez l'adresse en utilisant la touche Entrée ou la virgule. Laissez vide pour désactiver les notifications par email."
|
||||
@@ -712,11 +833,14 @@ msgstr "Rechercher des systèmes ou des paramètres..."
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "Voir les <0>paramètres de notification</0> pour configurer comment vous recevez les alertes."
|
||||
|
||||
#. Network bytes sent (upload)
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "Envoyé"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "Définir des seuils de pourcentage pour les couleurs des compteurs."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Sets the default time range for charts when a system is viewed."
|
||||
msgstr "Définit la plage de temps par défaut pour les graphiques lorsqu'un système est consulté."
|
||||
@@ -744,6 +868,11 @@ msgstr "Paramètres SMTP"
|
||||
msgid "Sort By"
|
||||
msgstr "Trier par"
|
||||
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "State"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Status"
|
||||
msgstr "Statut"
|
||||
@@ -759,11 +888,16 @@ msgstr "Utilisation du swap"
|
||||
#. System theme
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "System"
|
||||
msgstr "Système"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "System load averages over time"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Systems"
|
||||
msgstr "Systèmes"
|
||||
@@ -777,7 +911,7 @@ msgid "Table"
|
||||
msgstr "Tableau"
|
||||
|
||||
#. Temperature label in systems table
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Temp"
|
||||
msgstr "Temp."
|
||||
|
||||
@@ -786,6 +920,10 @@ msgstr "Temp."
|
||||
msgid "Temperature"
|
||||
msgstr "Température"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Temperature unit"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Temperatures of system sensors"
|
||||
msgstr "Températures des capteurs du système"
|
||||
@@ -802,10 +940,14 @@ msgstr "Notification de test envoyée"
|
||||
msgid "Then log into the backend and reset your user account password in the users table."
|
||||
msgstr "Ensuite, connectez-vous au backend et réinitialisez le mot de passe de votre compte utilisateur dans la table des utilisateurs."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "This action cannot be undone. This will permanently delete all current records for {name} from the database."
|
||||
msgstr "Cette action ne peut pas être annulée. Cela supprimera définitivement tous les enregistrements actuels pour {name} de la base de données."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "This will permanently delete all selected records from the database."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Throughput of {extraFsName}"
|
||||
msgstr "Débit de {extraFsName}"
|
||||
@@ -830,7 +972,7 @@ msgstr "Changer le thème"
|
||||
#: 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/tokens-fingerprints.tsx
|
||||
@@ -846,6 +988,10 @@ msgstr "Les tokens permettent aux agents de se connecter et de s'enregistrer. Le
|
||||
msgid "Tokens and fingerprints are used to authenticate WebSocket connections to the hub."
|
||||
msgstr "Les tokens et les empreintes sont utilisés pour authentifier les connexions WebSocket vers le hub."
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when 1 minute load average exceeds a threshold"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when 15 minute load average exceeds a threshold"
|
||||
msgstr ""
|
||||
@@ -878,12 +1024,17 @@ msgstr "Se déclenche lorsque le statut passe de \"Joignable\" à \"Injoignable\
|
||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||
msgstr "Déclenchement lorsque l'utilisation de tout disque dépasse un seuil"
|
||||
|
||||
#. Temperature / network units
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Unit preferences"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Universal token"
|
||||
msgstr "Token universel"
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Up"
|
||||
msgstr "Joignable"
|
||||
@@ -898,7 +1049,8 @@ msgstr "Temps de fonctionnement"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Usage"
|
||||
msgstr "Utilisation"
|
||||
|
||||
@@ -908,7 +1060,6 @@ msgstr "Utilisation de la partition racine"
|
||||
|
||||
#: src/components/charts/swap-chart.tsx
|
||||
#: src/components/charts/mem-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
msgid "Used"
|
||||
msgstr "Utilisé"
|
||||
|
||||
@@ -917,10 +1068,18 @@ msgstr "Utilisé"
|
||||
msgid "Users"
|
||||
msgstr "Utilisateurs"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Value"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "View"
|
||||
msgstr "Vue"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "View your 200 most recent alerts."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Visible Fields"
|
||||
msgstr "Colonnes visibles"
|
||||
@@ -933,6 +1092,14 @@ msgstr "En attente de suffisamment d'enregistrements à afficher"
|
||||
msgid "Want to help improve our translations? Check <0>Crowdin</0> for details."
|
||||
msgstr "Vous voulez nous aider à améliorer nos traductions ? Consultez <0>Crowdin</0> pour plus de détails."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Warning (%)"
|
||||
msgstr "Avertissement (%)"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Warning thresholds"
|
||||
msgstr "Seuils d'avertissement"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Webhook / Push notifications"
|
||||
msgstr "Notifications Webhook / Push"
|
||||
@@ -948,8 +1115,8 @@ msgid "Windows command"
|
||||
msgstr "Commande Windows"
|
||||
|
||||
#. Disk write
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Write"
|
||||
msgstr "Écriture"
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: hr\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-03-06 07:27\n"
|
||||
"PO-Revision-Date: 2025-07-25 22:44\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"
|
||||
@@ -23,6 +23,12 @@ msgstr ""
|
||||
msgid "{0, plural, one {# day} other {# days}}"
|
||||
msgstr "{0, plural, one {# dan} other {# dani}}"
|
||||
|
||||
#. placeholder {0}: table.getFilteredSelectedRowModel().rows.length
|
||||
#. placeholder {1}: table.getFilteredRowModel().rows.length
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "{0} of {1} row(s) selected."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "{hours, plural, one {# hour} other {# hours}}"
|
||||
msgstr "{hours, plural, one {# sat} other {# sati}}"
|
||||
@@ -31,6 +37,11 @@ msgstr "{hours, plural, one {# sat} other {# sati}}"
|
||||
msgid "1 hour"
|
||||
msgstr "1 sat"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "1 min"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 week"
|
||||
msgstr "1 tjedan"
|
||||
@@ -39,6 +50,11 @@ msgstr "1 tjedan"
|
||||
msgid "12 hours"
|
||||
msgstr "12 sati"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "15 min"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "24 hours"
|
||||
msgstr "24 sati"
|
||||
@@ -47,12 +63,22 @@ msgstr "24 sati"
|
||||
msgid "30 days"
|
||||
msgstr "30 dana"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "5 min"
|
||||
msgstr ""
|
||||
|
||||
#. Table column
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Actions"
|
||||
msgstr "Akcije"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Active"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/home.tsx
|
||||
msgid "Active Alerts"
|
||||
msgstr "Aktivna upozorenja"
|
||||
@@ -82,10 +108,16 @@ msgstr "Podesite opcije prikaza za grafikone."
|
||||
msgid "Admin"
|
||||
msgstr "Admin"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Agent"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Alert History"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/alerts/alert-button.tsx
|
||||
#: src/components/alerts/alert-button.tsx
|
||||
msgid "Alerts"
|
||||
@@ -96,10 +128,14 @@ msgstr "Upozorenja"
|
||||
msgid "All Systems"
|
||||
msgstr "Svi Sistemi"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Are you sure you want to delete {name}?"
|
||||
msgstr "Jeste li sigurni da želite izbrisati {name}?"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Are you sure?"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Automatic copy requires a secure context."
|
||||
msgstr "Automatsko kopiranje zahtijeva siguran kontekst."
|
||||
@@ -152,11 +188,22 @@ msgstr "Beszel koristi <0>Shoutrrr</0> za integraciju sa popularnim servisima za
|
||||
msgid "Binary"
|
||||
msgstr "Binarni"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bits (Kbps, Mbps, Gbps)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bytes (KB/s, MB/s, GB/s)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/charts/mem-chart.tsx
|
||||
msgid "Cache / Buffers"
|
||||
msgstr "Predmemorija / Međuspremnici"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Cancel"
|
||||
msgstr "Otkaži"
|
||||
|
||||
@@ -164,6 +211,14 @@ msgstr "Otkaži"
|
||||
msgid "Caution - potential data loss"
|
||||
msgstr "Oprez - mogući gubitak podataka"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Celsius (°C)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change display units for metrics."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change general application options."
|
||||
msgstr "Promijenite opće opcije aplikacije."
|
||||
@@ -202,7 +257,12 @@ msgstr "Konfigurirajte način primanja obavijesti upozorenja."
|
||||
msgid "Confirm password"
|
||||
msgstr "Potvrdite lozinku"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/routes/home.tsx
|
||||
msgid "Connection is down"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Continue"
|
||||
msgstr "Nastavite"
|
||||
|
||||
@@ -227,7 +287,7 @@ msgctxt "Environment variables"
|
||||
msgid "Copy env"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Copy host"
|
||||
msgstr "Kopiraj hosta"
|
||||
|
||||
@@ -236,6 +296,10 @@ msgstr "Kopiraj hosta"
|
||||
msgid "Copy Linux command"
|
||||
msgstr "Kopiraj Linux komandu"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Copy name"
|
||||
msgstr "Kopiraj naziv"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
msgstr "Kopiraj tekst"
|
||||
@@ -252,13 +316,13 @@ msgstr ""
|
||||
msgid "Copy YAML"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "CPU"
|
||||
msgstr "Procesor"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "CPU Usage"
|
||||
msgstr "Iskorištenost procesora"
|
||||
|
||||
@@ -266,6 +330,15 @@ msgstr "Iskorištenost procesora"
|
||||
msgid "Create account"
|
||||
msgstr "Napravite račun"
|
||||
|
||||
#. Context: date created
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Created"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Critical (%)"
|
||||
msgstr "Kritično (%)"
|
||||
|
||||
#. Dark theme
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Dark"
|
||||
@@ -280,7 +353,8 @@ msgstr "Nadzorna ploča"
|
||||
msgid "Default time period"
|
||||
msgstr "Zadano vremensko razdoblje"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Delete"
|
||||
msgstr "Izbriši"
|
||||
|
||||
@@ -288,7 +362,7 @@ msgstr "Izbriši"
|
||||
msgid "Delete fingerprint"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Disk"
|
||||
msgstr "Disk"
|
||||
|
||||
@@ -296,6 +370,10 @@ msgstr "Disk"
|
||||
msgid "Disk I/O"
|
||||
msgstr "Disk I/O"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Disk unit"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/disk-chart.tsx
|
||||
@@ -324,13 +402,18 @@ msgstr "Dokumentacija"
|
||||
|
||||
#. Context: System is down
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Down"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Duration"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Edit"
|
||||
msgstr ""
|
||||
|
||||
@@ -354,6 +437,7 @@ msgstr "Unesite email adresu..."
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Error"
|
||||
msgstr "Greška"
|
||||
@@ -369,6 +453,10 @@ msgstr "Premašuje {0}{1} u posljednjih {2, plural, one {# minuta} other {# minu
|
||||
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
|
||||
msgstr "Postojeći sistemi koji nisu definirani u <0>config.yml</0> će biti izbrisani. Molimo Vas napravite redovite sigurnosne kopije."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
msgid "Export configuration"
|
||||
msgstr "Izvoz konfiguracije"
|
||||
@@ -377,6 +465,10 @@ msgstr "Izvoz konfiguracije"
|
||||
msgid "Export your current systems configuration."
|
||||
msgstr "Izvoz trenutne sistemske konfiguracije."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Fahrenheit (°F)"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Failed to authenticate"
|
||||
msgstr "Provjera autentičnosti nije uspjela"
|
||||
@@ -396,6 +488,7 @@ msgstr "Ažuriranje upozorenja nije uspjelo"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Filter..."
|
||||
msgstr "Filter..."
|
||||
|
||||
@@ -448,16 +541,6 @@ msgstr "Nevažeća adresa e-pošte."
|
||||
msgid "Kernel"
|
||||
msgstr "Kernel"
|
||||
|
||||
#. Load average 15 minutes
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "L15"
|
||||
msgstr ""
|
||||
|
||||
#. Load average 5 minutes
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "L5"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "Jezik"
|
||||
@@ -471,14 +554,27 @@ msgstr "Izgled"
|
||||
msgid "Light"
|
||||
msgstr "Svijetlo"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Load Average"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 15m"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 1m"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 5m"
|
||||
msgstr ""
|
||||
|
||||
#. Short label for load average
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Load Avg"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Log Out"
|
||||
msgstr "Odjava"
|
||||
@@ -514,7 +610,7 @@ msgstr ""
|
||||
msgid "Max 1 min"
|
||||
msgstr "Maksimalno 1 minuta"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Memory"
|
||||
msgstr "Memorija"
|
||||
|
||||
@@ -527,11 +623,12 @@ msgstr "Upotreba memorije"
|
||||
msgid "Memory usage of docker containers"
|
||||
msgstr "Upotreba memorije Docker spremnika"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Name"
|
||||
msgstr "Ime"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Net"
|
||||
msgstr "Mreža"
|
||||
|
||||
@@ -543,10 +640,19 @@ msgstr "Mrežni promet Docker spremnika"
|
||||
msgid "Network traffic of public interfaces"
|
||||
msgstr "Mrežni promet javnih sučelja"
|
||||
|
||||
#. Context: Bytes or bits
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Network unit"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "No results found."
|
||||
msgstr "Nema rezultata."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "No results."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "No systems found."
|
||||
@@ -566,7 +672,7 @@ msgstr "Podrška za OAuth 2 / OIDC"
|
||||
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
|
||||
msgstr "Prilikom svakog ponovnog pokretanja, sustavi u bazi podataka biti će ažurirani kako bi odgovarali sustavima definiranim u datoteci."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Open menu"
|
||||
@@ -584,6 +690,12 @@ msgstr "Prebrišite postojeća upozorenja"
|
||||
msgid "Page"
|
||||
msgstr "Stranica"
|
||||
|
||||
#. placeholder {0}: table.getState().pagination.pageIndex + 1
|
||||
#. placeholder {1}: table.getPageCount()
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Page {0} of {1}"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Pages / Settings"
|
||||
msgstr "Stranice / Postavke"
|
||||
@@ -605,11 +717,11 @@ msgstr ""
|
||||
msgid "Password reset request received"
|
||||
msgstr "Zahtjev za ponovno postavljanje lozinke primljen"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Pause"
|
||||
msgstr "Pauza"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Paused"
|
||||
msgstr "Pauzirano"
|
||||
|
||||
@@ -665,13 +777,12 @@ msgid "Public Key"
|
||||
msgstr "Javni Ključ"
|
||||
|
||||
#. Disk read
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Read"
|
||||
msgstr "Pročitaj"
|
||||
|
||||
#. Network bytes received (download)
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Received"
|
||||
msgstr "Primljeno"
|
||||
|
||||
@@ -679,7 +790,13 @@ msgstr "Primljeno"
|
||||
msgid "Reset Password"
|
||||
msgstr "Resetiraj Lozinku"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Resolved"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Nastavi"
|
||||
|
||||
@@ -687,6 +804,10 @@ msgstr "Nastavi"
|
||||
msgid "Rotate token"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Rows per page"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Save address using enter key or comma. Leave blank to disable email notifications."
|
||||
msgstr "Spremite adresu pomoću tipke enter ili zareza. Ostavite prazno kako biste onemogućili obavijesti e-poštom."
|
||||
@@ -712,11 +833,14 @@ msgstr "Pretraži za sisteme ili postavke..."
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "Pogledajte <0>postavke obavijesti</0> da biste konfigurirali način primanja upozorenja."
|
||||
|
||||
#. Network bytes sent (upload)
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "Poslano"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "Postavite pragove postotka za boje mjerača."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Sets the default time range for charts when a system is viewed."
|
||||
msgstr "Postavlja zadani vremenski raspon za grafikone kada se sustav gleda."
|
||||
@@ -744,6 +868,11 @@ msgstr "SMTP postavke"
|
||||
msgid "Sort By"
|
||||
msgstr "Sortiraj po"
|
||||
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "State"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Status"
|
||||
msgstr "Status"
|
||||
@@ -759,11 +888,16 @@ msgstr "Swap Iskorištenost"
|
||||
#. System theme
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "System"
|
||||
msgstr "Sistem"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "System load averages over time"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Systems"
|
||||
msgstr "Sistemi"
|
||||
@@ -777,7 +911,7 @@ msgid "Table"
|
||||
msgstr "Tablica"
|
||||
|
||||
#. Temperature label in systems table
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Temp"
|
||||
msgstr ""
|
||||
|
||||
@@ -786,6 +920,10 @@ msgstr ""
|
||||
msgid "Temperature"
|
||||
msgstr "Temperatura"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Temperature unit"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Temperatures of system sensors"
|
||||
msgstr "Temperature sistemskih senzora"
|
||||
@@ -802,10 +940,14 @@ msgstr "Testna obavijest poslana"
|
||||
msgid "Then log into the backend and reset your user account password in the users table."
|
||||
msgstr "Zatim se prijavite u backend i resetirajte lozinku korisničkog računa u tablici korisnika."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "This action cannot be undone. This will permanently delete all current records for {name} from the database."
|
||||
msgstr "Ova radnja se ne može poništiti. Ovo će trajno izbrisati sve trenutne zapise za {name} iz baze podataka."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "This will permanently delete all selected records from the database."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Throughput of {extraFsName}"
|
||||
msgstr "Protok {extraFsName}"
|
||||
@@ -846,6 +988,10 @@ msgstr ""
|
||||
msgid "Tokens and fingerprints are used to authenticate WebSocket connections to the hub."
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when 1 minute load average exceeds a threshold"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when 15 minute load average exceeds a threshold"
|
||||
msgstr ""
|
||||
@@ -878,12 +1024,17 @@ msgstr "Pokreće se kada se status sistema promijeni"
|
||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||
msgstr "Pokreće se kada iskorištenost bilo kojeg diska premaši prag"
|
||||
|
||||
#. Temperature / network units
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Unit preferences"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Universal token"
|
||||
msgstr ""
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Up"
|
||||
msgstr ""
|
||||
@@ -898,7 +1049,8 @@ msgstr "Vrijeme rada"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Usage"
|
||||
msgstr "Iskorištenost"
|
||||
|
||||
@@ -908,7 +1060,6 @@ msgstr "Iskorištenost root datotečnog sustava"
|
||||
|
||||
#: src/components/charts/swap-chart.tsx
|
||||
#: src/components/charts/mem-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
msgid "Used"
|
||||
msgstr "Iskorišteno"
|
||||
|
||||
@@ -917,10 +1068,18 @@ msgstr "Iskorišteno"
|
||||
msgid "Users"
|
||||
msgstr "Korisnici"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Value"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "View"
|
||||
msgstr "Prikaz"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "View your 200 most recent alerts."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Visible Fields"
|
||||
msgstr "Vidljiva polja"
|
||||
@@ -933,6 +1092,14 @@ msgstr "Čeka se na više podataka prije prikaza"
|
||||
msgid "Want to help improve our translations? Check <0>Crowdin</0> for details."
|
||||
msgstr "Želite li nam pomoći da naše prijevode učinimo još boljim? Posjetite <0>Crowdin</0> za više detalja."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Warning (%)"
|
||||
msgstr "Upozorenje (%)"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Warning thresholds"
|
||||
msgstr "Pragovi upozorenja"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Webhook / Push notifications"
|
||||
msgstr "Webhook / Push obavijest"
|
||||
@@ -948,8 +1115,8 @@ msgid "Windows command"
|
||||
msgstr "Windows naredba"
|
||||
|
||||
#. Disk write
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Write"
|
||||
msgstr "Piši"
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: hu\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-03-06 07:27\n"
|
||||
"PO-Revision-Date: 2025-07-25 22:44\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Hungarian\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -23,6 +23,12 @@ msgstr ""
|
||||
msgid "{0, plural, one {# day} other {# days}}"
|
||||
msgstr "{0, plural, one {# nap} other {# nap}}"
|
||||
|
||||
#. placeholder {0}: table.getFilteredSelectedRowModel().rows.length
|
||||
#. placeholder {1}: table.getFilteredRowModel().rows.length
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "{0} of {1} row(s) selected."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "{hours, plural, one {# hour} other {# hours}}"
|
||||
msgstr "{hours, plural, one {# óra} other {# óra}}"
|
||||
@@ -31,6 +37,11 @@ msgstr "{hours, plural, one {# óra} other {# óra}}"
|
||||
msgid "1 hour"
|
||||
msgstr "1 óra"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "1 min"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 week"
|
||||
msgstr "1 hét"
|
||||
@@ -39,6 +50,11 @@ msgstr "1 hét"
|
||||
msgid "12 hours"
|
||||
msgstr "12 óra"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "15 min"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "24 hours"
|
||||
msgstr "24 óra"
|
||||
@@ -47,12 +63,22 @@ msgstr "24 óra"
|
||||
msgid "30 days"
|
||||
msgstr "30 nap"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "5 min"
|
||||
msgstr ""
|
||||
|
||||
#. Table column
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Actions"
|
||||
msgstr "Műveletek"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Active"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/home.tsx
|
||||
msgid "Active Alerts"
|
||||
msgstr "Aktív riasztások"
|
||||
@@ -82,10 +108,16 @@ msgstr "Állítsa be a diagram megjelenítését."
|
||||
msgid "Admin"
|
||||
msgstr "Admin"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Ügynök"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Alert History"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/alerts/alert-button.tsx
|
||||
#: src/components/alerts/alert-button.tsx
|
||||
msgid "Alerts"
|
||||
@@ -96,10 +128,14 @@ msgstr "Riasztások"
|
||||
msgid "All Systems"
|
||||
msgstr "Minden rendszer"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Are you sure you want to delete {name}?"
|
||||
msgstr "Biztosan törölni szeretnéd {name}-t?"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Are you sure?"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Automatic copy requires a secure context."
|
||||
msgstr "Az automatikus másolás biztonságos környezetet igényel."
|
||||
@@ -152,11 +188,22 @@ msgstr "A Beszel a <0>Shoutrrr</0>-t használja a népszerű értesítési szolg
|
||||
msgid "Binary"
|
||||
msgstr "Bináris"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bits (Kbps, Mbps, Gbps)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bytes (KB/s, MB/s, GB/s)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/charts/mem-chart.tsx
|
||||
msgid "Cache / Buffers"
|
||||
msgstr "Gyorsítótár / Pufferelések"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Cancel"
|
||||
msgstr "Mégsem"
|
||||
|
||||
@@ -164,6 +211,14 @@ msgstr "Mégsem"
|
||||
msgid "Caution - potential data loss"
|
||||
msgstr "Figyelem - potenciális adatvesztés"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Celsius (°C)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change display units for metrics."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change general application options."
|
||||
msgstr "Általános alkalmazásbeállítások módosítása."
|
||||
@@ -202,7 +257,12 @@ msgstr "Konfiguráld, hogyan kapod az értesítéseket."
|
||||
msgid "Confirm password"
|
||||
msgstr "Jelszó megerősítése"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/routes/home.tsx
|
||||
msgid "Connection is down"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Continue"
|
||||
msgstr "Tovább"
|
||||
|
||||
@@ -227,7 +287,7 @@ msgctxt "Environment variables"
|
||||
msgid "Copy env"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Copy host"
|
||||
msgstr "Hoszt másolása"
|
||||
|
||||
@@ -236,6 +296,10 @@ msgstr "Hoszt másolása"
|
||||
msgid "Copy Linux command"
|
||||
msgstr "Linux parancs másolása"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Copy name"
|
||||
msgstr "Név másolása"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
msgstr "Szöveg másolása"
|
||||
@@ -252,13 +316,13 @@ msgstr ""
|
||||
msgid "Copy YAML"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "CPU"
|
||||
msgstr "CPU"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "CPU Usage"
|
||||
msgstr "CPU használat"
|
||||
|
||||
@@ -266,6 +330,15 @@ msgstr "CPU használat"
|
||||
msgid "Create account"
|
||||
msgstr "Fiók létrehozása"
|
||||
|
||||
#. Context: date created
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Created"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Critical (%)"
|
||||
msgstr "Kritikus (%)"
|
||||
|
||||
#. Dark theme
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Dark"
|
||||
@@ -280,7 +353,8 @@ msgstr "Áttekintés"
|
||||
msgid "Default time period"
|
||||
msgstr "Alapértelmezett időszak"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Delete"
|
||||
msgstr "Törlés"
|
||||
|
||||
@@ -288,7 +362,7 @@ msgstr "Törlés"
|
||||
msgid "Delete fingerprint"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Disk"
|
||||
msgstr "Lemez"
|
||||
|
||||
@@ -296,6 +370,10 @@ msgstr "Lemez"
|
||||
msgid "Disk I/O"
|
||||
msgstr "Lemez I/O"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Disk unit"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/disk-chart.tsx
|
||||
@@ -324,13 +402,18 @@ msgstr "Dokumentáció"
|
||||
|
||||
#. Context: System is down
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Down"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Duration"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Edit"
|
||||
msgstr ""
|
||||
|
||||
@@ -354,6 +437,7 @@ msgstr "Adja meg az e-mail címet..."
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Error"
|
||||
msgstr "Hiba"
|
||||
@@ -369,6 +453,10 @@ msgstr "Túllépi a {0}{1} értéket az elmúlt {2, plural, one {# percben} othe
|
||||
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
|
||||
msgstr "A <0>config.yml</0> fájlban nem definiált meglévő rendszerek törlésre kerülnek. Kérjük, készítsen rendszeres biztonsági mentéseket."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
msgid "Export configuration"
|
||||
msgstr "Konfiguráció exportálása"
|
||||
@@ -377,6 +465,10 @@ msgstr "Konfiguráció exportálása"
|
||||
msgid "Export your current systems configuration."
|
||||
msgstr "Exportálja a jelenlegi rendszerkonfigurációt."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Fahrenheit (°F)"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Failed to authenticate"
|
||||
msgstr "Hitelesítés sikertelen"
|
||||
@@ -396,6 +488,7 @@ msgstr "Nem sikerült frissíteni a riasztást"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Filter..."
|
||||
msgstr "Szűrő..."
|
||||
|
||||
@@ -448,16 +541,6 @@ msgstr "Érvénytelen e-mail cím."
|
||||
msgid "Kernel"
|
||||
msgstr "Kernel"
|
||||
|
||||
#. Load average 15 minutes
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "L15"
|
||||
msgstr ""
|
||||
|
||||
#. Load average 5 minutes
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "L5"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "Nyelv"
|
||||
@@ -471,14 +554,27 @@ msgstr "Elrendezés"
|
||||
msgid "Light"
|
||||
msgstr "Világos"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Load Average"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 15m"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 1m"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 5m"
|
||||
msgstr ""
|
||||
|
||||
#. Short label for load average
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Load Avg"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Log Out"
|
||||
msgstr "Kijelentkezés"
|
||||
@@ -514,7 +610,7 @@ msgstr ""
|
||||
msgid "Max 1 min"
|
||||
msgstr "Maximum 1 perc"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Memory"
|
||||
msgstr "RAM"
|
||||
|
||||
@@ -527,11 +623,12 @@ msgstr "Memóriahasználat"
|
||||
msgid "Memory usage of docker containers"
|
||||
msgstr "Docker konténerek memória használata"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Name"
|
||||
msgstr "Név"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Net"
|
||||
msgstr "Hálózat"
|
||||
|
||||
@@ -543,10 +640,19 @@ msgstr "Docker konténerek hálózati forgalma"
|
||||
msgid "Network traffic of public interfaces"
|
||||
msgstr "Nyilvános interfészek hálózati forgalma"
|
||||
|
||||
#. Context: Bytes or bits
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Network unit"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "No results found."
|
||||
msgstr "Nincs találat."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "No results."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "No systems found."
|
||||
@@ -566,7 +672,7 @@ msgstr "OAuth 2 / OIDC támogatás"
|
||||
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
|
||||
msgstr "Minden újraindításkor az adatbázisban lévő rendszerek frissítésre kerülnek, hogy megfeleljenek a fájlban meghatározott rendszereknek."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Open menu"
|
||||
@@ -584,6 +690,12 @@ msgstr "Felülírja a meglévő riasztásokat"
|
||||
msgid "Page"
|
||||
msgstr "Oldal"
|
||||
|
||||
#. placeholder {0}: table.getState().pagination.pageIndex + 1
|
||||
#. placeholder {1}: table.getPageCount()
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Page {0} of {1}"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Pages / Settings"
|
||||
msgstr "Oldalak / Beállítások"
|
||||
@@ -605,11 +717,11 @@ msgstr ""
|
||||
msgid "Password reset request received"
|
||||
msgstr "Jelszó-visszaállítási kérelmet kaptunk"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Pause"
|
||||
msgstr "Szüneteltetés"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Paused"
|
||||
msgstr "Szüneteltetve"
|
||||
|
||||
@@ -665,13 +777,12 @@ msgid "Public Key"
|
||||
msgstr "Nyilvános kulcs"
|
||||
|
||||
#. Disk read
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Read"
|
||||
msgstr "Olvasás"
|
||||
|
||||
#. Network bytes received (download)
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Received"
|
||||
msgstr "Fogadott"
|
||||
|
||||
@@ -679,7 +790,13 @@ msgstr "Fogadott"
|
||||
msgid "Reset Password"
|
||||
msgstr "Jelszó visszaállítása"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Resolved"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Folytatás"
|
||||
|
||||
@@ -687,6 +804,10 @@ msgstr "Folytatás"
|
||||
msgid "Rotate token"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Rows per page"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Save address using enter key or comma. Leave blank to disable email notifications."
|
||||
msgstr "Mentse el a címet az Enter billentyű vagy a vessző használatával. Hagyja üresen az e-mail értesítések letiltásához."
|
||||
@@ -712,11 +833,14 @@ msgstr "Keresés rendszerek vagy beállítások után..."
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "Lásd <0>az értesítési beállításokat</0>, hogy konfigurálja, hogyan kap értesítéseket."
|
||||
|
||||
#. Network bytes sent (upload)
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "Elküldve"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "Százalékos küszöbértékek beállítása a mérőszínekhez."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Sets the default time range for charts when a system is viewed."
|
||||
msgstr "Beállítja az alapértelmezett időtartamot a diagramokhoz, amikor egy rendszert néznek."
|
||||
@@ -744,6 +868,11 @@ msgstr "SMTP beállítások"
|
||||
msgid "Sort By"
|
||||
msgstr "Rendezés"
|
||||
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "State"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Status"
|
||||
msgstr "Állapot"
|
||||
@@ -759,11 +888,16 @@ msgstr "Swap használat"
|
||||
#. System theme
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "System"
|
||||
msgstr "Rendszer"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "System load averages over time"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Systems"
|
||||
msgstr "Rendszer"
|
||||
@@ -777,7 +911,7 @@ msgid "Table"
|
||||
msgstr "Tábla"
|
||||
|
||||
#. Temperature label in systems table
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Temp"
|
||||
msgstr ""
|
||||
|
||||
@@ -786,6 +920,10 @@ msgstr ""
|
||||
msgid "Temperature"
|
||||
msgstr "Hőmérséklet"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Temperature unit"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Temperatures of system sensors"
|
||||
msgstr "A rendszer érzékelőinek hőmérséklete"
|
||||
@@ -802,10 +940,14 @@ msgstr "Teszt értesítés elküldve"
|
||||
msgid "Then log into the backend and reset your user account password in the users table."
|
||||
msgstr "Ezután jelentkezzen be a backendbe, és állítsa vissza a felhasználói fiók jelszavát a felhasználók táblázatban."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "This action cannot be undone. This will permanently delete all current records for {name} from the database."
|
||||
msgstr "Ezt a műveletet nem lehet visszavonni! Véglegesen törli a {name} összes jelenlegi rekordját az adatbázisból!"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "This will permanently delete all selected records from the database."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Throughput of {extraFsName}"
|
||||
msgstr "A {extraFsName} átviteli teljesítménye"
|
||||
@@ -846,6 +988,10 @@ msgstr ""
|
||||
msgid "Tokens and fingerprints are used to authenticate WebSocket connections to the hub."
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when 1 minute load average exceeds a threshold"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when 15 minute load average exceeds a threshold"
|
||||
msgstr ""
|
||||
@@ -878,12 +1024,17 @@ msgstr "Bekapcsol, amikor az állapot fel és le között változik"
|
||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||
msgstr "Bekapcsol, ha a lemez érzékelő túllép egy küszöbértéket"
|
||||
|
||||
#. Temperature / network units
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Unit preferences"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Universal token"
|
||||
msgstr ""
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Up"
|
||||
msgstr ""
|
||||
@@ -898,7 +1049,8 @@ msgstr "Üzemidő"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Usage"
|
||||
msgstr "Használat"
|
||||
|
||||
@@ -908,7 +1060,6 @@ msgstr "Root partíció kihasználtsága"
|
||||
|
||||
#: src/components/charts/swap-chart.tsx
|
||||
#: src/components/charts/mem-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
msgid "Used"
|
||||
msgstr "Felhasznált"
|
||||
|
||||
@@ -917,10 +1068,18 @@ msgstr "Felhasznált"
|
||||
msgid "Users"
|
||||
msgstr "Felhasználók"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Value"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "View"
|
||||
msgstr "Nézet"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "View your 200 most recent alerts."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Visible Fields"
|
||||
msgstr "Látható mezők"
|
||||
@@ -933,6 +1092,14 @@ msgstr "Elegendő rekordra várva a megjelenítéshez"
|
||||
msgid "Want to help improve our translations? Check <0>Crowdin</0> for details."
|
||||
msgstr "Szeretne segíteni nekünk abban, hogy fordításaink még jobbak legyenek? További részletekért nézze meg a <0>Crowdin</0> honlapot."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Warning (%)"
|
||||
msgstr "Figyelmeztetés (%)"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Warning thresholds"
|
||||
msgstr "Figyelmeztetési küszöbértékek"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Webhook / Push notifications"
|
||||
msgstr "Webhook / Push értesítések"
|
||||
@@ -948,8 +1115,8 @@ msgid "Windows command"
|
||||
msgstr "Windows parancs"
|
||||
|
||||
#. Disk write
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Write"
|
||||
msgstr "Írás"
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: is\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-03-06 07:27\n"
|
||||
"PO-Revision-Date: 2025-07-25 22:44\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Icelandic\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -23,6 +23,12 @@ msgstr ""
|
||||
msgid "{0, plural, one {# day} other {# days}}"
|
||||
msgstr "{0, plural, one {# dagur} other {# dagar}}"
|
||||
|
||||
#. placeholder {0}: table.getFilteredSelectedRowModel().rows.length
|
||||
#. placeholder {1}: table.getFilteredRowModel().rows.length
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "{0} of {1} row(s) selected."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "{hours, plural, one {# hour} other {# hours}}"
|
||||
msgstr "{hours, plural, one {# klukkustund} other {# klukkustundir}}"
|
||||
@@ -31,6 +37,11 @@ msgstr "{hours, plural, one {# klukkustund} other {# klukkustundir}}"
|
||||
msgid "1 hour"
|
||||
msgstr "1 klukkustund"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "1 min"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 week"
|
||||
msgstr "1 vika"
|
||||
@@ -39,6 +50,11 @@ msgstr "1 vika"
|
||||
msgid "12 hours"
|
||||
msgstr "12 klukkustundir"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "15 min"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "24 hours"
|
||||
msgstr "24 klukkustundir"
|
||||
@@ -47,12 +63,22 @@ msgstr "24 klukkustundir"
|
||||
msgid "30 days"
|
||||
msgstr "30 dagar"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "5 min"
|
||||
msgstr ""
|
||||
|
||||
#. Table column
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Actions"
|
||||
msgstr "Aðgerðir"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Active"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/home.tsx
|
||||
msgid "Active Alerts"
|
||||
msgstr "Virkar tilkynningar"
|
||||
@@ -82,10 +108,16 @@ msgstr ""
|
||||
msgid "Admin"
|
||||
msgstr "Admin"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Alert History"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/alerts/alert-button.tsx
|
||||
#: src/components/alerts/alert-button.tsx
|
||||
msgid "Alerts"
|
||||
@@ -96,10 +128,14 @@ msgstr "Tilkynningar"
|
||||
msgid "All Systems"
|
||||
msgstr "Öll kerfi"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Are you sure you want to delete {name}?"
|
||||
msgstr "Ertu viss um að þú viljir eyða {name}?"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Are you sure?"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Automatic copy requires a secure context."
|
||||
msgstr "Sjálfvisk afritun krefst öruggs samhengis."
|
||||
@@ -152,11 +188,22 @@ msgstr "Beszel notar <0>Shoutrrr</0> til að tengjast vinsælum tilkynningaþjó
|
||||
msgid "Binary"
|
||||
msgstr "Binary"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bits (Kbps, Mbps, Gbps)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bytes (KB/s, MB/s, GB/s)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/charts/mem-chart.tsx
|
||||
msgid "Cache / Buffers"
|
||||
msgstr "Skyndiminni / Biðminni"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Cancel"
|
||||
msgstr "Hætta við"
|
||||
|
||||
@@ -164,6 +211,14 @@ msgstr "Hætta við"
|
||||
msgid "Caution - potential data loss"
|
||||
msgstr "Aðvörun - möguleiki á gagnatapi"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Celsius (°C)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change display units for metrics."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change general application options."
|
||||
msgstr "Breyta almennum stillingum."
|
||||
@@ -202,7 +257,12 @@ msgstr "Stilltu hvernig þú vilt fá tilkynningar."
|
||||
msgid "Confirm password"
|
||||
msgstr "Staðfestu lykilorð"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/routes/home.tsx
|
||||
msgid "Connection is down"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Continue"
|
||||
msgstr "Halda áfram"
|
||||
|
||||
@@ -227,7 +287,7 @@ msgctxt "Environment variables"
|
||||
msgid "Copy env"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Copy host"
|
||||
msgstr "Afrita host"
|
||||
|
||||
@@ -236,6 +296,10 @@ msgstr "Afrita host"
|
||||
msgid "Copy Linux command"
|
||||
msgstr "Afrita Linux aðgerð"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Copy name"
|
||||
msgstr "Afrita nafn"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
msgstr "Afrita texta"
|
||||
@@ -252,13 +316,13 @@ msgstr ""
|
||||
msgid "Copy YAML"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "CPU"
|
||||
msgstr "Örgjörvi"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "CPU Usage"
|
||||
msgstr "Örgjörva notkun"
|
||||
|
||||
@@ -266,6 +330,15 @@ msgstr "Örgjörva notkun"
|
||||
msgid "Create account"
|
||||
msgstr "Búa til aðgang"
|
||||
|
||||
#. Context: date created
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Created"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Critical (%)"
|
||||
msgstr "Kritískt (%)"
|
||||
|
||||
#. Dark theme
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Dark"
|
||||
@@ -280,7 +353,8 @@ msgstr "Yfirlitssíða"
|
||||
msgid "Default time period"
|
||||
msgstr "Sjálfgefið tímabil"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Delete"
|
||||
msgstr "Eyða"
|
||||
|
||||
@@ -288,7 +362,7 @@ msgstr "Eyða"
|
||||
msgid "Delete fingerprint"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Disk"
|
||||
msgstr "Diskur"
|
||||
|
||||
@@ -296,6 +370,10 @@ msgstr "Diskur"
|
||||
msgid "Disk I/O"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Disk unit"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/disk-chart.tsx
|
||||
@@ -324,13 +402,18 @@ msgstr "Skjal"
|
||||
|
||||
#. Context: System is down
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Down"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Duration"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Edit"
|
||||
msgstr ""
|
||||
|
||||
@@ -354,6 +437,7 @@ msgstr "Settu inn Netfang..."
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Error"
|
||||
msgstr "Villa"
|
||||
@@ -369,6 +453,10 @@ msgstr "Fór yfir {0}{1} á síðustu {2, plural, one {# mínútu} other {# mín
|
||||
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
msgid "Export configuration"
|
||||
msgstr ""
|
||||
@@ -377,6 +465,10 @@ msgstr ""
|
||||
msgid "Export your current systems configuration."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Fahrenheit (°F)"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Failed to authenticate"
|
||||
msgstr "Villa í auðkenningu"
|
||||
@@ -396,6 +488,7 @@ msgstr "Mistókst að uppfæra tilkynningu"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Filter..."
|
||||
msgstr "Sía..."
|
||||
|
||||
@@ -448,16 +541,6 @@ msgstr "Ógilt netfang."
|
||||
msgid "Kernel"
|
||||
msgstr ""
|
||||
|
||||
#. Load average 15 minutes
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "L15"
|
||||
msgstr ""
|
||||
|
||||
#. Load average 5 minutes
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "L5"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "Tungumál"
|
||||
@@ -471,14 +554,27 @@ msgstr ""
|
||||
msgid "Light"
|
||||
msgstr "Ljóst"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Load Average"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 15m"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 1m"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 5m"
|
||||
msgstr ""
|
||||
|
||||
#. Short label for load average
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Load Avg"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Log Out"
|
||||
msgstr "Útskrá"
|
||||
@@ -514,7 +610,7 @@ msgstr ""
|
||||
msgid "Max 1 min"
|
||||
msgstr "Mest 1 mínúta"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Memory"
|
||||
msgstr "Minni"
|
||||
|
||||
@@ -527,11 +623,12 @@ msgstr "Minnisnotkun"
|
||||
msgid "Memory usage of docker containers"
|
||||
msgstr "Minnisnotkun docker kerfa"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Name"
|
||||
msgstr "Nafn"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Net"
|
||||
msgstr "Net"
|
||||
|
||||
@@ -543,10 +640,19 @@ msgstr "Net traffík docker kerfa"
|
||||
msgid "Network traffic of public interfaces"
|
||||
msgstr ""
|
||||
|
||||
#. Context: Bytes or bits
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Network unit"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "No results found."
|
||||
msgstr "Engar niðurstöður fundust."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "No results."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "No systems found."
|
||||
@@ -566,7 +672,7 @@ msgstr "OAuth 2 / OIDC stuðningur"
|
||||
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Open menu"
|
||||
@@ -584,6 +690,12 @@ msgstr "Yfirskrifa núverandi tilkynningu"
|
||||
msgid "Page"
|
||||
msgstr "Síða"
|
||||
|
||||
#. placeholder {0}: table.getState().pagination.pageIndex + 1
|
||||
#. placeholder {1}: table.getPageCount()
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Page {0} of {1}"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Pages / Settings"
|
||||
msgstr "Síða / Stillingar"
|
||||
@@ -605,11 +717,11 @@ msgstr ""
|
||||
msgid "Password reset request received"
|
||||
msgstr "Beiðni um að endurstilla lykilorð móttekin"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Pause"
|
||||
msgstr "Pása"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Paused"
|
||||
msgstr "Í bið"
|
||||
|
||||
@@ -665,13 +777,12 @@ msgid "Public Key"
|
||||
msgstr "Dreifilykill"
|
||||
|
||||
#. Disk read
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Read"
|
||||
msgstr "Lesa"
|
||||
|
||||
#. Network bytes received (download)
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Received"
|
||||
msgstr "Móttekið"
|
||||
|
||||
@@ -679,7 +790,13 @@ msgstr "Móttekið"
|
||||
msgid "Reset Password"
|
||||
msgstr "Endurstilla lykilorð"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Resolved"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Halda áfram"
|
||||
|
||||
@@ -687,6 +804,10 @@ msgstr "Halda áfram"
|
||||
msgid "Rotate token"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Rows per page"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Save address using enter key or comma. Leave blank to disable email notifications."
|
||||
msgstr ""
|
||||
@@ -712,11 +833,14 @@ msgstr "Leita að kerfum eða stillingum..."
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr ""
|
||||
|
||||
#. Network bytes sent (upload)
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "Sent"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "Stilltu prósentuþröskuld fyrir mælaliti."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Sets the default time range for charts when a system is viewed."
|
||||
msgstr ""
|
||||
@@ -744,6 +868,11 @@ msgstr "SMTP stillingar"
|
||||
msgid "Sort By"
|
||||
msgstr "Raða eftir"
|
||||
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "State"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Status"
|
||||
msgstr "Staða"
|
||||
@@ -759,11 +888,16 @@ msgstr "Skipti minni"
|
||||
#. System theme
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "System"
|
||||
msgstr "Kerfi"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "System load averages over time"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Systems"
|
||||
msgstr "Kerfi"
|
||||
@@ -777,7 +911,7 @@ msgid "Table"
|
||||
msgstr "Tafla"
|
||||
|
||||
#. Temperature label in systems table
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Temp"
|
||||
msgstr ""
|
||||
|
||||
@@ -786,6 +920,10 @@ msgstr ""
|
||||
msgid "Temperature"
|
||||
msgstr "Hitastig"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Temperature unit"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Temperatures of system sensors"
|
||||
msgstr "Hitastig kerfa skynjara"
|
||||
@@ -802,10 +940,14 @@ msgstr "Prufu tilkynning send"
|
||||
msgid "Then log into the backend and reset your user account password in the users table."
|
||||
msgstr "Skráðu þig þá inní bakendann og endurstilltu lykilorðið þitt inni í notenda töflunni."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "This action cannot be undone. This will permanently delete all current records for {name} from the database."
|
||||
msgstr "Þessi aðgerð er óafturkvæmanleg. Þetta mun eyða gögnum fyrir {name} varanlega úr gagnagrunninum."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "This will permanently delete all selected records from the database."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Throughput of {extraFsName}"
|
||||
msgstr ""
|
||||
@@ -846,6 +988,10 @@ msgstr ""
|
||||
msgid "Tokens and fingerprints are used to authenticate WebSocket connections to the hub."
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when 1 minute load average exceeds a threshold"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when 15 minute load average exceeds a threshold"
|
||||
msgstr ""
|
||||
@@ -878,12 +1024,17 @@ msgstr "Virkjast þegar staða breytist milli virkur og óvirkur"
|
||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||
msgstr "Virkjast þegar diska notkun fer yfir þröskuld"
|
||||
|
||||
#. Temperature / network units
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Unit preferences"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Universal token"
|
||||
msgstr ""
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Up"
|
||||
msgstr ""
|
||||
@@ -898,7 +1049,8 @@ msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Usage"
|
||||
msgstr ""
|
||||
|
||||
@@ -908,7 +1060,6 @@ msgstr ""
|
||||
|
||||
#: src/components/charts/swap-chart.tsx
|
||||
#: src/components/charts/mem-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
msgid "Used"
|
||||
msgstr "Notað"
|
||||
|
||||
@@ -917,10 +1068,18 @@ msgstr "Notað"
|
||||
msgid "Users"
|
||||
msgstr "Notendur"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Value"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "View"
|
||||
msgstr "Skoða"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "View your 200 most recent alerts."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Visible Fields"
|
||||
msgstr "Sjáanlegir reitir"
|
||||
@@ -933,6 +1092,14 @@ msgstr "Bíður eftir nægum upplýsingum til að sýna"
|
||||
msgid "Want to help improve our translations? Check <0>Crowdin</0> for details."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Warning (%)"
|
||||
msgstr "Viðvörun (%)"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Warning thresholds"
|
||||
msgstr "Viðvörunarþröskuldur"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Webhook / Push notifications"
|
||||
msgstr "Webhook / Tilkynningar"
|
||||
@@ -948,8 +1115,8 @@ msgid "Windows command"
|
||||
msgstr "Windows skipun"
|
||||
|
||||
#. Disk write
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Write"
|
||||
msgstr "Skrifa"
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: it\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-03-06 07:27\n"
|
||||
"PO-Revision-Date: 2025-07-25 22:44\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Italian\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -23,6 +23,12 @@ msgstr ""
|
||||
msgid "{0, plural, one {# day} other {# days}}"
|
||||
msgstr "{0, plural, one {# giorno} other {# giorni}}"
|
||||
|
||||
#. placeholder {0}: table.getFilteredSelectedRowModel().rows.length
|
||||
#. placeholder {1}: table.getFilteredRowModel().rows.length
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "{0} of {1} row(s) selected."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "{hours, plural, one {# hour} other {# hours}}"
|
||||
msgstr "{hours, plural, one {# ora} other {# ore}}"
|
||||
@@ -31,6 +37,11 @@ msgstr "{hours, plural, one {# ora} other {# ore}}"
|
||||
msgid "1 hour"
|
||||
msgstr "1 ora"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "1 min"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 week"
|
||||
msgstr "1 settimana"
|
||||
@@ -39,6 +50,11 @@ msgstr "1 settimana"
|
||||
msgid "12 hours"
|
||||
msgstr "12 ore"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "15 min"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "24 hours"
|
||||
msgstr "24 ore"
|
||||
@@ -47,12 +63,22 @@ msgstr "24 ore"
|
||||
msgid "30 days"
|
||||
msgstr "30 giorni"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "5 min"
|
||||
msgstr ""
|
||||
|
||||
#. Table column
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Actions"
|
||||
msgstr "Azioni"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Active"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/home.tsx
|
||||
msgid "Active Alerts"
|
||||
msgstr "Avvisi Attivi"
|
||||
@@ -82,10 +108,16 @@ msgstr "Regola le opzioni di visualizzazione per i grafici."
|
||||
msgid "Admin"
|
||||
msgstr "Amministratore"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Agente"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Alert History"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/alerts/alert-button.tsx
|
||||
#: src/components/alerts/alert-button.tsx
|
||||
msgid "Alerts"
|
||||
@@ -96,10 +128,14 @@ msgstr "Avvisi"
|
||||
msgid "All Systems"
|
||||
msgstr "Tutti i Sistemi"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Are you sure you want to delete {name}?"
|
||||
msgstr "Sei sicuro di voler eliminare {name}?"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Are you sure?"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Automatic copy requires a secure context."
|
||||
msgstr "La copia automatica richiede un contesto sicuro."
|
||||
@@ -152,11 +188,22 @@ msgstr "Beszel utilizza <0>Shoutrrr</0> per integrarsi con i servizi di notifica
|
||||
msgid "Binary"
|
||||
msgstr "Binario"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bits (Kbps, Mbps, Gbps)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bytes (KB/s, MB/s, GB/s)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/charts/mem-chart.tsx
|
||||
msgid "Cache / Buffers"
|
||||
msgstr "Cache / Buffer"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Cancel"
|
||||
msgstr "Annulla"
|
||||
|
||||
@@ -164,6 +211,14 @@ msgstr "Annulla"
|
||||
msgid "Caution - potential data loss"
|
||||
msgstr "Attenzione - possibile perdita di dati"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Celsius (°C)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change display units for metrics."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change general application options."
|
||||
msgstr "Modifica le opzioni generali dell'applicazione."
|
||||
@@ -202,7 +257,12 @@ msgstr "Configura come ricevere le notifiche di avviso."
|
||||
msgid "Confirm password"
|
||||
msgstr "Conferma password"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/routes/home.tsx
|
||||
msgid "Connection is down"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Continue"
|
||||
msgstr "Continua"
|
||||
|
||||
@@ -227,7 +287,7 @@ msgctxt "Environment variables"
|
||||
msgid "Copy env"
|
||||
msgstr "Copia env"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Copy host"
|
||||
msgstr "Copia host"
|
||||
|
||||
@@ -236,6 +296,10 @@ msgstr "Copia host"
|
||||
msgid "Copy Linux command"
|
||||
msgstr "Copia comando Linux"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Copy name"
|
||||
msgstr "Copia nome"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
msgstr "Copia testo"
|
||||
@@ -252,13 +316,13 @@ msgstr "Copia il contenuto<0>docker-compose.yml</0> per l'agente qui sotto, o re
|
||||
msgid "Copy YAML"
|
||||
msgstr "Copia YAML"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "CPU"
|
||||
msgstr "CPU"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "CPU Usage"
|
||||
msgstr "Utilizzo CPU"
|
||||
|
||||
@@ -266,6 +330,15 @@ msgstr "Utilizzo CPU"
|
||||
msgid "Create account"
|
||||
msgstr "Crea account"
|
||||
|
||||
#. Context: date created
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Created"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Critical (%)"
|
||||
msgstr "Critico (%)"
|
||||
|
||||
#. Dark theme
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Dark"
|
||||
@@ -280,7 +353,8 @@ msgstr "Cruscotto"
|
||||
msgid "Default time period"
|
||||
msgstr "Periodo di tempo predefinito"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Delete"
|
||||
msgstr "Elimina"
|
||||
|
||||
@@ -288,7 +362,7 @@ msgstr "Elimina"
|
||||
msgid "Delete fingerprint"
|
||||
msgstr "Elimina impronta digitale"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Disk"
|
||||
msgstr "Disco"
|
||||
|
||||
@@ -296,6 +370,10 @@ msgstr "Disco"
|
||||
msgid "Disk I/O"
|
||||
msgstr "I/O Disco"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Disk unit"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/disk-chart.tsx
|
||||
@@ -324,13 +402,18 @@ msgstr "Documentazione"
|
||||
|
||||
#. Context: System is down
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Down"
|
||||
msgstr "Offline"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Duration"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Edit"
|
||||
msgstr "Modifica"
|
||||
|
||||
@@ -354,6 +437,7 @@ msgstr "Inserisci l'indirizzo email..."
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Error"
|
||||
msgstr "Errore"
|
||||
@@ -369,6 +453,10 @@ msgstr "Supera {0}{1} negli ultimi {2, plural, one {# minuto} other {# minuti}}"
|
||||
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
|
||||
msgstr "I sistemi esistenti non definiti in <0>config.yml</0> verranno eliminati. Si prega di effettuare backup regolari."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
msgid "Export configuration"
|
||||
msgstr "Esporta configurazione"
|
||||
@@ -377,6 +465,10 @@ msgstr "Esporta configurazione"
|
||||
msgid "Export your current systems configuration."
|
||||
msgstr "Esporta la configurazione attuale dei tuoi sistemi."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Fahrenheit (°F)"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Failed to authenticate"
|
||||
msgstr "Autenticazione fallita"
|
||||
@@ -396,6 +488,7 @@ msgstr "Aggiornamento dell'avviso fallito"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Filter..."
|
||||
msgstr "Filtra..."
|
||||
|
||||
@@ -448,16 +541,6 @@ msgstr "Indirizzo email non valido."
|
||||
msgid "Kernel"
|
||||
msgstr "Kernel"
|
||||
|
||||
#. Load average 15 minutes
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "L15"
|
||||
msgstr ""
|
||||
|
||||
#. Load average 5 minutes
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "L5"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "Lingua"
|
||||
@@ -471,12 +554,25 @@ msgstr "Aspetto"
|
||||
msgid "Light"
|
||||
msgstr "Chiaro"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Load Average"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 15m"
|
||||
msgstr "Caricamento medio 15m"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 1m"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 5m"
|
||||
msgstr "Caricamento medio 5m"
|
||||
|
||||
#. Short label for load average
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Load Avg"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
@@ -514,7 +610,7 @@ msgstr "Istruzioni di configurazione manuale"
|
||||
msgid "Max 1 min"
|
||||
msgstr "Max 1 min"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Memory"
|
||||
msgstr "Memoria"
|
||||
|
||||
@@ -527,11 +623,12 @@ msgstr "Utilizzo Memoria"
|
||||
msgid "Memory usage of docker containers"
|
||||
msgstr "Utilizzo della memoria dei container Docker"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Name"
|
||||
msgstr "Nome"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Net"
|
||||
msgstr "Rete"
|
||||
|
||||
@@ -543,10 +640,19 @@ msgstr "Traffico di rete dei container Docker"
|
||||
msgid "Network traffic of public interfaces"
|
||||
msgstr "Traffico di rete delle interfacce pubbliche"
|
||||
|
||||
#. Context: Bytes or bits
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Network unit"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "No results found."
|
||||
msgstr "Nessun risultato trovato."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "No results."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "No systems found."
|
||||
@@ -566,7 +672,7 @@ msgstr "Supporto OAuth 2 / OIDC"
|
||||
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
|
||||
msgstr "Ad ogni riavvio, i sistemi nel database verranno aggiornati per corrispondere ai sistemi definiti nel file."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Open menu"
|
||||
@@ -584,6 +690,12 @@ msgstr "Sovrascrivi avvisi esistenti"
|
||||
msgid "Page"
|
||||
msgstr "Pagina"
|
||||
|
||||
#. placeholder {0}: table.getState().pagination.pageIndex + 1
|
||||
#. placeholder {1}: table.getPageCount()
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Page {0} of {1}"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Pages / Settings"
|
||||
msgstr "Pagine / Impostazioni"
|
||||
@@ -605,11 +717,11 @@ msgstr "La password deve essere inferiore a 72 byte."
|
||||
msgid "Password reset request received"
|
||||
msgstr "Richiesta di reimpostazione password ricevuta"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Pause"
|
||||
msgstr "Pausa"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Paused"
|
||||
msgstr "In pausa"
|
||||
|
||||
@@ -665,13 +777,12 @@ msgid "Public Key"
|
||||
msgstr "Chiave Pub"
|
||||
|
||||
#. Disk read
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Read"
|
||||
msgstr "Lettura"
|
||||
|
||||
#. Network bytes received (download)
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Received"
|
||||
msgstr "Ricevuto"
|
||||
|
||||
@@ -679,7 +790,13 @@ msgstr "Ricevuto"
|
||||
msgid "Reset Password"
|
||||
msgstr "Reimposta Password"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Resolved"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Riprendi"
|
||||
|
||||
@@ -687,6 +804,10 @@ msgstr "Riprendi"
|
||||
msgid "Rotate token"
|
||||
msgstr "Ruota token"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Rows per page"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Save address using enter key or comma. Leave blank to disable email notifications."
|
||||
msgstr "Salva l'indirizzo usando il tasto invio o la virgola. Lascia vuoto per disabilitare le notifiche email."
|
||||
@@ -712,11 +833,14 @@ msgstr "Cerca sistemi o impostazioni..."
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "Vedi <0>impostazioni di notifica</0> per configurare come ricevere gli avvisi."
|
||||
|
||||
#. Network bytes sent (upload)
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "Inviato"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "Imposta le soglie percentuali per i colori dei contatori."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Sets the default time range for charts when a system is viewed."
|
||||
msgstr "Imposta l'intervallo di tempo predefinito per i grafici quando viene visualizzato un sistema."
|
||||
@@ -744,6 +868,11 @@ msgstr "Impostazioni SMTP"
|
||||
msgid "Sort By"
|
||||
msgstr "Ordina per"
|
||||
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "State"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Status"
|
||||
msgstr "Stato"
|
||||
@@ -759,11 +888,16 @@ msgstr "Utilizzo Swap"
|
||||
#. System theme
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "System"
|
||||
msgstr "Sistema"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "System load averages over time"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Systems"
|
||||
msgstr "Sistemi"
|
||||
@@ -777,7 +911,7 @@ msgid "Table"
|
||||
msgstr "Tabella"
|
||||
|
||||
#. Temperature label in systems table
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Temp"
|
||||
msgstr "Temperatura"
|
||||
|
||||
@@ -786,6 +920,10 @@ msgstr "Temperatura"
|
||||
msgid "Temperature"
|
||||
msgstr "Temperatura"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Temperature unit"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Temperatures of system sensors"
|
||||
msgstr "Temperature dei sensori di sistema"
|
||||
@@ -802,10 +940,14 @@ msgstr "Notifica di test inviata"
|
||||
msgid "Then log into the backend and reset your user account password in the users table."
|
||||
msgstr "Quindi accedi al backend e reimposta la password del tuo account utente nella tabella degli utenti."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "This action cannot be undone. This will permanently delete all current records for {name} from the database."
|
||||
msgstr "Questa azione non può essere annullata. Questo eliminerà permanentemente tutti i record attuali per {name} dal database."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "This will permanently delete all selected records from the database."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Throughput of {extraFsName}"
|
||||
msgstr "Throughput di {extraFsName}"
|
||||
@@ -846,6 +988,10 @@ msgstr "I token consentono agli agenti di connettersi e registrarsi. Le impronte
|
||||
msgid "Tokens and fingerprints are used to authenticate WebSocket connections to the hub."
|
||||
msgstr "I token e le impronte digitali vengono utilizzati per autenticare le connessioni WebSocket all'hub."
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when 1 minute load average exceeds a threshold"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when 15 minute load average exceeds a threshold"
|
||||
msgstr ""
|
||||
@@ -878,12 +1024,17 @@ msgstr "Attiva quando lo stato passa tra up e down"
|
||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||
msgstr "Attiva quando l'utilizzo di un disco supera una soglia"
|
||||
|
||||
#. Temperature / network units
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Unit preferences"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Universal token"
|
||||
msgstr "Token universale"
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Up"
|
||||
msgstr "Attivo"
|
||||
@@ -898,7 +1049,8 @@ msgstr "Tempo di attività"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Usage"
|
||||
msgstr "Utilizzo"
|
||||
|
||||
@@ -908,7 +1060,6 @@ msgstr "Utilizzo della partizione root"
|
||||
|
||||
#: src/components/charts/swap-chart.tsx
|
||||
#: src/components/charts/mem-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
msgid "Used"
|
||||
msgstr "Utilizzato"
|
||||
|
||||
@@ -917,10 +1068,18 @@ msgstr "Utilizzato"
|
||||
msgid "Users"
|
||||
msgstr "Utenti"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Value"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "View"
|
||||
msgstr "Vista"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "View your 200 most recent alerts."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Visible Fields"
|
||||
msgstr "Colonne visibili"
|
||||
@@ -933,6 +1092,14 @@ msgstr "In attesa di abbastanza record da visualizzare"
|
||||
msgid "Want to help improve our translations? Check <0>Crowdin</0> for details."
|
||||
msgstr "Vuoi aiutarci a migliorare ulteriormente le nostre traduzioni? Dai un'occhiata a <0>Crowdin</0> per maggiori dettagli."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Warning (%)"
|
||||
msgstr "Avviso (%)"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Warning thresholds"
|
||||
msgstr "Soglie di avviso"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Webhook / Push notifications"
|
||||
msgstr "Notifiche Webhook / Push"
|
||||
@@ -948,8 +1115,8 @@ msgid "Windows command"
|
||||
msgstr "Comando Windows"
|
||||
|
||||
#. Disk write
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Write"
|
||||
msgstr "Scrittura"
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: ja\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-03-13 10:13\n"
|
||||
"PO-Revision-Date: 2025-07-25 22:44\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Japanese\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
@@ -23,6 +23,12 @@ msgstr ""
|
||||
msgid "{0, plural, one {# day} other {# days}}"
|
||||
msgstr "{0, plural, one {# 日} other {# 日}}"
|
||||
|
||||
#. placeholder {0}: table.getFilteredSelectedRowModel().rows.length
|
||||
#. placeholder {1}: table.getFilteredRowModel().rows.length
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "{0} of {1} row(s) selected."
|
||||
msgstr "{1}行のうち{0}行が選択されました。"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "{hours, plural, one {# hour} other {# hours}}"
|
||||
msgstr "{hours, plural, one {# 時間} other {# 時間}}"
|
||||
@@ -31,6 +37,11 @@ msgstr "{hours, plural, one {# 時間} other {# 時間}}"
|
||||
msgid "1 hour"
|
||||
msgstr "1時間"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "1 min"
|
||||
msgstr "1分"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 week"
|
||||
msgstr "1週間"
|
||||
@@ -39,6 +50,11 @@ msgstr "1週間"
|
||||
msgid "12 hours"
|
||||
msgstr "12時間"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "15 min"
|
||||
msgstr "15分"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "24 hours"
|
||||
msgstr "24時間"
|
||||
@@ -47,12 +63,22 @@ msgstr "24時間"
|
||||
msgid "30 days"
|
||||
msgstr "30日間"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "5 min"
|
||||
msgstr "5分"
|
||||
|
||||
#. Table column
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Actions"
|
||||
msgstr "アクション"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Active"
|
||||
msgstr "アクティブ"
|
||||
|
||||
#: src/components/routes/home.tsx
|
||||
msgid "Active Alerts"
|
||||
msgstr "アクティブなアラート"
|
||||
@@ -82,10 +108,16 @@ msgstr "チャートの表示オプションを調整します。"
|
||||
msgid "Admin"
|
||||
msgstr "管理者"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "エージェント"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Alert History"
|
||||
msgstr "アラート履歴"
|
||||
|
||||
#: src/components/alerts/alert-button.tsx
|
||||
#: src/components/alerts/alert-button.tsx
|
||||
msgid "Alerts"
|
||||
@@ -96,10 +128,14 @@ msgstr "アラート"
|
||||
msgid "All Systems"
|
||||
msgstr "すべてのシステム"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Are you sure you want to delete {name}?"
|
||||
msgstr "{name}を削除してもよろしいですか?"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Are you sure?"
|
||||
msgstr "よろしいですか?"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Automatic copy requires a secure context."
|
||||
msgstr "自動コピーには安全なコンテキストが必要です。"
|
||||
@@ -152,11 +188,22 @@ msgstr "Beszelは<0>Shoutrrr</0>を使用して、人気のある通知サービ
|
||||
msgid "Binary"
|
||||
msgstr "バイナリ"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bits (Kbps, Mbps, Gbps)"
|
||||
msgstr "ビット (Kbps, Mbps, Gbps)"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bytes (KB/s, MB/s, GB/s)"
|
||||
msgstr "バイト (KB/s, MB/s, GB/s)"
|
||||
|
||||
#: src/components/charts/mem-chart.tsx
|
||||
msgid "Cache / Buffers"
|
||||
msgstr "キャッシュ / バッファ"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Cancel"
|
||||
msgstr "キャンセル"
|
||||
|
||||
@@ -164,6 +211,14 @@ msgstr "キャンセル"
|
||||
msgid "Caution - potential data loss"
|
||||
msgstr "注意 - データ損失の可能性"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Celsius (°C)"
|
||||
msgstr "セルシウス (°C)"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change display units for metrics."
|
||||
msgstr "メトリックの表示単位を変更します。"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change general application options."
|
||||
msgstr "一般的なアプリケーションオプションを変更します。"
|
||||
@@ -202,7 +257,12 @@ msgstr "アラート通知の受信方法を設定します。"
|
||||
msgid "Confirm password"
|
||||
msgstr "パスワードを確認"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/routes/home.tsx
|
||||
msgid "Connection is down"
|
||||
msgstr "接続が切断されました"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Continue"
|
||||
msgstr "続行"
|
||||
|
||||
@@ -227,7 +287,7 @@ msgctxt "Environment variables"
|
||||
msgid "Copy env"
|
||||
msgstr "環境変数をコピー"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Copy host"
|
||||
msgstr "ホストをコピー"
|
||||
|
||||
@@ -236,6 +296,10 @@ msgstr "ホストをコピー"
|
||||
msgid "Copy Linux command"
|
||||
msgstr "Linuxコマンドをコピー"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Copy name"
|
||||
msgstr "名前をコピー"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
msgstr "テキストをコピー"
|
||||
@@ -252,13 +316,13 @@ msgstr "下記のエージェントの<0>docker-compose.yml</0>内容をコピ
|
||||
msgid "Copy YAML"
|
||||
msgstr "YAMLをコピー"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "CPU"
|
||||
msgstr "CPU"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "CPU Usage"
|
||||
msgstr "CPU使用率"
|
||||
|
||||
@@ -266,6 +330,15 @@ msgstr "CPU使用率"
|
||||
msgid "Create account"
|
||||
msgstr "アカウントを作成"
|
||||
|
||||
#. Context: date created
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Created"
|
||||
msgstr "作成日"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Critical (%)"
|
||||
msgstr "クリティカル (%)"
|
||||
|
||||
#. Dark theme
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Dark"
|
||||
@@ -280,7 +353,8 @@ msgstr "ダッシュボード"
|
||||
msgid "Default time period"
|
||||
msgstr "デフォルトの期間"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Delete"
|
||||
msgstr "削除"
|
||||
|
||||
@@ -288,7 +362,7 @@ msgstr "削除"
|
||||
msgid "Delete fingerprint"
|
||||
msgstr "フィンガープリントを削除"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Disk"
|
||||
msgstr "ディスク"
|
||||
|
||||
@@ -296,6 +370,10 @@ msgstr "ディスク"
|
||||
msgid "Disk I/O"
|
||||
msgstr "ディスクI/O"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Disk unit"
|
||||
msgstr "ディスク単位"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/disk-chart.tsx
|
||||
@@ -324,13 +402,18 @@ msgstr "ドキュメント"
|
||||
|
||||
#. Context: System is down
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Down"
|
||||
msgstr "停止"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Duration"
|
||||
msgstr "期間"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Edit"
|
||||
msgstr "編集"
|
||||
|
||||
@@ -354,6 +437,7 @@ msgstr "メールアドレスを入力..."
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Error"
|
||||
msgstr "エラー"
|
||||
@@ -369,6 +453,10 @@ msgstr "過去{2, plural, one {# 分} other {# 分}}で{0}{1}を超えていま
|
||||
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
|
||||
msgstr "<0>config.yml</0>に定義されていない既存のシステムは削除されます。定期的にバックアップを作成してください。"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr "エクスポート"
|
||||
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
msgid "Export configuration"
|
||||
msgstr "設定をエクスポート"
|
||||
@@ -377,6 +465,10 @@ msgstr "設定をエクスポート"
|
||||
msgid "Export your current systems configuration."
|
||||
msgstr "現在のシステム設定をエクスポートします。"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Fahrenheit (°F)"
|
||||
msgstr "華氏 (°F)"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Failed to authenticate"
|
||||
msgstr "認証に失敗しました"
|
||||
@@ -396,12 +488,13 @@ msgstr "アラートの更新に失敗しました"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Filter..."
|
||||
msgstr "フィルター..."
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Fingerprint"
|
||||
msgstr ""
|
||||
msgstr "フィンガープリント"
|
||||
|
||||
#: src/components/alerts/alerts-system.tsx
|
||||
msgid "For <0>{min}</0> {min, plural, one {minute} other {minutes}}"
|
||||
@@ -448,16 +541,6 @@ msgstr "無効なメールアドレスです。"
|
||||
msgid "Kernel"
|
||||
msgstr "カーネル"
|
||||
|
||||
#. Load average 15 minutes
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "L15"
|
||||
msgstr ""
|
||||
|
||||
#. Load average 5 minutes
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "L5"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "言語"
|
||||
@@ -471,13 +554,26 @@ msgstr "レイアウト"
|
||||
msgid "Light"
|
||||
msgstr "ライト"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Load Average"
|
||||
msgstr "ロードアベレージ"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 15m"
|
||||
msgstr ""
|
||||
msgstr "ロードアベレージ (15分)"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 1m"
|
||||
msgstr "ロードアベレージ (1分)"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 5m"
|
||||
msgstr ""
|
||||
msgstr "ロードアベレージ (5分)"
|
||||
|
||||
#. Short label for load average
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Load Avg"
|
||||
msgstr "ロードアベレージ"
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Log Out"
|
||||
@@ -514,7 +610,7 @@ msgstr "手動セットアップの手順"
|
||||
msgid "Max 1 min"
|
||||
msgstr "最大1分"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Memory"
|
||||
msgstr "メモリ"
|
||||
|
||||
@@ -527,11 +623,12 @@ msgstr "メモリ使用率"
|
||||
msgid "Memory usage of docker containers"
|
||||
msgstr "Dockerコンテナのメモリ使用率"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Name"
|
||||
msgstr "名前"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Net"
|
||||
msgstr "帯域"
|
||||
|
||||
@@ -543,10 +640,19 @@ msgstr "Dockerコンテナのネットワークトラフィック"
|
||||
msgid "Network traffic of public interfaces"
|
||||
msgstr "パブリックインターフェースのネットワークトラフィック"
|
||||
|
||||
#. Context: Bytes or bits
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Network unit"
|
||||
msgstr "ネットワーク単位"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "No results found."
|
||||
msgstr "結果が見つかりませんでした。"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "No results."
|
||||
msgstr "結果がありません。"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "No systems found."
|
||||
@@ -566,7 +672,7 @@ msgstr "OAuth 2 / OIDCサポート"
|
||||
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
|
||||
msgstr "再起動のたびに、データベース内のシステムはファイルに定義されたシステムに一致するように更新されます。"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Open menu"
|
||||
@@ -584,6 +690,12 @@ msgstr "既存のアラートを上書き"
|
||||
msgid "Page"
|
||||
msgstr "ページ"
|
||||
|
||||
#. placeholder {0}: table.getState().pagination.pageIndex + 1
|
||||
#. placeholder {1}: table.getPageCount()
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Page {0} of {1}"
|
||||
msgstr "{1}ページ中{0}ページ目"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Pages / Settings"
|
||||
msgstr "ページ / 設定"
|
||||
@@ -605,11 +717,11 @@ msgstr "パスワードは72バイト未満でなければなりません。"
|
||||
msgid "Password reset request received"
|
||||
msgstr "パスワードリセットのリクエストを受け取りました"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Pause"
|
||||
msgstr "一時停止"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Paused"
|
||||
msgstr "一時停止中"
|
||||
|
||||
@@ -665,13 +777,12 @@ msgid "Public Key"
|
||||
msgstr "公開鍵"
|
||||
|
||||
#. Disk read
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Read"
|
||||
msgstr "読み取り"
|
||||
|
||||
#. Network bytes received (download)
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Received"
|
||||
msgstr "受信"
|
||||
|
||||
@@ -679,7 +790,13 @@ msgstr "受信"
|
||||
msgid "Reset Password"
|
||||
msgstr "パスワードをリセット"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Resolved"
|
||||
msgstr "解決済み"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Resume"
|
||||
msgstr "再開"
|
||||
|
||||
@@ -687,6 +804,10 @@ msgstr "再開"
|
||||
msgid "Rotate token"
|
||||
msgstr "トークンをローテート"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Rows per page"
|
||||
msgstr "ページあたりの行数"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Save address using enter key or comma. Leave blank to disable email notifications."
|
||||
msgstr "Enterキーまたはカンマを使用してアドレスを保存します。空白のままにするとメール通知が無効になります。"
|
||||
@@ -712,11 +833,14 @@ msgstr "システムまたは設定を検索..."
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "アラートの受信方法を設定するには<0>通知設定</0>を参照してください。"
|
||||
|
||||
#. Network bytes sent (upload)
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "送信"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "メーターの色にパーセンテージのしきい値を設定します。"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Sets the default time range for charts when a system is viewed."
|
||||
msgstr "システムを表示する際のチャートのデフォルトの時間範囲を設定します。"
|
||||
@@ -744,6 +868,11 @@ msgstr "SMTP設定"
|
||||
msgid "Sort By"
|
||||
msgstr "並び替え基準"
|
||||
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "状態"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Status"
|
||||
msgstr "ステータス"
|
||||
@@ -759,11 +888,16 @@ msgstr "スワップ使用量"
|
||||
#. System theme
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "System"
|
||||
msgstr "システム"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "System load averages over time"
|
||||
msgstr "システムのロードアベレージの推移"
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Systems"
|
||||
msgstr "システム"
|
||||
@@ -777,7 +911,7 @@ msgid "Table"
|
||||
msgstr "テーブル"
|
||||
|
||||
#. Temperature label in systems table
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Temp"
|
||||
msgstr "温度"
|
||||
|
||||
@@ -786,6 +920,10 @@ msgstr "温度"
|
||||
msgid "Temperature"
|
||||
msgstr "温度"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Temperature unit"
|
||||
msgstr "温度単位"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Temperatures of system sensors"
|
||||
msgstr "システムセンサーの温度"
|
||||
@@ -802,10 +940,14 @@ msgstr "テスト通知が送信されました"
|
||||
msgid "Then log into the backend and reset your user account password in the users table."
|
||||
msgstr "その後、バックエンドにログインして、ユーザーテーブルでユーザーアカウントのパスワードをリセットしてください。"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "This action cannot be undone. This will permanently delete all current records for {name} from the database."
|
||||
msgstr "この操作は元に戻せません。これにより、データベースから{name}のすべての現在のレコードが永久に削除されます。"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "This will permanently delete all selected records from the database."
|
||||
msgstr "これにより、選択したすべてのレコードがデータベースから完全に削除されます。"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Throughput of {extraFsName}"
|
||||
msgstr "{extraFsName}のスループット"
|
||||
@@ -846,13 +988,17 @@ msgstr "トークンはエージェントの接続と登録を可能にします
|
||||
msgid "Tokens and fingerprints are used to authenticate WebSocket connections to the hub."
|
||||
msgstr "トークンとフィンガープリントは、ハブへのWebSocket接続の認証に使用されます。"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when 1 minute load average exceeds a threshold"
|
||||
msgstr "1分間のロードアベレージがしきい値を超えたときにトリガーされます"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when 15 minute load average exceeds a threshold"
|
||||
msgstr ""
|
||||
msgstr "15分間のロードアベレージがしきい値を超えたときにトリガーされます"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when 5 minute load average exceeds a threshold"
|
||||
msgstr ""
|
||||
msgstr "5分間のロードアベレージがしきい値を超えたときにトリガーされます"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when any sensor exceeds a threshold"
|
||||
@@ -878,12 +1024,17 @@ msgstr "ステータスが上から下に切り替わるときにトリガーさ
|
||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||
msgstr "ディスクの使用量がしきい値を超えたときにトリガーされます"
|
||||
|
||||
#. Temperature / network units
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Unit preferences"
|
||||
msgstr "単位の設定"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Universal token"
|
||||
msgstr "ユニバーサルトークン"
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Up"
|
||||
msgstr "正常"
|
||||
@@ -898,7 +1049,8 @@ msgstr "稼働時間"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Usage"
|
||||
msgstr "使用量"
|
||||
|
||||
@@ -908,7 +1060,6 @@ msgstr "ルートパーティションの使用量"
|
||||
|
||||
#: src/components/charts/swap-chart.tsx
|
||||
#: src/components/charts/mem-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
msgid "Used"
|
||||
msgstr "使用中"
|
||||
|
||||
@@ -917,10 +1068,18 @@ msgstr "使用中"
|
||||
msgid "Users"
|
||||
msgstr "ユーザー"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Value"
|
||||
msgstr "値"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "View"
|
||||
msgstr "表示"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "View your 200 most recent alerts."
|
||||
msgstr "直近200件のアラートを表示します。"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Visible Fields"
|
||||
msgstr "表示列"
|
||||
@@ -931,7 +1090,15 @@ msgstr "表示するのに十分なレコードを待っています"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Want to help improve our translations? Check <0>Crowdin</0> for details."
|
||||
msgstr "翻訳をさらに良くするためにご協力いただけますか?詳細については<0>Crowdin</0>をご覧ください。"
|
||||
msgstr "翻訳をさらに良くするためにご協力をお願いします。詳細については<0>Crowdin</0>をご覧ください。"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Warning (%)"
|
||||
msgstr "警告 (%)"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Warning thresholds"
|
||||
msgstr "警告のしきい値"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Webhook / Push notifications"
|
||||
@@ -948,8 +1115,8 @@ msgid "Windows command"
|
||||
msgstr "Windows コマンド"
|
||||
|
||||
#. Disk write
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Write"
|
||||
msgstr "書き込み"
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: ko\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-03-07 10:06\n"
|
||||
"PO-Revision-Date: 2025-07-25 22:44\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Korean\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
@@ -23,6 +23,12 @@ msgstr ""
|
||||
msgid "{0, plural, one {# day} other {# days}}"
|
||||
msgstr "{0, plural, one {# 일} other {# 일}}"
|
||||
|
||||
#. placeholder {0}: table.getFilteredSelectedRowModel().rows.length
|
||||
#. placeholder {1}: table.getFilteredRowModel().rows.length
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "{0} of {1} row(s) selected."
|
||||
msgstr "{1}개의 행 중 {0}개가 선택되었습니다."
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "{hours, plural, one {# hour} other {# hours}}"
|
||||
msgstr "{hours, plural, one {# 시간} other {# 시간}}"
|
||||
@@ -31,6 +37,11 @@ msgstr "{hours, plural, one {# 시간} other {# 시간}}"
|
||||
msgid "1 hour"
|
||||
msgstr "1시간"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "1 min"
|
||||
msgstr "1분"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 week"
|
||||
msgstr "1주"
|
||||
@@ -39,6 +50,11 @@ msgstr "1주"
|
||||
msgid "12 hours"
|
||||
msgstr "12시간"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "15 min"
|
||||
msgstr "15분"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "24 hours"
|
||||
msgstr "24시간"
|
||||
@@ -47,12 +63,22 @@ msgstr "24시간"
|
||||
msgid "30 days"
|
||||
msgstr "30일"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "5 min"
|
||||
msgstr "5분"
|
||||
|
||||
#. Table column
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Actions"
|
||||
msgstr "작업"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Active"
|
||||
msgstr "활성"
|
||||
|
||||
#: src/components/routes/home.tsx
|
||||
msgid "Active Alerts"
|
||||
msgstr "활성화된 알림들"
|
||||
@@ -82,10 +108,16 @@ msgstr "차트 표시 옵션 변경."
|
||||
msgid "Admin"
|
||||
msgstr "관리자"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "에이전트"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Alert History"
|
||||
msgstr "알림 기록"
|
||||
|
||||
#: src/components/alerts/alert-button.tsx
|
||||
#: src/components/alerts/alert-button.tsx
|
||||
msgid "Alerts"
|
||||
@@ -96,10 +128,14 @@ msgstr "알림"
|
||||
msgid "All Systems"
|
||||
msgstr "모든 시스템"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Are you sure you want to delete {name}?"
|
||||
msgstr "{name}을(를) 삭제하시겠습니까?"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Are you sure?"
|
||||
msgstr "확실합니까?"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Automatic copy requires a secure context."
|
||||
msgstr "자동 복사는 안전한 컨텍스트가 필요합니다."
|
||||
@@ -110,7 +146,7 @@ msgstr "평균"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Average CPU utilization of containers"
|
||||
msgstr "컨테이너의 평균 CPU 사용량"
|
||||
msgstr "Docker 컨테이너의 평균 CPU 사용량"
|
||||
|
||||
#. placeholder {0}: data.alert.unit
|
||||
#: src/components/alerts/alerts-system.tsx
|
||||
@@ -152,11 +188,22 @@ msgstr "Beszel은 여러 인기 있는 알림 서비스와 연동하기 위해 <
|
||||
msgid "Binary"
|
||||
msgstr "실행 파일"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bits (Kbps, Mbps, Gbps)"
|
||||
msgstr "비트 (Kbps, Mbps, Gbps)"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bytes (KB/s, MB/s, GB/s)"
|
||||
msgstr "바이트 (KB/s, MB/s, GB/s)"
|
||||
|
||||
#: src/components/charts/mem-chart.tsx
|
||||
msgid "Cache / Buffers"
|
||||
msgstr "캐시 / 버퍼"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Cancel"
|
||||
msgstr "취소"
|
||||
|
||||
@@ -164,6 +211,14 @@ msgstr "취소"
|
||||
msgid "Caution - potential data loss"
|
||||
msgstr "주의 - 데이터 손실 가능성"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Celsius (°C)"
|
||||
msgstr "섭씨 (°C)"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change display units for metrics."
|
||||
msgstr "메트릭의 표시 단위를 변경합니다."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change general application options."
|
||||
msgstr "일반 애플리케이션 옵션 변경."
|
||||
@@ -202,7 +257,12 @@ msgstr "알림을 수신할 방법을 설정하세요."
|
||||
msgid "Confirm password"
|
||||
msgstr "비밀번호 확인"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/routes/home.tsx
|
||||
msgid "Connection is down"
|
||||
msgstr "연결이 끊겼습니다"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Continue"
|
||||
msgstr "계속"
|
||||
|
||||
@@ -214,20 +274,20 @@ msgstr "클립보드에 복사됨"
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy docker compose file content"
|
||||
msgid "Copy docker compose"
|
||||
msgstr "docker compose 복사"
|
||||
msgstr "docker compose 내용 복사"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy docker run command"
|
||||
msgid "Copy docker run"
|
||||
msgstr "docker run 복사"
|
||||
msgstr "docker run 명령어 복사"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Environment variables"
|
||||
msgid "Copy env"
|
||||
msgstr ""
|
||||
msgstr "환경 복사"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Copy host"
|
||||
msgstr "호스트 복사"
|
||||
|
||||
@@ -236,29 +296,33 @@ msgstr "호스트 복사"
|
||||
msgid "Copy Linux command"
|
||||
msgstr "리눅스 명령어 복사"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Copy name"
|
||||
msgstr "이름 복사"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
msgstr "텍스트 복사"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Copy the installation command for the agent below, or register agents automatically with a <0>universal token</0>."
|
||||
msgstr ""
|
||||
msgstr "아래 에이전트의 설치 명령을 복사하거나 <0>범용 토큰</0>으로 에이전트를 자동으로 등록하세요."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Copy the<0>docker-compose.yml</0> content for the agent below, or register agents automatically with a <1>universal token</1>."
|
||||
msgstr ""
|
||||
msgstr "아래 에이전트의 <0>docker-compose.yml</0> 내용을 복사하거나 <1>범용 토큰</1>으로 에이전트를 자동으로 등록하세요."
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Copy YAML"
|
||||
msgstr ""
|
||||
msgstr "YAML 복사"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "CPU"
|
||||
msgstr "CPU"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "CPU Usage"
|
||||
msgstr "CPU 사용량"
|
||||
|
||||
@@ -266,6 +330,15 @@ msgstr "CPU 사용량"
|
||||
msgid "Create account"
|
||||
msgstr "계정 생성"
|
||||
|
||||
#. Context: date created
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Created"
|
||||
msgstr "생성됨"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Critical (%)"
|
||||
msgstr "치명적 (%)"
|
||||
|
||||
#. Dark theme
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Dark"
|
||||
@@ -280,15 +353,16 @@ msgstr "대시보드"
|
||||
msgid "Default time period"
|
||||
msgstr "기본 기간"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Delete"
|
||||
msgstr "삭제"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Delete fingerprint"
|
||||
msgstr ""
|
||||
msgstr "지문 삭제"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Disk"
|
||||
msgstr "디스크"
|
||||
|
||||
@@ -296,6 +370,10 @@ msgstr "디스크"
|
||||
msgid "Disk I/O"
|
||||
msgstr "디스크 I/O"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Disk unit"
|
||||
msgstr "디스크 단위"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/disk-chart.tsx
|
||||
@@ -324,13 +402,18 @@ msgstr "문서"
|
||||
|
||||
#. Context: System is down
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Down"
|
||||
msgstr "오프라인"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Duration"
|
||||
msgstr "기간"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Edit"
|
||||
msgstr "수정"
|
||||
|
||||
@@ -354,6 +437,7 @@ msgstr "이메일 주소 입력..."
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Error"
|
||||
msgstr "오류"
|
||||
@@ -369,6 +453,10 @@ msgstr "마지막 {2, plural, one {# 분} other {# 분}} 동안 {0}{1} 초과"
|
||||
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
|
||||
msgstr "<0>config.yml</0>에 정의되지 않은 기존 시스템은 삭제됩니다. 정기적으로 백업을 하세요."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr "내보내기"
|
||||
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
msgid "Export configuration"
|
||||
msgstr "구성 내보내기"
|
||||
@@ -377,6 +465,10 @@ msgstr "구성 내보내기"
|
||||
msgid "Export your current systems configuration."
|
||||
msgstr "현재 시스템 구성 내보내기"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Fahrenheit (°F)"
|
||||
msgstr "화씨 (°F)"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Failed to authenticate"
|
||||
msgstr "인증 실패"
|
||||
@@ -396,12 +488,13 @@ msgstr "알림 수정 실패"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Filter..."
|
||||
msgstr "필터..."
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Fingerprint"
|
||||
msgstr ""
|
||||
msgstr "지문"
|
||||
|
||||
#: src/components/alerts/alerts-system.tsx
|
||||
msgid "For <0>{min}</0> {min, plural, one {minute} other {minutes}}"
|
||||
@@ -448,16 +541,6 @@ msgstr "잘못된 이메일 주소입니다."
|
||||
msgid "Kernel"
|
||||
msgstr "커널"
|
||||
|
||||
#. Load average 15 minutes
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "L15"
|
||||
msgstr ""
|
||||
|
||||
#. Load average 5 minutes
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "L5"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "언어"
|
||||
@@ -471,13 +554,26 @@ msgstr "레이아웃"
|
||||
msgid "Light"
|
||||
msgstr "밝게"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Load Average"
|
||||
msgstr "부하 평균"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 15m"
|
||||
msgstr ""
|
||||
msgstr "부하 평균 15분"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 1m"
|
||||
msgstr "부하 평균 1분"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 5m"
|
||||
msgstr ""
|
||||
msgstr "부하 평균 5분"
|
||||
|
||||
#. Short label for load average
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Load Avg"
|
||||
msgstr "부하 평균"
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Log Out"
|
||||
@@ -514,7 +610,7 @@ msgstr "수동 설정 방법"
|
||||
msgid "Max 1 min"
|
||||
msgstr "1분간 최댓값"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Memory"
|
||||
msgstr "메모리"
|
||||
|
||||
@@ -527,11 +623,12 @@ msgstr "메모리 사용량"
|
||||
msgid "Memory usage of docker containers"
|
||||
msgstr "Docker 컨테이너의 메모리 사용량"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Name"
|
||||
msgstr "이름"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Net"
|
||||
msgstr "네트워크"
|
||||
|
||||
@@ -543,10 +640,19 @@ msgstr "Docker 컨테이너의 네트워크 트래픽"
|
||||
msgid "Network traffic of public interfaces"
|
||||
msgstr "공용 인터페이스의 네트워크 트래픽"
|
||||
|
||||
#. Context: Bytes or bits
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Network unit"
|
||||
msgstr "네트워크 단위"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "No results found."
|
||||
msgstr "결과가 없습니다."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "No results."
|
||||
msgstr "결과 없음."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "No systems found."
|
||||
@@ -566,7 +672,7 @@ msgstr "OAuth 2 / OIDC 지원"
|
||||
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
|
||||
msgstr "매 시작 시, 데이터베이스가 파일에 정의된 시스템과 일치하도록 업데이트됩니다."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Open menu"
|
||||
@@ -584,6 +690,12 @@ msgstr "기존 알림 덮어쓰기"
|
||||
msgid "Page"
|
||||
msgstr "페이지"
|
||||
|
||||
#. placeholder {0}: table.getState().pagination.pageIndex + 1
|
||||
#. placeholder {1}: table.getPageCount()
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Page {0} of {1}"
|
||||
msgstr "{1}페이지 중 {0}페이지"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Pages / Settings"
|
||||
msgstr "페이지 / 설정"
|
||||
@@ -605,13 +717,13 @@ msgstr "비밀번호는 72 바이트 이하여야 합니다."
|
||||
msgid "Password reset request received"
|
||||
msgstr "비밀번호 재설정 요청이 접수되었습니다"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Pause"
|
||||
msgstr "일시 중지"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Paused"
|
||||
msgstr "일시정지됨"
|
||||
msgstr "일시 정지됨"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
@@ -665,13 +777,12 @@ msgid "Public Key"
|
||||
msgstr "공개 키"
|
||||
|
||||
#. Disk read
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Read"
|
||||
msgstr "읽기"
|
||||
|
||||
#. Network bytes received (download)
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Received"
|
||||
msgstr "수신됨"
|
||||
|
||||
@@ -679,13 +790,23 @@ msgstr "수신됨"
|
||||
msgid "Reset Password"
|
||||
msgstr "비밀번호 재설정"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Resolved"
|
||||
msgstr "해결됨"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Resume"
|
||||
msgstr "재개"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Rotate token"
|
||||
msgstr ""
|
||||
msgstr "토큰 회전"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Rows per page"
|
||||
msgstr "페이지당 행 수"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Save address using enter key or comma. Leave blank to disable email notifications."
|
||||
@@ -712,11 +833,14 @@ msgstr "시스템 또는 설정 검색..."
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "알림을 받는 방법을 구성하려면 <0>알림 설정</0>을 참조하세요."
|
||||
|
||||
#. Network bytes sent (upload)
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "보냄"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "미터 색상에 대한 백분율 임계값을 설정합니다."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Sets the default time range for charts when a system is viewed."
|
||||
msgstr "시스템을 볼 때 차트의 기본 시간 범위를 설정합니다."
|
||||
@@ -744,6 +868,11 @@ msgstr "SMTP 설정"
|
||||
msgid "Sort By"
|
||||
msgstr "정렬 기준"
|
||||
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "State"
|
||||
msgstr "상태"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Status"
|
||||
msgstr "상태"
|
||||
@@ -759,11 +888,16 @@ msgstr "스왑 사용량"
|
||||
#. System theme
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "System"
|
||||
msgstr "시스템"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "System load averages over time"
|
||||
msgstr "시간에 따른 시스템 부하 평균"
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Systems"
|
||||
msgstr "시스템"
|
||||
@@ -777,7 +911,7 @@ msgid "Table"
|
||||
msgstr "표"
|
||||
|
||||
#. Temperature label in systems table
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Temp"
|
||||
msgstr "온도"
|
||||
|
||||
@@ -786,6 +920,10 @@ msgstr "온도"
|
||||
msgid "Temperature"
|
||||
msgstr "온도"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Temperature unit"
|
||||
msgstr "온도 단위"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Temperatures of system sensors"
|
||||
msgstr "시스템 센서의 온도"
|
||||
@@ -802,10 +940,14 @@ msgstr "테스트 알림이 전송되었습니다."
|
||||
msgid "Then log into the backend and reset your user account password in the users table."
|
||||
msgstr "그런 다음 백엔드에 로그인하여 사용자 테이블에서 사용자 계정 비밀번호를 재설정하세요."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "This action cannot be undone. This will permanently delete all current records for {name} from the database."
|
||||
msgstr "이 작업은 되돌릴 수 없습니다. 데이터베이스에서 {name}에 대한 모든 현재 기록이 영구적으로 삭제됩니다."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "This will permanently delete all selected records from the database."
|
||||
msgstr "선택한 모든 레코드를 데이터베이스에서 영구적으로 삭제합니다."
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Throughput of {extraFsName}"
|
||||
msgstr "{extraFsName}의 처리량"
|
||||
@@ -830,29 +972,33 @@ msgstr "테마 전환"
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Token"
|
||||
msgstr ""
|
||||
msgstr "토큰"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Tokens & Fingerprints"
|
||||
msgstr ""
|
||||
msgstr "토큰 및 지문"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Tokens allow agents to connect and register. Fingerprints are stable identifiers unique to each system, set on first connection."
|
||||
msgstr ""
|
||||
msgstr "토큰은 에이전트가 연결하고 등록할 수 있도록 합니다. 지문은 첫 연결 시 설정되는 각 시스템의 고유한 안정적인 식별자입니다."
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Tokens and fingerprints are used to authenticate WebSocket connections to the hub."
|
||||
msgstr ""
|
||||
msgstr "토큰과 지문은 허브에 대한 WebSocket 연결을 인증하는 데 사용됩니다."
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when 1 minute load average exceeds a threshold"
|
||||
msgstr "1분 부하 평균이 임계값을 초과하면 트리거됩니다."
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when 15 minute load average exceeds a threshold"
|
||||
msgstr ""
|
||||
msgstr "15분 부하 평균이 임계값을 초과하면 트리거됩니다."
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when 5 minute load average exceeds a threshold"
|
||||
msgstr ""
|
||||
msgstr "5분 부하 평균이 임계값을 초과하면 트리거됩니다."
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when any sensor exceeds a threshold"
|
||||
@@ -878,12 +1024,17 @@ msgstr "시스템의 전원이 켜지거나 꺼질때 트리거됩니다."
|
||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||
msgstr "디스크 사용량이 임계값을 초과할 때 트리거됩니다."
|
||||
|
||||
#. Temperature / network units
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Unit preferences"
|
||||
msgstr "단위 기본 설정"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Universal token"
|
||||
msgstr ""
|
||||
msgstr "범용 토큰"
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Up"
|
||||
msgstr "온라인"
|
||||
@@ -898,7 +1049,8 @@ msgstr "가동 시간"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Usage"
|
||||
msgstr "사용량"
|
||||
|
||||
@@ -908,7 +1060,6 @@ msgstr "루트 파티션의 사용량"
|
||||
|
||||
#: src/components/charts/swap-chart.tsx
|
||||
#: src/components/charts/mem-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
msgid "Used"
|
||||
msgstr "사용됨"
|
||||
|
||||
@@ -917,10 +1068,18 @@ msgstr "사용됨"
|
||||
msgid "Users"
|
||||
msgstr "사용자"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Value"
|
||||
msgstr "값"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "View"
|
||||
msgstr "보기"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "View your 200 most recent alerts."
|
||||
msgstr "최근 200개의 알림을 봅니다."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Visible Fields"
|
||||
msgstr "표시할 열"
|
||||
@@ -931,7 +1090,15 @@ msgstr "표시할 충분한 기록을 기다리는 중"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Want to help improve our translations? Check <0>Crowdin</0> for details."
|
||||
msgstr "번역을 더 좋게 만드는 데 도움을 주시겠습니까? 자세한 내용은 <0>Crowdin</0>을 확인하세요."
|
||||
msgstr "번역을 개선하는데 도움을 주시겠습니까? 자세한 내용은 <0>Crowdin</0>을 확인해 주세요."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Warning (%)"
|
||||
msgstr "경고 (%)"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Warning thresholds"
|
||||
msgstr "경고 임계값"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Webhook / Push notifications"
|
||||
@@ -939,7 +1106,7 @@ msgstr "Webhook / 푸시 알림"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation. Expires after one hour or on hub restart."
|
||||
msgstr ""
|
||||
msgstr "활성화하면 이 토큰을 통해 에이전트가 사전 시스템 생성 없이 자체 등록할 수 있습니다. 1시간 후 또는 허브 재시작 시 만료됩니다."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
@@ -948,8 +1115,8 @@ msgid "Windows command"
|
||||
msgstr "Windows 명령어"
|
||||
|
||||
#. Disk write
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Write"
|
||||
msgstr "쓰기"
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: nl\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-03-06 07:27\n"
|
||||
"PO-Revision-Date: 2025-07-25 22:44\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Dutch\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -23,6 +23,12 @@ msgstr ""
|
||||
msgid "{0, plural, one {# day} other {# days}}"
|
||||
msgstr "{0, plural, one {# dag} other {# dagen}}"
|
||||
|
||||
#. placeholder {0}: table.getFilteredSelectedRowModel().rows.length
|
||||
#. placeholder {1}: table.getFilteredRowModel().rows.length
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "{0} of {1} row(s) selected."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "{hours, plural, one {# hour} other {# hours}}"
|
||||
msgstr "{hours, plural, one {# uur} other {# uren}}"
|
||||
@@ -31,6 +37,11 @@ msgstr "{hours, plural, one {# uur} other {# uren}}"
|
||||
msgid "1 hour"
|
||||
msgstr "1 uur"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "1 min"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 week"
|
||||
msgstr "1 week"
|
||||
@@ -39,6 +50,11 @@ msgstr "1 week"
|
||||
msgid "12 hours"
|
||||
msgstr "12 uren"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "15 min"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "24 hours"
|
||||
msgstr "24 uren"
|
||||
@@ -47,12 +63,22 @@ msgstr "24 uren"
|
||||
msgid "30 days"
|
||||
msgstr "30 dagen"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "5 min"
|
||||
msgstr ""
|
||||
|
||||
#. Table column
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Actions"
|
||||
msgstr "Acties"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Active"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/home.tsx
|
||||
msgid "Active Alerts"
|
||||
msgstr "Actieve waarschuwingen"
|
||||
@@ -82,10 +108,16 @@ msgstr "Weergaveopties voor grafieken aanpassen."
|
||||
msgid "Admin"
|
||||
msgstr "Admin"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Agent"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Alert History"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/alerts/alert-button.tsx
|
||||
#: src/components/alerts/alert-button.tsx
|
||||
msgid "Alerts"
|
||||
@@ -96,10 +128,14 @@ msgstr "Waarschuwingen"
|
||||
msgid "All Systems"
|
||||
msgstr "Alle systemen"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Are you sure you want to delete {name}?"
|
||||
msgstr "Weet je zeker dat je {name} wilt verwijderen?"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Are you sure?"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Automatic copy requires a secure context."
|
||||
msgstr "Automatisch kopiëren vereist een veilige context."
|
||||
@@ -152,11 +188,22 @@ msgstr "Beszel gebruikt <0>Shoutrr</0> om te integreren met populaire meldingsdi
|
||||
msgid "Binary"
|
||||
msgstr "Binair"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bits (Kbps, Mbps, Gbps)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bytes (KB/s, MB/s, GB/s)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/charts/mem-chart.tsx
|
||||
msgid "Cache / Buffers"
|
||||
msgstr "Cache / Buffers"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Cancel"
|
||||
msgstr "Annuleren"
|
||||
|
||||
@@ -164,6 +211,14 @@ msgstr "Annuleren"
|
||||
msgid "Caution - potential data loss"
|
||||
msgstr "Opgelet - potentieel gegevensverlies"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Celsius (°C)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change display units for metrics."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change general application options."
|
||||
msgstr "Wijzig algemene applicatie opties."
|
||||
@@ -202,7 +257,12 @@ msgstr "Configureer hoe je waarschuwingsmeldingen ontvangt."
|
||||
msgid "Confirm password"
|
||||
msgstr "Bevestig wachtwoord"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/routes/home.tsx
|
||||
msgid "Connection is down"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Continue"
|
||||
msgstr "Volgende"
|
||||
|
||||
@@ -225,9 +285,9 @@ msgstr "Docker run kopiëren"
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Environment variables"
|
||||
msgid "Copy env"
|
||||
msgstr ""
|
||||
msgstr "Env kopiëren"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Copy host"
|
||||
msgstr "Kopieer host"
|
||||
|
||||
@@ -236,29 +296,33 @@ msgstr "Kopieer host"
|
||||
msgid "Copy Linux command"
|
||||
msgstr "Kopieer Linux-opdracht"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Copy name"
|
||||
msgstr "Kopieer naam"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
msgstr "Kopieer tekst"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Copy the installation command for the agent below, or register agents automatically with a <0>universal token</0>."
|
||||
msgstr ""
|
||||
msgstr "Kopieer de installatie opdracht voor de agent hieronder, of registreer agenten automatisch met een <0>universele token</0>."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Copy the<0>docker-compose.yml</0> content for the agent below, or register agents automatically with a <1>universal token</1>."
|
||||
msgstr ""
|
||||
msgstr "Kopieer de<0>docker-compose.yml</0> inhoud voor de agent hieronder, of registreer agenten automatisch met een <1>universele token</1>."
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Copy YAML"
|
||||
msgstr ""
|
||||
msgstr "YAML kopiëren"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "CPU"
|
||||
msgstr "CPU"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "CPU Usage"
|
||||
msgstr "Processorgebruik"
|
||||
|
||||
@@ -266,6 +330,15 @@ msgstr "Processorgebruik"
|
||||
msgid "Create account"
|
||||
msgstr "Account aanmaken"
|
||||
|
||||
#. Context: date created
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Created"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Critical (%)"
|
||||
msgstr "Kritiek (%)"
|
||||
|
||||
#. Dark theme
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Dark"
|
||||
@@ -280,15 +353,16 @@ msgstr "Dashboard"
|
||||
msgid "Default time period"
|
||||
msgstr "Standaard tijdsduur"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Delete"
|
||||
msgstr "Verwijderen"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Delete fingerprint"
|
||||
msgstr ""
|
||||
msgstr "Vingerafdruk verwijderen"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Disk"
|
||||
msgstr "Schijf"
|
||||
|
||||
@@ -296,6 +370,10 @@ msgstr "Schijf"
|
||||
msgid "Disk I/O"
|
||||
msgstr "Schijf I/O"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Disk unit"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/disk-chart.tsx
|
||||
@@ -324,13 +402,18 @@ msgstr "Documentatie"
|
||||
|
||||
#. Context: System is down
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Down"
|
||||
msgstr "Offline"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Duration"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Edit"
|
||||
msgstr "Bewerken"
|
||||
|
||||
@@ -354,6 +437,7 @@ msgstr "Voer een e-mailadres in..."
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Error"
|
||||
msgstr "Fout"
|
||||
@@ -369,6 +453,10 @@ msgstr "Overschrijdt {0}{1} in de laatste {2, plural, one {# minuut} other {# mi
|
||||
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
|
||||
msgstr "Bestaande systemen die niet gedefinieerd zijn in <0>config.yml</0> zullen worden verwijderd. Maak regelmatige backups."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
msgid "Export configuration"
|
||||
msgstr "Configuratie exporteren"
|
||||
@@ -377,6 +465,10 @@ msgstr "Configuratie exporteren"
|
||||
msgid "Export your current systems configuration."
|
||||
msgstr "Exporteer je huidige systeemconfiguratie."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Fahrenheit (°F)"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Failed to authenticate"
|
||||
msgstr "Authenticatie mislukt"
|
||||
@@ -396,12 +488,13 @@ msgstr "Bijwerken waarschuwing mislukt"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Filter..."
|
||||
msgstr "Filter..."
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Fingerprint"
|
||||
msgstr ""
|
||||
msgstr "Vingerafdruk"
|
||||
|
||||
#: src/components/alerts/alerts-system.tsx
|
||||
msgid "For <0>{min}</0> {min, plural, one {minute} other {minutes}}"
|
||||
@@ -448,16 +541,6 @@ msgstr "Ongeldig e-mailadres."
|
||||
msgid "Kernel"
|
||||
msgstr "Kernel"
|
||||
|
||||
#. Load average 15 minutes
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "L15"
|
||||
msgstr ""
|
||||
|
||||
#. Load average 5 minutes
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "L5"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "Taal"
|
||||
@@ -471,12 +554,25 @@ msgstr "Indeling"
|
||||
msgid "Light"
|
||||
msgstr "Licht"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Load Average"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 15m"
|
||||
msgstr "Gemiddelde Belasting 15m"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 1m"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 5m"
|
||||
msgstr "Gemiddelde Belasting 5m"
|
||||
|
||||
#. Short label for load average
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Load Avg"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
@@ -514,7 +610,7 @@ msgstr "Handmatige installatie-instructies"
|
||||
msgid "Max 1 min"
|
||||
msgstr "Max 1 min"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Memory"
|
||||
msgstr "Geheugen"
|
||||
|
||||
@@ -527,11 +623,12 @@ msgstr "Geheugengebruik"
|
||||
msgid "Memory usage of docker containers"
|
||||
msgstr "Geheugengebruik van docker containers"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Name"
|
||||
msgstr "Naam"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Net"
|
||||
msgstr "Net"
|
||||
|
||||
@@ -543,10 +640,19 @@ msgstr "Netwerkverkeer van docker containers"
|
||||
msgid "Network traffic of public interfaces"
|
||||
msgstr "Netwerkverkeer van publieke interfaces"
|
||||
|
||||
#. Context: Bytes or bits
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Network unit"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "No results found."
|
||||
msgstr "Geen resultaten gevonden."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "No results."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "No systems found."
|
||||
@@ -566,7 +672,7 @@ msgstr "OAuth 2 / OIDC ondersteuning"
|
||||
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
|
||||
msgstr "Bij elke herstart zullen systemen in de database worden bijgewerkt om overeen te komen met de systemen die in het bestand zijn gedefinieerd."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Open menu"
|
||||
@@ -584,6 +690,12 @@ msgstr "Overschrijf bestaande waarschuwingen"
|
||||
msgid "Page"
|
||||
msgstr "Pagina"
|
||||
|
||||
#. placeholder {0}: table.getState().pagination.pageIndex + 1
|
||||
#. placeholder {1}: table.getPageCount()
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Page {0} of {1}"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Pages / Settings"
|
||||
msgstr "Pagina's / Instellingen"
|
||||
@@ -605,11 +717,11 @@ msgstr "Het wachtwoord moet minder zijn dat 72 bytes."
|
||||
msgid "Password reset request received"
|
||||
msgstr "Wachtwoord reset aanvraag ontvangen"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Pause"
|
||||
msgstr "Pauze"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Paused"
|
||||
msgstr "Gepauzeerd"
|
||||
|
||||
@@ -665,13 +777,12 @@ msgid "Public Key"
|
||||
msgstr "Publieke sleutel"
|
||||
|
||||
#. Disk read
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Read"
|
||||
msgstr "Lezen"
|
||||
|
||||
#. Network bytes received (download)
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Received"
|
||||
msgstr "Ontvangen"
|
||||
|
||||
@@ -679,12 +790,22 @@ msgstr "Ontvangen"
|
||||
msgid "Reset Password"
|
||||
msgstr "Wachtwoord resetten"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Resolved"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Hervatten"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Rotate token"
|
||||
msgstr "Roteer Token"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Rows per page"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
@@ -712,11 +833,14 @@ msgstr "Zoek naar systemen of instellingen..."
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "Zie <0>notificatie-instellingen</0> om te configureren hoe je meldingen ontvangt."
|
||||
|
||||
#. Network bytes sent (upload)
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "Verzonden"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "Stel percentagedrempels in voor meterkleuren."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Sets the default time range for charts when a system is viewed."
|
||||
msgstr "Stelt het standaard tijdsbereik voor grafieken in wanneer een systeem wordt bekeken."
|
||||
@@ -744,6 +868,11 @@ msgstr "SMTP-instellingen"
|
||||
msgid "Sort By"
|
||||
msgstr "Sorteren op"
|
||||
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "State"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Status"
|
||||
msgstr "Status"
|
||||
@@ -759,11 +888,16 @@ msgstr "Swap gebruik"
|
||||
#. System theme
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "System"
|
||||
msgstr "Systeem"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "System load averages over time"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Systems"
|
||||
msgstr "Systemen"
|
||||
@@ -777,7 +911,7 @@ msgid "Table"
|
||||
msgstr "Tabel"
|
||||
|
||||
#. Temperature label in systems table
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Temp"
|
||||
msgstr "Temperatuur"
|
||||
|
||||
@@ -786,6 +920,10 @@ msgstr "Temperatuur"
|
||||
msgid "Temperature"
|
||||
msgstr "Temperatuur"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Temperature unit"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Temperatures of system sensors"
|
||||
msgstr "Temperatuur van systeem sensoren"
|
||||
@@ -802,10 +940,14 @@ msgstr "Testmelding verzonden"
|
||||
msgid "Then log into the backend and reset your user account password in the users table."
|
||||
msgstr "Log vervolgens in op de backend en reset het wachtwoord van je gebruikersaccount in het gebruikersoverzicht."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "This action cannot be undone. This will permanently delete all current records for {name} from the database."
|
||||
msgstr "Deze actie kan niet ongedaan worden gemaakt. Dit zal alle huidige records voor {name} permanent verwijderen uit de database."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "This will permanently delete all selected records from the database."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Throughput of {extraFsName}"
|
||||
msgstr "Doorvoer van {extraFsName}"
|
||||
@@ -830,29 +972,33 @@ msgstr "Schakel thema"
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Token"
|
||||
msgstr ""
|
||||
msgstr "Token"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
msgid "Tokens & Fingerprints"
|
||||
msgstr ""
|
||||
msgstr "Tokens & Vingerafdrukken"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Tokens allow agents to connect and register. Fingerprints are stable identifiers unique to each system, set on first connection."
|
||||
msgstr ""
|
||||
msgstr "Tokens staan agenten toe om verbinding te maken met en te registreren. Vingerafdrukken zijn stabiele Ids deze zijn uniek voor elk systeem, ingesteld bij eerste verbinding."
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Tokens and fingerprints are used to authenticate WebSocket connections to the hub."
|
||||
msgstr "Tokens en vingerafdrukken worden gebruikt om WebSocket verbindingen te verifiëren naar de hub."
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when 1 minute load average exceeds a threshold"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when 15 minute load average exceeds a threshold"
|
||||
msgstr ""
|
||||
msgstr "Triggert wanneer de 15 minuten gemiddelde belasting een drempelwaarde overschrijdt"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when 5 minute load average exceeds a threshold"
|
||||
msgstr ""
|
||||
msgstr "Triggert wanneer de 5 minuten gemiddelde belasting een drempelwaarde overschrijdt"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when any sensor exceeds a threshold"
|
||||
@@ -878,12 +1024,17 @@ msgstr "Triggert wanneer de status schakelt tussen up en down"
|
||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||
msgstr "Triggert wanneer het gebruik van een schijf een drempelwaarde overschrijdt"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Universal token"
|
||||
#. Temperature / network units
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Unit preferences"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Universal token"
|
||||
msgstr "Universele token"
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Up"
|
||||
msgstr "Online"
|
||||
@@ -898,7 +1049,8 @@ msgstr "Actief"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Usage"
|
||||
msgstr "Gebruik"
|
||||
|
||||
@@ -908,7 +1060,6 @@ msgstr "Gebruik van root-partitie"
|
||||
|
||||
#: src/components/charts/swap-chart.tsx
|
||||
#: src/components/charts/mem-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
msgid "Used"
|
||||
msgstr "Gebruikt"
|
||||
|
||||
@@ -917,10 +1068,18 @@ msgstr "Gebruikt"
|
||||
msgid "Users"
|
||||
msgstr "Gebruikers"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Value"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "View"
|
||||
msgstr "Weergave"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "View your 200 most recent alerts."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Visible Fields"
|
||||
msgstr "Zichtbare kolommen"
|
||||
@@ -933,13 +1092,21 @@ msgstr "Wachtend op genoeg records om weer te geven"
|
||||
msgid "Want to help improve our translations? Check <0>Crowdin</0> for details."
|
||||
msgstr "Wil je ons helpen onze vertalingen nog beter te maken? Bekijk <0>Crowdin</0> voor meer informatie."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Warning (%)"
|
||||
msgstr "Waarschuwing (%)"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Warning thresholds"
|
||||
msgstr "Waarschuwingsdrempels"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Webhook / Push notifications"
|
||||
msgstr "Webhook / Pushmeldingen"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation. Expires after one hour or on hub restart."
|
||||
msgstr ""
|
||||
msgstr "Wanneer ingeschakeld kunnen agenten zich met dit token registreren zonder dat er vooraf een systeem aangemaakt hoeft te worden. Het token verloopt na één uur of bij herstart van de hub."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
@@ -948,8 +1115,8 @@ msgid "Windows command"
|
||||
msgstr "Windows-commando"
|
||||
|
||||
#. Disk write
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Write"
|
||||
msgstr "Schrijven"
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: no\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-03-06 07:27\n"
|
||||
"PO-Revision-Date: 2025-07-25 22:44\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Norwegian\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -23,6 +23,12 @@ msgstr ""
|
||||
msgid "{0, plural, one {# day} other {# days}}"
|
||||
msgstr "{0, plural, one {# dag} other {# dager}}"
|
||||
|
||||
#. placeholder {0}: table.getFilteredSelectedRowModel().rows.length
|
||||
#. placeholder {1}: table.getFilteredRowModel().rows.length
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "{0} of {1} row(s) selected."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "{hours, plural, one {# hour} other {# hours}}"
|
||||
msgstr "{hours, plural, one {# time} other {# timer}}"
|
||||
@@ -31,6 +37,11 @@ msgstr "{hours, plural, one {# time} other {# timer}}"
|
||||
msgid "1 hour"
|
||||
msgstr "1 time"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "1 min"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 week"
|
||||
msgstr "1 uke"
|
||||
@@ -39,6 +50,11 @@ msgstr "1 uke"
|
||||
msgid "12 hours"
|
||||
msgstr "12 timer"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "15 min"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "24 hours"
|
||||
msgstr "24 timer"
|
||||
@@ -47,12 +63,22 @@ msgstr "24 timer"
|
||||
msgid "30 days"
|
||||
msgstr "30 dager"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "5 min"
|
||||
msgstr ""
|
||||
|
||||
#. Table column
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Actions"
|
||||
msgstr "Handlinger"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Active"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/home.tsx
|
||||
msgid "Active Alerts"
|
||||
msgstr "Aktive Alarmer"
|
||||
@@ -82,10 +108,16 @@ msgstr "Juster visningsalternativer for diagrammer."
|
||||
msgid "Admin"
|
||||
msgstr "Admin"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Agent"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Alert History"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/alerts/alert-button.tsx
|
||||
#: src/components/alerts/alert-button.tsx
|
||||
msgid "Alerts"
|
||||
@@ -96,10 +128,14 @@ msgstr "Alarmer"
|
||||
msgid "All Systems"
|
||||
msgstr "Alle Systemer"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Are you sure you want to delete {name}?"
|
||||
msgstr "Er du sikker på at du vil slette {name}?"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Are you sure?"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Automatic copy requires a secure context."
|
||||
msgstr "Automatisk kopiering krever en sikker kontekst."
|
||||
@@ -152,11 +188,22 @@ msgstr "Beszel bruker <0>Shoutrrr</0> for integrering mot populære meldingstjen
|
||||
msgid "Binary"
|
||||
msgstr "Binær"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bits (Kbps, Mbps, Gbps)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bytes (KB/s, MB/s, GB/s)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/charts/mem-chart.tsx
|
||||
msgid "Cache / Buffers"
|
||||
msgstr "Cache / Buffere"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Cancel"
|
||||
msgstr "Avbryt"
|
||||
|
||||
@@ -164,6 +211,14 @@ msgstr "Avbryt"
|
||||
msgid "Caution - potential data loss"
|
||||
msgstr "Advarsel - potensielt tap av data"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Celsius (°C)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change display units for metrics."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change general application options."
|
||||
msgstr "Endre generelle program-innstillinger."
|
||||
@@ -202,7 +257,12 @@ msgstr "Konfigurer hvordan du vil motta alarmvarsler."
|
||||
msgid "Confirm password"
|
||||
msgstr "Bekreft passord"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/routes/home.tsx
|
||||
msgid "Connection is down"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Continue"
|
||||
msgstr "Fortsett"
|
||||
|
||||
@@ -227,7 +287,7 @@ msgctxt "Environment variables"
|
||||
msgid "Copy env"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Copy host"
|
||||
msgstr "Kopier vert"
|
||||
|
||||
@@ -236,6 +296,10 @@ msgstr "Kopier vert"
|
||||
msgid "Copy Linux command"
|
||||
msgstr "Kopier Linux-kommando"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Copy name"
|
||||
msgstr "Kopier navn"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
msgstr "Kopier tekst"
|
||||
@@ -252,13 +316,13 @@ msgstr ""
|
||||
msgid "Copy YAML"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "CPU"
|
||||
msgstr "CPU"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "CPU Usage"
|
||||
msgstr "CPU-bruk"
|
||||
|
||||
@@ -266,6 +330,15 @@ msgstr "CPU-bruk"
|
||||
msgid "Create account"
|
||||
msgstr "Opprett konto"
|
||||
|
||||
#. Context: date created
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Created"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Critical (%)"
|
||||
msgstr "Kritisk (%)"
|
||||
|
||||
#. Dark theme
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Dark"
|
||||
@@ -280,7 +353,8 @@ msgstr "Dashbord"
|
||||
msgid "Default time period"
|
||||
msgstr "Standard tidsperiode"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Delete"
|
||||
msgstr "Slett"
|
||||
|
||||
@@ -288,7 +362,7 @@ msgstr "Slett"
|
||||
msgid "Delete fingerprint"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Disk"
|
||||
msgstr "Disk"
|
||||
|
||||
@@ -296,6 +370,10 @@ msgstr "Disk"
|
||||
msgid "Disk I/O"
|
||||
msgstr "Disk I/O"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Disk unit"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/disk-chart.tsx
|
||||
@@ -324,13 +402,18 @@ msgstr "Dokumentasjon"
|
||||
|
||||
#. Context: System is down
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Down"
|
||||
msgstr "Nede"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Duration"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Edit"
|
||||
msgstr "Rediger"
|
||||
|
||||
@@ -354,6 +437,7 @@ msgstr "Skriv inn e-postadresse..."
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Error"
|
||||
msgstr "Feil"
|
||||
@@ -369,6 +453,10 @@ msgstr "Overstiger {0}{1} {2, plural, one {det siste minuttet} other {de siste #
|
||||
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
|
||||
msgstr "Eksisterende systemer som ikke er er definert i <0>config.yml</0> vil bli slettet. Vennligst ta jevnlige sikkerhetskopier."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
msgid "Export configuration"
|
||||
msgstr "Eksporter konfigurasjon"
|
||||
@@ -377,6 +465,10 @@ msgstr "Eksporter konfigurasjon"
|
||||
msgid "Export your current systems configuration."
|
||||
msgstr "Eksporter din nåværende systemkonfigurasjon"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Fahrenheit (°F)"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Failed to authenticate"
|
||||
msgstr "Autentisering mislyktes"
|
||||
@@ -396,6 +488,7 @@ msgstr "Kunne ikke oppdatere alarm"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Filter..."
|
||||
msgstr "Filter..."
|
||||
|
||||
@@ -448,16 +541,6 @@ msgstr "Ugyldig e-postadresse."
|
||||
msgid "Kernel"
|
||||
msgstr "Kjerne"
|
||||
|
||||
#. Load average 15 minutes
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "L15"
|
||||
msgstr ""
|
||||
|
||||
#. Load average 5 minutes
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "L5"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "Språk"
|
||||
@@ -471,14 +554,27 @@ msgstr "Layout"
|
||||
msgid "Light"
|
||||
msgstr "Lyst"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Load Average"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 15m"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 1m"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 5m"
|
||||
msgstr ""
|
||||
|
||||
#. Short label for load average
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Load Avg"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Log Out"
|
||||
msgstr "Logg Ut"
|
||||
@@ -514,7 +610,7 @@ msgstr "Instruks for Manuell Installasjon"
|
||||
msgid "Max 1 min"
|
||||
msgstr "Maks 1 min"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Memory"
|
||||
msgstr "Minne"
|
||||
|
||||
@@ -527,11 +623,12 @@ msgstr "Minnebruk"
|
||||
msgid "Memory usage of docker containers"
|
||||
msgstr "Minnebruk av docker-konteinere"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Name"
|
||||
msgstr "Navn"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Net"
|
||||
msgstr "Nett"
|
||||
|
||||
@@ -543,10 +640,19 @@ msgstr "Nettverkstrafikk av docker-konteinere"
|
||||
msgid "Network traffic of public interfaces"
|
||||
msgstr "Nettverkstrafikk av eksterne nettverksgrensesnitt"
|
||||
|
||||
#. Context: Bytes or bits
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Network unit"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "No results found."
|
||||
msgstr "Ingen resultater funnet."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "No results."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "No systems found."
|
||||
@@ -566,7 +672,7 @@ msgstr "OAuth 2 / OIDC-støtte"
|
||||
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
|
||||
msgstr "Ved hver omstart vil systemer i databasen bli oppdatert til å matche systemene definert i fila."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Open menu"
|
||||
@@ -584,6 +690,12 @@ msgstr "Overskriv eksisterende alarmer"
|
||||
msgid "Page"
|
||||
msgstr "Side"
|
||||
|
||||
#. placeholder {0}: table.getState().pagination.pageIndex + 1
|
||||
#. placeholder {1}: table.getPageCount()
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Page {0} of {1}"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Pages / Settings"
|
||||
msgstr "Sider / Innstillinger"
|
||||
@@ -605,13 +717,13 @@ msgstr "Passord må være mindre enn 72 byte."
|
||||
msgid "Password reset request received"
|
||||
msgstr "Mottatt forespørsel om å nullstille passord"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Pause"
|
||||
msgstr "Pause"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Paused"
|
||||
msgstr "Pauset"
|
||||
msgstr "Satt på Pause"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
@@ -665,13 +777,12 @@ msgid "Public Key"
|
||||
msgstr "Offentlig Nøkkel"
|
||||
|
||||
#. Disk read
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Read"
|
||||
msgstr "Lesing"
|
||||
|
||||
#. Network bytes received (download)
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Received"
|
||||
msgstr "Mottatt"
|
||||
|
||||
@@ -679,7 +790,13 @@ msgstr "Mottatt"
|
||||
msgid "Reset Password"
|
||||
msgstr "Nullstill Passord"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Resolved"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Gjenoppta"
|
||||
|
||||
@@ -687,6 +804,10 @@ msgstr "Gjenoppta"
|
||||
msgid "Rotate token"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Rows per page"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Save address using enter key or comma. Leave blank to disable email notifications."
|
||||
msgstr "Lagre adressen med Enter-tasten eller komma. La feltet være tomt for å deaktivere e-postvarsler."
|
||||
@@ -712,11 +833,14 @@ msgstr "Søk etter systemer eller innstillinger..."
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "Se <0>varslingsinnstillingene</0> for å konfigurere hvordan du vil motta varsler."
|
||||
|
||||
#. Network bytes sent (upload)
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "Sendt"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "Angi prosentvise terskler for målerfarger."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Sets the default time range for charts when a system is viewed."
|
||||
msgstr "Angir standard tidsperiode for diagrammer når et system vises."
|
||||
@@ -744,6 +868,11 @@ msgstr "SMTP-innstillinger"
|
||||
msgid "Sort By"
|
||||
msgstr "Sorter Etter"
|
||||
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "State"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Status"
|
||||
msgstr "Status"
|
||||
@@ -759,11 +888,16 @@ msgstr "Swap-bruk"
|
||||
#. System theme
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "System"
|
||||
msgstr "System"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "System load averages over time"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Systems"
|
||||
msgstr "Systemer"
|
||||
@@ -777,7 +911,7 @@ msgid "Table"
|
||||
msgstr "Tabell"
|
||||
|
||||
#. Temperature label in systems table
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Temp"
|
||||
msgstr "Temp"
|
||||
|
||||
@@ -786,6 +920,10 @@ msgstr "Temp"
|
||||
msgid "Temperature"
|
||||
msgstr "Temperatur"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Temperature unit"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Temperatures of system sensors"
|
||||
msgstr "Temperaturer på system-sensorer"
|
||||
@@ -802,10 +940,14 @@ msgstr "Test-varsling sendt"
|
||||
msgid "Then log into the backend and reset your user account password in the users table."
|
||||
msgstr "Logg deretter inn i backend og nullstill passordet på din konto i users-tabellen."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "This action cannot be undone. This will permanently delete all current records for {name} from the database."
|
||||
msgstr "Denne handlingen kan ikke omgjøres. Dette vil slette alle poster for {name} permanent fra databasen."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "This will permanently delete all selected records from the database."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Throughput of {extraFsName}"
|
||||
msgstr "Gjennomstrømning av {extraFsName}"
|
||||
@@ -846,6 +988,10 @@ msgstr ""
|
||||
msgid "Tokens and fingerprints are used to authenticate WebSocket connections to the hub."
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when 1 minute load average exceeds a threshold"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when 15 minute load average exceeds a threshold"
|
||||
msgstr ""
|
||||
@@ -878,12 +1024,17 @@ msgstr "Slår inn når statusen veksler mellom oppe og nede"
|
||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||
msgstr "Slår inn når forbruk av hvilken som helst disk overstiger en grenseverdi"
|
||||
|
||||
#. Temperature / network units
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Unit preferences"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Universal token"
|
||||
msgstr ""
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Up"
|
||||
msgstr "Oppe"
|
||||
@@ -898,7 +1049,8 @@ msgstr "Oppetid"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Usage"
|
||||
msgstr "Forbruk"
|
||||
|
||||
@@ -908,7 +1060,6 @@ msgstr "Forbruk av rot-partisjon"
|
||||
|
||||
#: src/components/charts/swap-chart.tsx
|
||||
#: src/components/charts/mem-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
msgid "Used"
|
||||
msgstr "Brukt"
|
||||
|
||||
@@ -917,10 +1068,18 @@ msgstr "Brukt"
|
||||
msgid "Users"
|
||||
msgstr "Brukere"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Value"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "View"
|
||||
msgstr "Visning"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "View your 200 most recent alerts."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Visible Fields"
|
||||
msgstr "Synlige Felter"
|
||||
@@ -933,6 +1092,14 @@ msgstr "Venter på nok registreringer til å vise"
|
||||
msgid "Want to help improve our translations? Check <0>Crowdin</0> for details."
|
||||
msgstr "Vil du hjelpe oss med å gjøre oversettelsene enda bedre? Ta en titt på <0>Crowdin</0> for mer informasjon."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Warning (%)"
|
||||
msgstr "Advarsel (%)"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Warning thresholds"
|
||||
msgstr "Advarselsterskler"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Webhook / Push notifications"
|
||||
msgstr "Webhook / Push-varslinger"
|
||||
@@ -948,8 +1115,8 @@ msgid "Windows command"
|
||||
msgstr "Windows-kommando"
|
||||
|
||||
#. Disk write
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Write"
|
||||
msgstr "Skriving"
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: pl\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-03-06 07:27\n"
|
||||
"PO-Revision-Date: 2025-07-25 22:44\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"
|
||||
@@ -23,6 +23,12 @@ msgstr ""
|
||||
msgid "{0, plural, one {# day} other {# days}}"
|
||||
msgstr "{0, plural, one {# dzień} few {# dni} many {# dni} other {# dni}}"
|
||||
|
||||
#. placeholder {0}: table.getFilteredSelectedRowModel().rows.length
|
||||
#. placeholder {1}: table.getFilteredRowModel().rows.length
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "{0} of {1} row(s) selected."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "{hours, plural, one {# hour} other {# hours}}"
|
||||
msgstr "{hours, plural, one {godzinę} few {# godziny} many {# godzin} other {# godziny}}"
|
||||
@@ -31,6 +37,11 @@ msgstr "{hours, plural, one {godzinę} few {# godziny} many {# godzin} other {#
|
||||
msgid "1 hour"
|
||||
msgstr "1 godzina"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "1 min"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 week"
|
||||
msgstr "1 tydzień"
|
||||
@@ -39,6 +50,11 @@ msgstr "1 tydzień"
|
||||
msgid "12 hours"
|
||||
msgstr "12 godzin"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "15 min"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "24 hours"
|
||||
msgstr "24 godziny"
|
||||
@@ -47,12 +63,22 @@ msgstr "24 godziny"
|
||||
msgid "30 days"
|
||||
msgstr "30 dni"
|
||||
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "5 min"
|
||||
msgstr ""
|
||||
|
||||
#. Table column
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Actions"
|
||||
msgstr "Akcje"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Active"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/home.tsx
|
||||
msgid "Active Alerts"
|
||||
msgstr "Aktywne alerty"
|
||||
@@ -82,10 +108,16 @@ msgstr "Dostosuj opcje wyświetlania wykresów."
|
||||
msgid "Admin"
|
||||
msgstr "Admin"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Agent"
|
||||
msgstr "Agent"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Alert History"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/alerts/alert-button.tsx
|
||||
#: src/components/alerts/alert-button.tsx
|
||||
msgid "Alerts"
|
||||
@@ -96,10 +128,14 @@ msgstr "Alerty"
|
||||
msgid "All Systems"
|
||||
msgstr "Wszystkie systemy"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Are you sure you want to delete {name}?"
|
||||
msgstr "Czy na pewno chcesz usunąć {name}?"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Are you sure?"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Automatic copy requires a secure context."
|
||||
msgstr "Automatyczne kopiowanie wymaga bezpiecznego kontekstu."
|
||||
@@ -152,11 +188,22 @@ msgstr "Beszel używa <0>Shoutrrr</0> do integracji z popularnych serwisami powi
|
||||
msgid "Binary"
|
||||
msgstr "Plik binarny"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bits (Kbps, Mbps, Gbps)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bytes (KB/s, MB/s, GB/s)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/charts/mem-chart.tsx
|
||||
msgid "Cache / Buffers"
|
||||
msgstr "Pamięć podręczna / Bufory"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Cancel"
|
||||
msgstr "Anuluj"
|
||||
|
||||
@@ -164,6 +211,14 @@ msgstr "Anuluj"
|
||||
msgid "Caution - potential data loss"
|
||||
msgstr "Uwaga- potencjalna utrata danych."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Celsius (°C)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change display units for metrics."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change general application options."
|
||||
msgstr "Zmiana ogólnych ustawień aplikacji."
|
||||
@@ -202,7 +257,12 @@ msgstr "Skonfiguruj sposób otrzymywania powiadomień."
|
||||
msgid "Confirm password"
|
||||
msgstr "Potwierdź hasło"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/routes/home.tsx
|
||||
msgid "Connection is down"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Continue"
|
||||
msgstr "Kontynuuj"
|
||||
|
||||
@@ -227,7 +287,7 @@ msgctxt "Environment variables"
|
||||
msgid "Copy env"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Copy host"
|
||||
msgstr "Kopiuj host"
|
||||
|
||||
@@ -236,6 +296,10 @@ msgstr "Kopiuj host"
|
||||
msgid "Copy Linux command"
|
||||
msgstr "Kopiuj polecenie Linux"
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Copy name"
|
||||
msgstr "Kopiuj nazwę"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
msgstr "Kopiuj tekst"
|
||||
@@ -252,13 +316,13 @@ msgstr ""
|
||||
msgid "Copy YAML"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "CPU"
|
||||
msgstr "Procesor"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "CPU Usage"
|
||||
msgstr "Użycie procesora"
|
||||
|
||||
@@ -266,6 +330,15 @@ msgstr "Użycie procesora"
|
||||
msgid "Create account"
|
||||
msgstr "Utwórz konto"
|
||||
|
||||
#. Context: date created
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Created"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Critical (%)"
|
||||
msgstr "Krytyczny (%)"
|
||||
|
||||
#. Dark theme
|
||||
#: src/components/mode-toggle.tsx
|
||||
msgid "Dark"
|
||||
@@ -280,7 +353,8 @@ msgstr "Panel kontrolny"
|
||||
msgid "Default time period"
|
||||
msgstr "Domyślny przedział czasu"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Delete"
|
||||
msgstr "Usuń"
|
||||
|
||||
@@ -288,7 +362,7 @@ msgstr "Usuń"
|
||||
msgid "Delete fingerprint"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Disk"
|
||||
msgstr "Dysk"
|
||||
|
||||
@@ -296,6 +370,10 @@ msgstr "Dysk"
|
||||
msgid "Disk I/O"
|
||||
msgstr "Dysk I/O"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Disk unit"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/disk-chart.tsx
|
||||
@@ -324,15 +402,20 @@ msgstr "Dokumentacja"
|
||||
|
||||
#. Context: System is down
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Down"
|
||||
msgstr "Nie działa"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Duration"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Edit"
|
||||
msgstr ""
|
||||
msgstr "Edytuj"
|
||||
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
#: src/components/login/auth-form.tsx
|
||||
@@ -354,6 +437,7 @@ msgstr "Wprowadź adres e-mail..."
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Error"
|
||||
msgstr "Błąd"
|
||||
@@ -369,6 +453,10 @@ msgstr "Przekracza {0}{1} w ciągu ostatnich {2, plural, one {# minuty} other {#
|
||||
msgid "Existing systems not defined in <0>config.yml</0> will be deleted. Please make regular backups."
|
||||
msgstr "Istniejące systemy, które nie są zdefiniowane w <0>config.yml</0>, zostaną usunięte. Proszę regularnie tworzyć kopie zapasowe."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
msgid "Export configuration"
|
||||
msgstr "Eksportuj konfigurację"
|
||||
@@ -377,6 +465,10 @@ msgstr "Eksportuj konfigurację"
|
||||
msgid "Export your current systems configuration."
|
||||
msgstr "Eksportuj aktualną konfigurację systemów."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Fahrenheit (°F)"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Failed to authenticate"
|
||||
msgstr "Błąd autoryzacji"
|
||||
@@ -396,6 +488,7 @@ msgstr "Nie udało się zaktualizować powiadomienia"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Filter..."
|
||||
msgstr "Filtruj..."
|
||||
|
||||
@@ -448,16 +541,6 @@ msgstr "Nieprawidłowy adres e-mail."
|
||||
msgid "Kernel"
|
||||
msgstr "Jądro"
|
||||
|
||||
#. Load average 15 minutes
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "L15"
|
||||
msgstr ""
|
||||
|
||||
#. Load average 5 minutes
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "L5"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Language"
|
||||
msgstr "Język"
|
||||
@@ -471,14 +554,27 @@ msgstr "Układ"
|
||||
msgid "Light"
|
||||
msgstr "Jasny"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Load Average"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 15m"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 1m"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 5m"
|
||||
msgstr ""
|
||||
|
||||
#. Short label for load average
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Load Avg"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Log Out"
|
||||
msgstr "Wyloguj"
|
||||
@@ -507,14 +603,14 @@ msgstr "Zarządzaj preferencjami wyświetlania i powiadomień."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Manual setup instructions"
|
||||
msgstr ""
|
||||
msgstr "Instrukcja ręcznej konfiguracji"
|
||||
|
||||
#. Chart select field. Please try to keep this short.
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Max 1 min"
|
||||
msgstr "Maks. 1 min"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Memory"
|
||||
msgstr "Pamięć"
|
||||
|
||||
@@ -527,11 +623,12 @@ msgstr "Wykorzystanie pamięci"
|
||||
msgid "Memory usage of docker containers"
|
||||
msgstr "Użycie pamięci przez kontenery Docker."
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Name"
|
||||
msgstr "Nazwa"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Net"
|
||||
msgstr "Sieć"
|
||||
|
||||
@@ -543,10 +640,19 @@ msgstr "Ruch sieciowy kontenerów Docker."
|
||||
msgid "Network traffic of public interfaces"
|
||||
msgstr "Ruch sieciowy interfejsów publicznych"
|
||||
|
||||
#. Context: Bytes or bits
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Network unit"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "No results found."
|
||||
msgstr "Brak wyników."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "No results."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "No systems found."
|
||||
@@ -566,7 +672,7 @@ msgstr "Wsparcie OAuth 2 / OIDC"
|
||||
msgid "On each restart, systems in the database will be updated to match the systems defined in the file."
|
||||
msgstr "Przy każdym ponownym uruchomieniu systemy w bazie danych będą aktualizowane, aby odpowiadały systemom zdefiniowanym w pliku."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Open menu"
|
||||
@@ -584,6 +690,12 @@ msgstr "Nadpisz istniejące alerty"
|
||||
msgid "Page"
|
||||
msgstr "Strona"
|
||||
|
||||
#. placeholder {0}: table.getState().pagination.pageIndex + 1
|
||||
#. placeholder {1}: table.getPageCount()
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Page {0} of {1}"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Pages / Settings"
|
||||
msgstr "Strony / Ustawienia"
|
||||
@@ -599,17 +711,17 @@ msgstr "Hasło musi mieć co najmniej 8 znaków."
|
||||
|
||||
#: src/components/login/auth-form.tsx
|
||||
msgid "Password must be less than 72 bytes."
|
||||
msgstr ""
|
||||
msgstr "Hasło musi być mniejsze niż 72 bajty."
|
||||
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
msgid "Password reset request received"
|
||||
msgstr "Otrzymane żądanie resetowania hasła"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Pause"
|
||||
msgstr "Pauza"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Paused"
|
||||
msgstr "Wstrzymane"
|
||||
|
||||
@@ -665,13 +777,12 @@ msgid "Public Key"
|
||||
msgstr "Klucz publiczny"
|
||||
|
||||
#. Disk read
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Read"
|
||||
msgstr "Czytaj"
|
||||
|
||||
#. Network bytes received (download)
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Received"
|
||||
msgstr "Otrzymane"
|
||||
|
||||
@@ -679,7 +790,13 @@ msgstr "Otrzymane"
|
||||
msgid "Reset Password"
|
||||
msgstr "Resetuj hasło"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Resolved"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Wznów"
|
||||
|
||||
@@ -687,6 +804,10 @@ msgstr "Wznów"
|
||||
msgid "Rotate token"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Rows per page"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Save address using enter key or comma. Leave blank to disable email notifications."
|
||||
msgstr "Zapisz adres, używając klawisza enter lub przecinka. Pozostaw puste, aby wyłączyć powiadomienia e-mail."
|
||||
@@ -698,7 +819,7 @@ msgstr "Zapisz ustawienia"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Save system"
|
||||
msgstr ""
|
||||
msgstr "Zapisz system"
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Search"
|
||||
@@ -712,11 +833,14 @@ msgstr "Szukaj systemów lub ustawień..."
|
||||
msgid "See <0>notification settings</0> to configure how you receive alerts."
|
||||
msgstr "Zobacz <0>ustawienia powiadomień</0>, aby skonfigurować sposób, w jaki otrzymujesz powiadomienia."
|
||||
|
||||
#. Network bytes sent (upload)
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Sent"
|
||||
msgstr "Wysłane"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Set percentage thresholds for meter colors."
|
||||
msgstr "Ustaw progi procentowe dla kolorów mierników."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Sets the default time range for charts when a system is viewed."
|
||||
msgstr "Ustawia domyślny zakres czasowy dla wykresów, gdy system jest wyświetlony."
|
||||
@@ -744,6 +868,11 @@ msgstr "Ustawienia SMTP"
|
||||
msgid "Sort By"
|
||||
msgstr "Sortuj według"
|
||||
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "State"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Status"
|
||||
msgstr "Status"
|
||||
@@ -759,11 +888,16 @@ msgstr "Użycie pamięci wymiany"
|
||||
#. System theme
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/mode-toggle.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "System"
|
||||
msgstr "System"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "System load averages over time"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Systems"
|
||||
msgstr "Systemy"
|
||||
@@ -777,15 +911,19 @@ msgid "Table"
|
||||
msgstr "Tabela"
|
||||
|
||||
#. Temperature label in systems table
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "Temp"
|
||||
msgstr ""
|
||||
msgstr "Temperatura"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Temperature"
|
||||
msgstr "Temperatura"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Temperature unit"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Temperatures of system sensors"
|
||||
msgstr "Temperatury czujników systemowych."
|
||||
@@ -802,10 +940,14 @@ msgstr "Testowe powiadomienie wysłane."
|
||||
msgid "Then log into the backend and reset your user account password in the users table."
|
||||
msgstr "Następnie zaloguj się do panelu administracyjnego i zresetuj hasło do konta użytkownika w tabeli użytkowników."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
msgid "This action cannot be undone. This will permanently delete all current records for {name} from the database."
|
||||
msgstr "Tej akcji nie można cofnąć. Spowoduje to trwałe usunięcie wszystkich bieżących rekordów dla {name} z bazy danych."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "This will permanently delete all selected records from the database."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Throughput of {extraFsName}"
|
||||
msgstr "Przepustowość {extraFsName}"
|
||||
@@ -846,6 +988,10 @@ msgstr ""
|
||||
msgid "Tokens and fingerprints are used to authenticate WebSocket connections to the hub."
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when 1 minute load average exceeds a threshold"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when 15 minute load average exceeds a threshold"
|
||||
msgstr ""
|
||||
@@ -878,15 +1024,20 @@ msgstr "Wyzwalane, gdy status przełącza się między stanem aktywnym a nieakty
|
||||
msgid "Triggers when usage of any disk exceeds a threshold"
|
||||
msgstr "Wyzwalane, gdy wykorzystanie któregokolwiek dysku przekroczy ustalony próg"
|
||||
|
||||
#. Temperature / network units
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Unit preferences"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Universal token"
|
||||
msgstr ""
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table-columns.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Up"
|
||||
msgstr ""
|
||||
msgstr "Działa"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Updated in real time. Click on a system to view information."
|
||||
@@ -898,7 +1049,8 @@ msgstr "Czas pracy"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Usage"
|
||||
msgstr "Wykorzystanie"
|
||||
|
||||
@@ -908,7 +1060,6 @@ msgstr "Użycie partycji głównej"
|
||||
|
||||
#: src/components/charts/swap-chart.tsx
|
||||
#: src/components/charts/mem-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
msgid "Used"
|
||||
msgstr "Używane"
|
||||
|
||||
@@ -917,10 +1068,18 @@ msgstr "Używane"
|
||||
msgid "Users"
|
||||
msgstr "Użytkownicy"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Value"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "View"
|
||||
msgstr "Widok"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "View your 200 most recent alerts."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Visible Fields"
|
||||
msgstr "Widoczne kolumny"
|
||||
@@ -933,6 +1092,14 @@ msgstr "Oczekiwanie na wystarczającą liczbę rekordów do wyświetlenia"
|
||||
msgid "Want to help improve our translations? Check <0>Crowdin</0> for details."
|
||||
msgstr "Chcesz pomóc nam uczynić nasze tłumaczenia jeszcze lepszymi? Sprawdź <0>Crowdin</0> po więcej szczegółów."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Warning (%)"
|
||||
msgstr "Ostrzeżenie (%)"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Warning thresholds"
|
||||
msgstr "Progi ostrzegawcze"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Webhook / Push notifications"
|
||||
msgstr "Webhook / Powiadomienia push"
|
||||
@@ -948,8 +1115,8 @@ msgid "Windows command"
|
||||
msgstr "Polecenie Windows"
|
||||
|
||||
#. Disk write
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/charts/area-chart.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Write"
|
||||
msgstr "Napisz"
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user