mirror of
https://github.com/henrygd/beszel.git
synced 2026-03-22 13:36:16 +01:00
Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
2
.github/workflows/docker-images.yml
vendored
2
.github/workflows/docker-images.yml
vendored
@@ -3,7 +3,7 @@ name: Make docker images
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
- "xv*"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
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 }}
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -17,3 +17,5 @@ beszel/build
|
||||
beszel/site/src/locales/**/*.ts
|
||||
*.bak
|
||||
__debug_*
|
||||
beszel/internal/agent/lhm/obj
|
||||
beszel/internal/agent/lhm/bin
|
||||
|
||||
@@ -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,8 +202,9 @@ winget:
|
||||
owner: henrygd
|
||||
name: beszel-winget
|
||||
branch: henrygd.beszel-agent-{{ .Version }}
|
||||
token: "{{ .Env.WINGET_TOKEN }}"
|
||||
pull_request:
|
||||
enabled: false
|
||||
enabled: true
|
||||
draft: false
|
||||
base:
|
||||
owner: microsoft
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -67,6 +84,15 @@ dev-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
|
||||
|
||||
@@ -111,7 +111,7 @@ func main() {
|
||||
serverConfig.Addr = addr
|
||||
serverConfig.Network = agent.GetNetwork(addr)
|
||||
|
||||
agent, err := agent.NewAgent("")
|
||||
agent, err := agent.NewAgent()
|
||||
if err != nil {
|
||||
log.Fatal("Failed to create agent: ", err)
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -61,7 +61,7 @@ func TestNewWebSocketClient(t *testing.T) {
|
||||
hubURL: "http://localhost:8080",
|
||||
token: "",
|
||||
expectError: true,
|
||||
errorMsg: "TOKEN environment variable not set",
|
||||
errorMsg: "must set TOKEN or TOKEN_FILE",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -389,3 +389,150 @@ func TestWebSocketClient_ConnectRateLimit(t *testing.T) {
|
||||
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 {
|
||||
|
||||
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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -255,9 +256,11 @@ 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()
|
||||
|
||||
@@ -47,9 +47,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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -31,11 +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 {
|
||||
@@ -79,25 +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"`
|
||||
BandwidthBytes uint64 `json:"bb" cbor:"18,keyasint"`
|
||||
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
|
||||
|
||||
@@ -203,9 +203,9 @@ func (rm *RecordManager) AverageSystemStats(db dbx.Builder, records RecordIds) *
|
||||
sum.DiskWritePs += stats.DiskWritePs
|
||||
sum.NetworkSent += stats.NetworkSent
|
||||
sum.NetworkRecv += stats.NetworkRecv
|
||||
sum.LoadAvg1 += stats.LoadAvg1
|
||||
sum.LoadAvg5 += stats.LoadAvg5
|
||||
sum.LoadAvg15 += stats.LoadAvg15
|
||||
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
|
||||
@@ -285,9 +285,9 @@ 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.LoadAvg1 = twoDecimals(sum.LoadAvg1 / count)
|
||||
sum.LoadAvg5 = twoDecimals(sum.LoadAvg5 / count)
|
||||
sum.LoadAvg15 = twoDecimals(sum.LoadAvg15 / 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
|
||||
|
||||
4
beszel/site/package-lock.json
generated
4
beszel/site/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "beszel",
|
||||
"version": "0.12.0",
|
||||
"version": "0.12.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "beszel",
|
||||
"version": "0.12.0",
|
||||
"version": "0.12.2",
|
||||
"dependencies": {
|
||||
"@henrygd/queue": "^1.0.7",
|
||||
"@henrygd/semaphore": "^0.0.2",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "beszel",
|
||||
"private": true,
|
||||
"version": "0.12.0",
|
||||
"version": "0.12.2",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -87,5 +87,5 @@ export default function AreaChartDefault({
|
||||
</ChartContainer>
|
||||
</div>
|
||||
)
|
||||
}, [chartData.systemStats.length, yAxisWidth, maxToggled])
|
||||
}, [chartData.systemStats.at(-1), yAxisWidth, maxToggled])
|
||||
}
|
||||
|
||||
@@ -9,31 +9,30 @@ import {
|
||||
xAxis,
|
||||
} from "@/components/ui/chart"
|
||||
import { useYAxisWidth, cn, formatShortDate, toFixedFloat, decimalString, chartMargin } from "@/lib/utils"
|
||||
import { ChartData } from "@/types"
|
||||
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
|
||||
}
|
||||
|
||||
const keys = {
|
||||
l1: {
|
||||
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" }),
|
||||
},
|
||||
l5: {
|
||||
{
|
||||
legacy: "l5",
|
||||
color: "hsl(217, 91%, 60%)", // Blue
|
||||
label: t({ message: `5 min`, comment: "Load average" }),
|
||||
},
|
||||
l15: {
|
||||
{
|
||||
legacy: "l15",
|
||||
color: "hsl(25, 95%, 53%)", // Orange
|
||||
label: t({ message: `15 min`, comment: "Load average" }),
|
||||
},
|
||||
}
|
||||
]
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -69,16 +68,22 @@ export default memo(function LoadAverageChart({ chartData }: { chartData: ChartD
|
||||
/>
|
||||
}
|
||||
/>
|
||||
{Object.entries(keys).map(([key, value]: [string, { color: string; label: string }]) => {
|
||||
{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={key}
|
||||
dataKey={`stats.${key}`}
|
||||
name={value.label}
|
||||
key={i}
|
||||
dataKey={dataKey}
|
||||
name={label}
|
||||
type="monotoneX"
|
||||
dot={false}
|
||||
strokeWidth={1.5}
|
||||
stroke={value.color}
|
||||
stroke={color}
|
||||
isAnimationActive={false}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -74,11 +74,11 @@ export default function AlertsHistoryDataTable() {
|
||||
}
|
||||
// Initial load
|
||||
pb.collection<AlertsHistoryRecord>("alerts_history")
|
||||
.getFullList({
|
||||
.getList(0, 200, {
|
||||
...pbOptions,
|
||||
sort: "-created",
|
||||
})
|
||||
.then((records) => setData(records))
|
||||
.then(({ items }) => setData(items))
|
||||
|
||||
// Subscribe to changes
|
||||
;(async () => {
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -642,7 +644,7 @@ export default function SystemDetail({ name }: { name: string }) {
|
||||
)}
|
||||
|
||||
{/* Load Average chart */}
|
||||
{system.info.l1 !== undefined && (
|
||||
{chartData.agentVersion?.minor >= 12 && (
|
||||
<ChartCard
|
||||
empty={dataEmpty}
|
||||
grid={grid}
|
||||
|
||||
@@ -73,6 +73,7 @@ import {
|
||||
formatTemperature,
|
||||
decimalString,
|
||||
formatBytes,
|
||||
parseSemVer,
|
||||
} from "@/lib/utils"
|
||||
import AlertsButton from "../alerts/alert-button"
|
||||
import { $router, Link, navigate } from "../router"
|
||||
@@ -88,10 +89,10 @@ import { Dialog } from "../ui/dialog"
|
||||
type ViewMode = "table" | "grid"
|
||||
|
||||
function CellFormatter(info: CellContext<SystemRecord, unknown>) {
|
||||
const val = (info.getValue() as number) || 0
|
||||
const val = Number(info.getValue()) || 0
|
||||
return (
|
||||
<div className="flex gap-2 items-center tabular-nums tracking-tight">
|
||||
<span className="min-w-8">{decimalString(val, 1)}%</span>
|
||||
<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(
|
||||
@@ -173,23 +174,17 @@ export default function SystemsTable() {
|
||||
invertSorting: false,
|
||||
Icon: ServerIcon,
|
||||
cell: (info) => (
|
||||
<span className="flex gap-0.5 items-center text-base md:ps-1 md:pe-5">
|
||||
<span className="flex gap-2 items-center md:ps-1 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)}
|
||||
>
|
||||
<span className="font-medium text-sm">
|
||||
{info.getValue() as string}
|
||||
<CopyIcon className="size-2.5" />
|
||||
</Button>
|
||||
</span>
|
||||
</span>
|
||||
),
|
||||
header: sortableHeader,
|
||||
},
|
||||
{
|
||||
accessorFn: ({ info }) => decimalString(info.cpu, info.cpu >= 10 ? 1 : 2),
|
||||
accessorFn: ({ info }) => info.cpu,
|
||||
id: "cpu",
|
||||
name: () => t`CPU`,
|
||||
cell: CellFormatter,
|
||||
@@ -198,7 +193,7 @@ export default function SystemsTable() {
|
||||
},
|
||||
{
|
||||
// accessorKey: "info.mp",
|
||||
accessorFn: (originalRow) => originalRow.info.mp,
|
||||
accessorFn: ({ info }) => info.mp,
|
||||
id: "memory",
|
||||
name: () => t`Memory`,
|
||||
cell: CellFormatter,
|
||||
@@ -206,7 +201,7 @@ export default function SystemsTable() {
|
||||
header: sortableHeader,
|
||||
},
|
||||
{
|
||||
accessorFn: (originalRow) => originalRow.info.dp,
|
||||
accessorFn: ({ info }) => info.dp,
|
||||
id: "disk",
|
||||
name: () => t`Disk`,
|
||||
cell: CellFormatter,
|
||||
@@ -214,7 +209,7 @@ export default function SystemsTable() {
|
||||
header: sortableHeader,
|
||||
},
|
||||
{
|
||||
accessorFn: (originalRow) => originalRow.info.g,
|
||||
accessorFn: ({ info }) => info.g,
|
||||
id: "gpu",
|
||||
name: () => "GPU",
|
||||
cell: CellFormatter,
|
||||
@@ -224,8 +219,12 @@ export default function SystemsTable() {
|
||||
{
|
||||
id: "loadAverage",
|
||||
accessorFn: ({ info }) => {
|
||||
const { l1 = 0, l5 = 0, l15 = 0 } = info
|
||||
return l1 + l5 + l15
|
||||
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,
|
||||
@@ -233,16 +232,22 @@ export default function SystemsTable() {
|
||||
header: sortableHeader,
|
||||
cell(info: CellContext<SystemRecord, unknown>) {
|
||||
const { info: sysInfo, status } = info.row.original
|
||||
if (sysInfo.l1 === undefined) {
|
||||
// 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 { l1 = 0, l5 = 0, l15 = 0, t: cpuThreads = 1 } = sysInfo
|
||||
const loadAverages = [l1, l5, l15]
|
||||
|
||||
function getDotColor() {
|
||||
const max = Math.max(...loadAverages)
|
||||
const normalized = max / cpuThreads
|
||||
const normalized = max / (sysInfo.t ?? 1)
|
||||
if (status !== "up") return "bg-primary/30"
|
||||
if (normalized < 0.7) return "bg-green-500"
|
||||
if (normalized < 1) return "bg-yellow-500"
|
||||
@@ -252,7 +257,7 @@ export default function SystemsTable() {
|
||||
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", getDotColor())} />
|
||||
{loadAverages.map((la, i) => (
|
||||
{loadAverages?.map((la, i) => (
|
||||
<span key={i}>{decimalString(la, la >= 10 ? 1 : 2)}</span>
|
||||
))}
|
||||
</div>
|
||||
@@ -281,7 +286,7 @@ export default function SystemsTable() {
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorFn: (originalRow) => originalRow.info.dt,
|
||||
accessorFn: ({ info }) => info.dt,
|
||||
id: "temp",
|
||||
name: () => t({ message: "Temp", comment: "Temperature label in systems table" }),
|
||||
size: 50,
|
||||
@@ -303,7 +308,7 @@ export default function SystemsTable() {
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorFn: (originalRow) => originalRow.info.v,
|
||||
accessorFn: ({ info }) => info.v,
|
||||
id: "agent",
|
||||
name: () => t`Agent`,
|
||||
// invertSorting: true,
|
||||
@@ -710,6 +715,10 @@ const ActionsButton = memo(({ system }: { system: SystemRecord }) => {
|
||||
</>
|
||||
)}
|
||||
</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>
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
ChartTimeData,
|
||||
ChartTimes,
|
||||
FingerprintRecord,
|
||||
SemVer,
|
||||
SystemRecord,
|
||||
UserSettings,
|
||||
} from "@/types"
|
||||
@@ -495,3 +496,14 @@ export function formatDuration(
|
||||
.filter(Boolean)
|
||||
.join(" ")
|
||||
}
|
||||
|
||||
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 }
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
@@ -97,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."
|
||||
@@ -155,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"
|
||||
@@ -178,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."
|
||||
@@ -250,7 +250,7 @@ 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
|
||||
@@ -274,13 +274,13 @@ 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"
|
||||
@@ -296,6 +296,10 @@ msgstr "نسخ المضيف"
|
||||
msgid "Copy Linux command"
|
||||
msgstr "نسخ أمر لينكس"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Copy name"
|
||||
msgstr "نسخ الاسم"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
msgstr "نسخ النص"
|
||||
@@ -378,15 +382,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"
|
||||
@@ -504,7 +508,7 @@ msgstr "عام"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "GPU Power Draw"
|
||||
msgstr "استهلاك طاقة GPU"
|
||||
msgstr "استهلاك طاقة وحدة معالجة الرسوميات"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Grid"
|
||||
@@ -600,7 +604,7 @@ 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
|
||||
msgid "Memory"
|
||||
@@ -613,7 +617,7 @@ 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
|
||||
@@ -626,7 +630,7 @@ 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"
|
||||
@@ -715,7 +719,7 @@ msgstr "إيقاف مؤقت"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Paused"
|
||||
msgstr "متوقف مؤقتًا"
|
||||
msgstr "متوقف مؤقتا"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
@@ -990,7 +994,7 @@ msgstr "يتم التفعيل عندما يتجاوز متوسط التحميل
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when any sensor exceeds a threshold"
|
||||
msgstr "يتم التفعيل عندما يتجاوز أي مستشعر عتبة معينة"
|
||||
msgstr "يتم التفعيل عندما <EFBFBD><EFBFBD>تجاوز أي مستشعر عتبة معينة"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when combined up/down exceeds a threshold"
|
||||
@@ -1092,7 +1096,7 @@ msgstr "عند التفعيل، يسمح هذا الرمز المميز للوك
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
msgid "Windows command"
|
||||
msgstr "أمر Windows"
|
||||
msgstr "أمر ويندوز"
|
||||
|
||||
#. Disk write
|
||||
#: src/components/routes/system.tsx
|
||||
|
||||
@@ -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"
|
||||
@@ -296,6 +296,10 @@ msgstr "Копирай хоста"
|
||||
msgid "Copy Linux command"
|
||||
msgstr "Копирай linux командата"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Copy name"
|
||||
msgstr "Копирай име"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
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-07-25 22:44\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Czech\n"
|
||||
"Plural-Forms: nplurals=4; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 3;\n"
|
||||
@@ -280,7 +280,7 @@ 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"
|
||||
@@ -296,6 +296,10 @@ msgstr "Kopírovat hostitele"
|
||||
msgid "Copy Linux command"
|
||||
msgstr "Kopírovat příkaz Linux"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Copy name"
|
||||
msgstr "Kopírovat název"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
msgstr "Kopírovat text"
|
||||
@@ -514,7 +518,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"
|
||||
@@ -1092,7 +1096,7 @@ msgstr ""
|
||||
#: 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/routes/system.tsx
|
||||
|
||||
@@ -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"
|
||||
@@ -296,6 +296,10 @@ msgstr "Kopier host"
|
||||
msgid "Copy Linux command"
|
||||
msgstr "Kopier Linux kommando"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Copy name"
|
||||
msgstr "Kopier navn"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
msgstr "Kopier tekst"
|
||||
@@ -398,7 +402,7 @@ msgstr "Dokumentation"
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Down"
|
||||
msgstr ""
|
||||
msgstr "Nede"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Duration"
|
||||
@@ -407,7 +411,7 @@ msgstr ""
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Edit"
|
||||
msgstr ""
|
||||
msgstr "Rediger"
|
||||
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
#: src/components/login/auth-form.tsx
|
||||
@@ -595,7 +599,7 @@ 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
|
||||
@@ -703,7 +707,7 @@ 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"
|
||||
@@ -811,7 +815,7 @@ msgstr "Gem indstillinger"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Save system"
|
||||
msgstr ""
|
||||
msgstr "Gem system"
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Search"
|
||||
@@ -901,7 +905,7 @@ msgstr "Tabel"
|
||||
#. Temperature label in systems table
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Temp"
|
||||
msgstr ""
|
||||
msgstr "Temperatur"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/routes/system.tsx
|
||||
@@ -1025,7 +1029,7 @@ msgstr ""
|
||||
#: src/components/systems-table/systems-table.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."
|
||||
|
||||
@@ -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"
|
||||
@@ -27,7 +27,7 @@ msgstr "{0, plural, one {# Tag} other {# Tage}}"
|
||||
#. placeholder {1}: table.getFilteredRowModel().rows.length
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "{0} of {1} row(s) selected."
|
||||
msgstr ""
|
||||
msgstr "{0} von {1} Zeile(n) ausgewählt."
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "{hours, plural, one {# hour} other {# hours}}"
|
||||
@@ -40,7 +40,7 @@ msgstr "1 Stunde"
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "1 min"
|
||||
msgstr ""
|
||||
msgstr "1 Min"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 week"
|
||||
@@ -53,7 +53,7 @@ msgstr "12 Stunden"
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "15 min"
|
||||
msgstr ""
|
||||
msgstr "15 Min"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "24 hours"
|
||||
@@ -66,7 +66,7 @@ msgstr "30 Tage"
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "5 min"
|
||||
msgstr ""
|
||||
msgstr "5 Min"
|
||||
|
||||
#. Table column
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
@@ -77,7 +77,7 @@ msgstr "Aktionen"
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Active"
|
||||
msgstr ""
|
||||
msgstr "Aktiv"
|
||||
|
||||
#: src/components/routes/home.tsx
|
||||
msgid "Active Alerts"
|
||||
@@ -116,7 +116,7 @@ msgstr "Agent"
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Alert History"
|
||||
msgstr ""
|
||||
msgstr "Alarm-Verlauf"
|
||||
|
||||
#: src/components/alerts/alert-button.tsx
|
||||
#: src/components/alerts/alert-button.tsx
|
||||
@@ -134,7 +134,7 @@ msgstr "Möchtest du {name} wirklich löschen?"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Are you sure?"
|
||||
msgstr ""
|
||||
msgstr "Bist du sicher?"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Automatic copy requires a secure context."
|
||||
@@ -191,12 +191,12 @@ msgstr "Binär"
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bits (Kbps, Mbps, Gbps)"
|
||||
msgstr ""
|
||||
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 ""
|
||||
msgstr "Bytes (KB/s, MB/s, GB/s)"
|
||||
|
||||
#: src/components/charts/mem-chart.tsx
|
||||
msgid "Cache / Buffers"
|
||||
@@ -213,11 +213,11 @@ msgstr "Vorsicht - potenzieller Datenverlust"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Celsius (°C)"
|
||||
msgstr ""
|
||||
msgstr "Celsius (°C)"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change display units for metrics."
|
||||
msgstr ""
|
||||
msgstr "Anzeigeeinheiten der Werte ändern."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change general application options."
|
||||
@@ -259,7 +259,7 @@ msgstr "Passwort bestätigen"
|
||||
|
||||
#: src/components/routes/home.tsx
|
||||
msgid "Connection is down"
|
||||
msgstr ""
|
||||
msgstr "Verbindung unterbrochen"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
@@ -296,6 +296,10 @@ msgstr "Host kopieren"
|
||||
msgid "Copy Linux command"
|
||||
msgstr "Linux-Befehl kopieren"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Copy name"
|
||||
msgstr "Name kopieren"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
msgstr "Text kopieren"
|
||||
@@ -329,7 +333,7 @@ msgstr "Konto erstellen"
|
||||
#. Context: date created
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Created"
|
||||
msgstr ""
|
||||
msgstr "Erstellt"
|
||||
|
||||
#. Dark theme
|
||||
#: src/components/mode-toggle.tsx
|
||||
@@ -364,7 +368,7 @@ msgstr "Festplatten-I/O"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Disk unit"
|
||||
msgstr ""
|
||||
msgstr "Festplatteneinheit"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/routes/system.tsx
|
||||
@@ -402,7 +406,7 @@ msgstr "Offline"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Duration"
|
||||
msgstr ""
|
||||
msgstr "Dauer"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
@@ -447,7 +451,7 @@ msgstr "Bestehende Systeme, die nicht in der <0>config.yml</0> definiert sind, w
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr ""
|
||||
msgstr "Exportieren"
|
||||
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
msgid "Export configuration"
|
||||
@@ -459,7 +463,7 @@ msgstr "Exportiere die aktuelle Systemkonfiguration."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Fahrenheit (°F)"
|
||||
msgstr ""
|
||||
msgstr "Fahrenheit (°F)"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Failed to authenticate"
|
||||
@@ -486,7 +490,7 @@ 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}}"
|
||||
@@ -548,24 +552,24 @@ msgstr "Hell"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Load Average"
|
||||
msgstr ""
|
||||
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 ""
|
||||
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.tsx
|
||||
msgid "Load Avg"
|
||||
msgstr ""
|
||||
msgstr "Durchschnittliche Last"
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Log Out"
|
||||
@@ -635,7 +639,7 @@ msgstr "Netzwerkverkehr der öffentlichen Schnittstellen"
|
||||
#. Context: Bytes or bits
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Network unit"
|
||||
msgstr ""
|
||||
msgstr "Netzwerkeinheit"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "No results found."
|
||||
@@ -643,7 +647,7 @@ msgstr "Keine Ergebnisse gefunden."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "No results."
|
||||
msgstr ""
|
||||
msgstr "Keine Ergebnisse."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
@@ -686,7 +690,7 @@ msgstr "Seite"
|
||||
#. placeholder {1}: table.getPageCount()
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Page {0} of {1}"
|
||||
msgstr ""
|
||||
msgstr "Seite {0} von {1}"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Pages / Settings"
|
||||
@@ -786,7 +790,7 @@ msgstr "Passwort zurücksetzen"
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Resolved"
|
||||
msgstr ""
|
||||
msgstr "Gelöst"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Resume"
|
||||
@@ -798,7 +802,7 @@ msgstr "Token rotieren"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Rows per page"
|
||||
msgstr ""
|
||||
msgstr "Zeilen pro Seite"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Save address using enter key or comma. Leave blank to disable email notifications."
|
||||
@@ -811,7 +815,7 @@ msgstr "Einstellungen speichern"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Save system"
|
||||
msgstr ""
|
||||
msgstr "System sichern"
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Search"
|
||||
@@ -859,7 +863,7 @@ msgstr "Sortieren nach"
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "State"
|
||||
msgstr ""
|
||||
msgstr "Status"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Status"
|
||||
@@ -884,7 +888,7 @@ msgstr "System"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "System load averages over time"
|
||||
msgstr ""
|
||||
msgstr "Systemlastdurchschnitt im Zeitverlauf"
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Systems"
|
||||
@@ -910,7 +914,7 @@ msgstr "Temperatur"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Temperature unit"
|
||||
msgstr ""
|
||||
msgstr "Temperatureinheit"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Temperatures of system sensors"
|
||||
@@ -934,7 +938,7 @@ msgstr "Diese Aktion kann nicht rückgängig gemacht werden. Dadurch werden alle
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "This will permanently delete all selected records from the database."
|
||||
msgstr ""
|
||||
msgstr "Dadurch werden alle ausgewählten Datensätze dauerhaft aus der Datenbank gelöscht."
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Throughput of {extraFsName}"
|
||||
@@ -978,15 +982,15 @@ msgstr "Tokens und Fingerabdrücke werden verwendet, um WebSocket-Verbindungen z
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when 1 minute load average exceeds a threshold"
|
||||
msgstr ""
|
||||
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"
|
||||
@@ -1015,7 +1019,7 @@ msgstr "Löst aus, wenn die Nutzung einer Festplatte einen Schwellenwert übersc
|
||||
#. Temperature / network units
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Unit preferences"
|
||||
msgstr ""
|
||||
msgstr "Einheiten"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Universal token"
|
||||
@@ -1025,7 +1029,7 @@ msgstr "Universeller Token"
|
||||
#: src/components/systems-table/systems-table.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."
|
||||
@@ -1058,7 +1062,7 @@ msgstr "Benutzer"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Value"
|
||||
msgstr ""
|
||||
msgstr "Wert"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "View"
|
||||
@@ -1066,7 +1070,7 @@ msgstr "Ansicht"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "View your 200 most recent alerts."
|
||||
msgstr ""
|
||||
msgstr "Sieh dir die neusten 200 Alarme an."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Visible Fields"
|
||||
|
||||
@@ -291,6 +291,10 @@ msgstr "Copy host"
|
||||
msgid "Copy Linux command"
|
||||
msgstr "Copy Linux command"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Copy name"
|
||||
msgstr "Copy name"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
msgstr "Copy text"
|
||||
|
||||
@@ -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"
|
||||
@@ -40,7 +40,7 @@ msgstr "1 hora"
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "1 min"
|
||||
msgstr "1 min"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 week"
|
||||
@@ -53,7 +53,7 @@ msgstr "12 horas"
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "15 min"
|
||||
msgstr "15 min"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "24 hours"
|
||||
@@ -66,7 +66,7 @@ msgstr "30 días"
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "5 min"
|
||||
msgstr "5 min"
|
||||
msgstr ""
|
||||
|
||||
#. Table column
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
@@ -191,12 +191,12 @@ msgstr "Binario"
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bits (Kbps, Mbps, Gbps)"
|
||||
msgstr "Bits (Kbps, Mbps, Gbps)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bytes (KB/s, MB/s, GB/s)"
|
||||
msgstr "Bytes (KB/s, MB/s, GB/s)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/charts/mem-chart.tsx
|
||||
msgid "Cache / Buffers"
|
||||
@@ -213,7 +213,7 @@ msgstr "Precaución - posible pérdida de datos"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Celsius (°C)"
|
||||
msgstr "Celsius (°C)"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change display units for metrics."
|
||||
@@ -296,6 +296,10 @@ msgstr "Copiar host"
|
||||
msgid "Copy Linux command"
|
||||
msgstr "Copiar comando de Linux"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Copy name"
|
||||
msgstr "Copiar nombre"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
msgstr "Copiar texto"
|
||||
@@ -459,7 +463,7 @@ msgstr "Exporte la configuración actual de sus sistemas."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Fahrenheit (°F)"
|
||||
msgstr "Fahrenheit (°F)"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Failed to authenticate"
|
||||
@@ -960,7 +964,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
|
||||
|
||||
@@ -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"
|
||||
@@ -296,6 +296,10 @@ msgstr "کپی میزبان"
|
||||
msgid "Copy Linux command"
|
||||
msgstr "کپی دستور لینوکس"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Copy name"
|
||||
msgstr "کپی نام"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
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"
|
||||
@@ -85,7 +85,7 @@ 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"
|
||||
@@ -97,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."
|
||||
@@ -296,6 +296,10 @@ msgstr "Copier l'hôte"
|
||||
msgid "Copy Linux command"
|
||||
msgstr "Copier la commande Linux"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Copy name"
|
||||
msgstr "Copier le nom"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
msgstr "Copier le texte"
|
||||
@@ -711,7 +715,7 @@ msgstr "Demande de réinitialisation du mot de passe reçue"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Pause"
|
||||
msgstr "En pause"
|
||||
msgstr "Pause"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Paused"
|
||||
@@ -960,7 +964,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
|
||||
|
||||
@@ -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"
|
||||
@@ -296,6 +296,10 @@ msgstr "Kopiraj hosta"
|
||||
msgid "Copy Linux command"
|
||||
msgstr "Kopiraj Linux komandu"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Copy name"
|
||||
msgstr "Kopiraj naziv"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
msgstr "Kopiraj tekst"
|
||||
|
||||
@@ -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"
|
||||
@@ -296,6 +296,10 @@ msgstr "Hoszt másolása"
|
||||
msgid "Copy Linux command"
|
||||
msgstr "Linux parancs másolása"
|
||||
|
||||
#: src/components/systems-table/systems-table.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"
|
||||
|
||||
@@ -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"
|
||||
@@ -296,6 +296,10 @@ msgstr "Afrita host"
|
||||
msgid "Copy Linux command"
|
||||
msgstr "Afrita Linux aðgerð"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Copy name"
|
||||
msgstr "Afrita nafn"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
msgstr "Afrita texta"
|
||||
|
||||
@@ -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"
|
||||
@@ -296,6 +296,10 @@ msgstr "Copia host"
|
||||
msgid "Copy Linux command"
|
||||
msgstr "Copia comando Linux"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Copy name"
|
||||
msgstr "Copia nome"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
msgstr "Copia testo"
|
||||
@@ -552,7 +556,7 @@ msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 15m"
|
||||
msgstr ""
|
||||
msgstr "Caricamento medio 15m"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 1m"
|
||||
@@ -560,7 +564,7 @@ msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 5m"
|
||||
msgstr ""
|
||||
msgstr "Caricamento medio 5m"
|
||||
|
||||
#. Short label for load average
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
|
||||
@@ -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"
|
||||
@@ -296,6 +296,10 @@ msgstr "ホストをコピー"
|
||||
msgid "Copy Linux command"
|
||||
msgstr "Linuxコマンドをコピー"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Copy name"
|
||||
msgstr "名前をコピー"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
msgstr "テキストをコピー"
|
||||
@@ -1078,7 +1082,7 @@ 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/notifications.tsx
|
||||
msgid "Webhook / Push notifications"
|
||||
|
||||
@@ -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"
|
||||
@@ -146,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
|
||||
@@ -274,13 +274,13 @@ 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"
|
||||
@@ -296,6 +296,10 @@ msgstr "호스트 복사"
|
||||
msgid "Copy Linux command"
|
||||
msgstr "리눅스 명령어 복사"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Copy name"
|
||||
msgstr "이름 복사"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
msgstr "텍스트 복사"
|
||||
@@ -715,7 +719,7 @@ msgstr "일시 중지"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Paused"
|
||||
msgstr "일시정지됨"
|
||||
msgstr "일시 정지됨"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
@@ -1078,7 +1082,7 @@ 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/notifications.tsx
|
||||
msgid "Webhook / Push notifications"
|
||||
|
||||
@@ -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"
|
||||
@@ -285,7 +285,7 @@ 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
|
||||
msgid "Copy host"
|
||||
@@ -296,21 +296,25 @@ msgstr "Kopieer host"
|
||||
msgid "Copy Linux command"
|
||||
msgstr "Kopieer Linux-opdracht"
|
||||
|
||||
#: src/components/systems-table/systems-table.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
|
||||
msgid "CPU"
|
||||
@@ -352,7 +356,7 @@ msgstr "Verwijderen"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Delete fingerprint"
|
||||
msgstr ""
|
||||
msgstr "Vingerafdruk verwijderen"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Disk"
|
||||
@@ -486,7 +490,7 @@ 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}}"
|
||||
@@ -552,7 +556,7 @@ msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 15m"
|
||||
msgstr ""
|
||||
msgstr "Gemiddelde Belasting 15m"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 1m"
|
||||
@@ -560,7 +564,7 @@ msgstr ""
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 5m"
|
||||
msgstr ""
|
||||
msgstr "Gemiddelde Belasting 5m"
|
||||
|
||||
#. Short label for load average
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
@@ -794,7 +798,7 @@ msgstr "Hervatten"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Rotate token"
|
||||
msgstr ""
|
||||
msgstr "Roteer Token"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Rows per page"
|
||||
@@ -960,21 +964,21 @@ 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 ""
|
||||
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"
|
||||
@@ -982,11 +986,11 @@ 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"
|
||||
@@ -1019,7 +1023,7 @@ msgstr ""
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Universal token"
|
||||
msgstr ""
|
||||
msgstr "Universele token"
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
@@ -1086,7 +1090,7 @@ 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
|
||||
|
||||
@@ -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"
|
||||
@@ -296,6 +296,10 @@ msgstr "Kopier vert"
|
||||
msgid "Copy Linux command"
|
||||
msgstr "Kopier Linux-kommando"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Copy name"
|
||||
msgstr "Kopier navn"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
msgstr "Kopier tekst"
|
||||
@@ -715,7 +719,7 @@ msgstr "Pause"
|
||||
|
||||
#: src/components/systems-table/systems-table.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."
|
||||
|
||||
@@ -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"
|
||||
@@ -296,6 +296,10 @@ msgstr "Kopiuj host"
|
||||
msgid "Copy Linux command"
|
||||
msgstr "Kopiuj polecenie Linux"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Copy name"
|
||||
msgstr "Kopiuj nazwę"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
msgstr "Kopiuj tekst"
|
||||
@@ -398,7 +402,7 @@ msgstr "Dokumentacja"
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Down"
|
||||
msgstr ""
|
||||
msgstr "Nie działa"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Duration"
|
||||
@@ -407,7 +411,7 @@ msgstr ""
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Edit"
|
||||
msgstr ""
|
||||
msgstr "Edytuj"
|
||||
|
||||
#: src/components/login/forgot-pass-form.tsx
|
||||
#: src/components/login/auth-form.tsx
|
||||
@@ -595,7 +599,7 @@ 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
|
||||
@@ -703,7 +707,7 @@ 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"
|
||||
@@ -811,7 +815,7 @@ msgstr "Zapisz ustawienia"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Save system"
|
||||
msgstr ""
|
||||
msgstr "Zapisz system"
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Search"
|
||||
@@ -901,7 +905,7 @@ msgstr "Tabela"
|
||||
#. Temperature label in systems table
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Temp"
|
||||
msgstr ""
|
||||
msgstr "Temperatura"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/routes/system.tsx
|
||||
@@ -1025,7 +1029,7 @@ msgstr ""
|
||||
#: src/components/systems-table/systems-table.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."
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: pt\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: Portuguese\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -296,6 +296,10 @@ msgstr "Copiar host"
|
||||
msgid "Copy Linux command"
|
||||
msgstr "Copiar comando Linux"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Copy name"
|
||||
msgstr "Copiar nome"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
msgstr "Copiar texto"
|
||||
@@ -398,7 +402,7 @@ msgstr "Documentação"
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Down"
|
||||
msgstr ""
|
||||
msgstr "“Desligado”"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Duration"
|
||||
@@ -595,7 +599,7 @@ msgstr "Gerenciar preferências de exibição e notificação."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Manual setup instructions"
|
||||
msgstr ""
|
||||
msgstr "Instruções de configuração manual"
|
||||
|
||||
#. Chart select field. Please try to keep this short.
|
||||
#: src/components/routes/system.tsx
|
||||
@@ -960,7 +964,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
|
||||
@@ -1025,7 +1029,7 @@ msgstr "Token universal"
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Up"
|
||||
msgstr ""
|
||||
msgstr "“Ligado”"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Updated in real time. Click on a system to view information."
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: ru\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: Russian\n"
|
||||
"Plural-Forms: nplurals=4; plural=((n%10==1 && n%100!=11) ? 0 : ((n%10 >= 2 && n%10 <=4 && (n%100 < 12 || n%100 > 14)) ? 1 : ((n%10 == 0 || (n%10 >= 5 && n%10 <=9)) || (n%100 >= 11 && n%100 <= 14)) ? 2 : 3));\n"
|
||||
@@ -27,7 +27,7 @@ msgstr "{0, plural, one {# день} other {# дней}}"
|
||||
#. placeholder {1}: table.getFilteredRowModel().rows.length
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "{0} of {1} row(s) selected."
|
||||
msgstr ""
|
||||
msgstr "Выбрано {0} из {1} строк."
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "{hours, plural, one {# hour} other {# hours}}"
|
||||
@@ -40,7 +40,7 @@ msgstr "1 час"
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "1 min"
|
||||
msgstr ""
|
||||
msgstr "1 мин"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 week"
|
||||
@@ -53,7 +53,7 @@ msgstr "12 часов"
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "15 min"
|
||||
msgstr ""
|
||||
msgstr "15 мин"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "24 hours"
|
||||
@@ -66,7 +66,7 @@ msgstr "30 дней"
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "5 min"
|
||||
msgstr ""
|
||||
msgstr "5 мин"
|
||||
|
||||
#. Table column
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
@@ -77,7 +77,7 @@ msgstr "Действия"
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Active"
|
||||
msgstr ""
|
||||
msgstr "Активно"
|
||||
|
||||
#: src/components/routes/home.tsx
|
||||
msgid "Active Alerts"
|
||||
@@ -116,7 +116,7 @@ msgstr "Агент"
|
||||
#: src/components/routes/settings/layout.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Alert History"
|
||||
msgstr ""
|
||||
msgstr "История оповещений"
|
||||
|
||||
#: src/components/alerts/alert-button.tsx
|
||||
#: src/components/alerts/alert-button.tsx
|
||||
@@ -134,7 +134,7 @@ msgstr "Вы уверены, что хотите удалить {name}?"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Are you sure?"
|
||||
msgstr ""
|
||||
msgstr "Вы уверены?"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Automatic copy requires a secure context."
|
||||
@@ -191,12 +191,12 @@ msgstr "Двоичный"
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bits (Kbps, Mbps, Gbps)"
|
||||
msgstr ""
|
||||
msgstr "Биты (Кбит/с, Мбит/с, Гбит/с)"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bytes (KB/s, MB/s, GB/s)"
|
||||
msgstr ""
|
||||
msgstr "Байты (Кбайт/с, Мбайт/с, Гбайт/с)"
|
||||
|
||||
#: src/components/charts/mem-chart.tsx
|
||||
msgid "Cache / Buffers"
|
||||
@@ -213,11 +213,11 @@ msgstr "Внимание - возможная потеря данных"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Celsius (°C)"
|
||||
msgstr ""
|
||||
msgstr "Цельсий (°C)"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change display units for metrics."
|
||||
msgstr ""
|
||||
msgstr "Изменить единицы измерения для метрик."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change general application options."
|
||||
@@ -259,7 +259,7 @@ msgstr "Подтвердите пароль"
|
||||
|
||||
#: src/components/routes/home.tsx
|
||||
msgid "Connection is down"
|
||||
msgstr ""
|
||||
msgstr "Нет соединения"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
@@ -285,7 +285,7 @@ msgstr "Скопировать docker run"
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Environment variables"
|
||||
msgid "Copy env"
|
||||
msgstr ""
|
||||
msgstr "Скопировать env"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Copy host"
|
||||
@@ -296,21 +296,25 @@ msgstr "Копировать хост"
|
||||
msgid "Copy Linux command"
|
||||
msgstr "Копировать команду Linux"
|
||||
|
||||
#: src/components/systems-table/systems-table.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
|
||||
msgid "CPU"
|
||||
@@ -329,7 +333,7 @@ msgstr "Создать аккаунт"
|
||||
#. Context: date created
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Created"
|
||||
msgstr ""
|
||||
msgstr "Создано"
|
||||
|
||||
#. Dark theme
|
||||
#: src/components/mode-toggle.tsx
|
||||
@@ -352,7 +356,7 @@ msgstr "Удалить"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Delete fingerprint"
|
||||
msgstr ""
|
||||
msgstr "Удалить отпечаток"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Disk"
|
||||
@@ -364,7 +368,7 @@ msgstr "Дисковый ввод/вывод"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Disk unit"
|
||||
msgstr ""
|
||||
msgstr "Единицы измерения температуры"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
#: src/components/routes/system.tsx
|
||||
@@ -402,7 +406,7 @@ msgstr "Не в сети"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Duration"
|
||||
msgstr ""
|
||||
msgstr "Длительность"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
@@ -447,7 +451,7 @@ msgstr "Существующие системы, не определенные
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Export"
|
||||
msgstr ""
|
||||
msgstr "Экспорт"
|
||||
|
||||
#: src/components/routes/settings/config-yaml.tsx
|
||||
msgid "Export configuration"
|
||||
@@ -459,7 +463,7 @@ msgstr "Экспортируйте текущую конфигурацию си
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Fahrenheit (°F)"
|
||||
msgstr ""
|
||||
msgstr "Фаренгейт (°F)"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Failed to authenticate"
|
||||
@@ -486,7 +490,7 @@ 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}}"
|
||||
@@ -548,24 +552,24 @@ msgstr "Светлая"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Load Average"
|
||||
msgstr ""
|
||||
msgstr "Средняя загрузка"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 15m"
|
||||
msgstr ""
|
||||
msgstr "Средняя загрузка за 15м"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 1m"
|
||||
msgstr ""
|
||||
msgstr "Средняя загрузка за 1м"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 5m"
|
||||
msgstr ""
|
||||
msgstr "Средняя загрузка за 5м"
|
||||
|
||||
#. Short label for load average
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Load Avg"
|
||||
msgstr ""
|
||||
msgstr "Ср. загрузка"
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Log Out"
|
||||
@@ -635,7 +639,7 @@ msgstr "Сетевой трафик публичных интерфейсов"
|
||||
#. Context: Bytes or bits
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Network unit"
|
||||
msgstr ""
|
||||
msgstr "Единицы измерения скорости сети"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "No results found."
|
||||
@@ -643,7 +647,7 @@ msgstr "Результаты не найдены."
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "No results."
|
||||
msgstr ""
|
||||
msgstr "Нет результатов."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
@@ -686,7 +690,7 @@ msgstr "Страница"
|
||||
#. placeholder {1}: table.getPageCount()
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Page {0} of {1}"
|
||||
msgstr ""
|
||||
msgstr "Страница {0} из {1}"
|
||||
|
||||
#: src/components/command-palette.tsx
|
||||
msgid "Pages / Settings"
|
||||
@@ -715,7 +719,7 @@ msgstr "Пауза"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Paused"
|
||||
msgstr "Приостановлено"
|
||||
msgstr "Пауза"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
@@ -786,7 +790,7 @@ msgstr "Сбросить пароль"
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Resolved"
|
||||
msgstr ""
|
||||
msgstr "Завершено"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Resume"
|
||||
@@ -794,11 +798,11 @@ 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 ""
|
||||
msgstr "Строк на странице"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Save address using enter key or comma. Leave blank to disable email notifications."
|
||||
@@ -859,7 +863,7 @@ msgstr "Сортировать по"
|
||||
#. Context: alert state (active or resolved)
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "State"
|
||||
msgstr ""
|
||||
msgstr "Состояние"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Status"
|
||||
@@ -884,7 +888,7 @@ msgstr "Система"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "System load averages over time"
|
||||
msgstr ""
|
||||
msgstr "Средняя загрузка системы за время"
|
||||
|
||||
#: src/components/navbar.tsx
|
||||
msgid "Systems"
|
||||
@@ -910,7 +914,7 @@ msgstr "Температура"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Temperature unit"
|
||||
msgstr ""
|
||||
msgstr "Температура"
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Temperatures of system sensors"
|
||||
@@ -934,7 +938,7 @@ msgstr "Это действие не может быть отменено. Эт
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "This will permanently delete all selected records from the database."
|
||||
msgstr ""
|
||||
msgstr "Это навсегда удалит все выбранные записи из базы данных."
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Throughput of {extraFsName}"
|
||||
@@ -960,33 +964,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 ""
|
||||
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"
|
||||
@@ -1015,11 +1019,11 @@ msgstr "Срабатывает, когда использование любог
|
||||
#. Temperature / network units
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Unit preferences"
|
||||
msgstr ""
|
||||
msgstr "Параметры единиц измерения"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Universal token"
|
||||
msgstr ""
|
||||
msgstr "Универсальный токен"
|
||||
|
||||
#. Context: System is up
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
@@ -1058,7 +1062,7 @@ msgstr "Пользователи"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Value"
|
||||
msgstr ""
|
||||
msgstr "Значение"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "View"
|
||||
@@ -1066,7 +1070,7 @@ msgstr "Вид"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "View your 200 most recent alerts."
|
||||
msgstr ""
|
||||
msgstr "Просмотреть 200 последних оповещений."
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Visible Fields"
|
||||
@@ -1086,7 +1090,7 @@ msgstr "Webhook / Push уведомления"
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "When enabled, this token allows agents to self-register without prior system creation. Expires after one hour or on hub restart."
|
||||
msgstr ""
|
||||
msgstr "Если включено, этот токен позволяет агентам регистрироваться самостоятельно без предварительного создания системы. Истекает через час или при перезапуске хаба."
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: sl\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: Slovenian\n"
|
||||
"Plural-Forms: nplurals=4; plural=n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3;\n"
|
||||
@@ -296,6 +296,10 @@ msgstr "Kopiraj gostitelja"
|
||||
msgid "Copy Linux command"
|
||||
msgstr "Kopiraj Linux ukaz"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Copy name"
|
||||
msgstr "Kopiraj ime"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
msgstr "Kopiraj besedilo"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: sv\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-03-06 07:27\n"
|
||||
"PO-Revision-Date: 2025-08-01 23:21\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Swedish\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -27,7 +27,7 @@ msgstr "{0, plural, one {# dag} other {# dagar}}"
|
||||
#. placeholder {1}: table.getFilteredRowModel().rows.length
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "{0} of {1} row(s) selected."
|
||||
msgstr ""
|
||||
msgstr "{0} av {1} rad(er) valda."
|
||||
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "{hours, plural, one {# hour} other {# hours}}"
|
||||
@@ -40,7 +40,7 @@ msgstr "1 timme"
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "1 min"
|
||||
msgstr ""
|
||||
msgstr "1 min"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "1 week"
|
||||
@@ -53,7 +53,7 @@ msgstr "12 timmar"
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "15 min"
|
||||
msgstr ""
|
||||
msgstr "15 min"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "24 hours"
|
||||
@@ -66,7 +66,7 @@ msgstr "30 dagar"
|
||||
#. Load average
|
||||
#: src/components/charts/load-average-chart.tsx
|
||||
msgid "5 min"
|
||||
msgstr ""
|
||||
msgstr "5 min"
|
||||
|
||||
#. Table column
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
@@ -77,7 +77,7 @@ msgstr "Åtgärder"
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Active"
|
||||
msgstr ""
|
||||
msgstr "Aktiv"
|
||||
|
||||
#: src/components/routes/home.tsx
|
||||
msgid "Active Alerts"
|
||||
@@ -134,7 +134,7 @@ msgstr "Är du säker på att du vill ta bort {name}?"
|
||||
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
msgid "Are you sure?"
|
||||
msgstr ""
|
||||
msgstr "Är du säker?"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Automatic copy requires a secure context."
|
||||
@@ -191,12 +191,12 @@ msgstr "Binär"
|
||||
#: src/components/routes/settings/general.tsx
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Bits (Kbps, Mbps, Gbps)"
|
||||
msgstr ""
|
||||
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 ""
|
||||
msgstr "Bytes (KB/s, MB/s, GB/S)"
|
||||
|
||||
#: src/components/charts/mem-chart.tsx
|
||||
msgid "Cache / Buffers"
|
||||
@@ -213,11 +213,11 @@ msgstr "Varning - potentiell dataförlust"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Celsius (°C)"
|
||||
msgstr ""
|
||||
msgstr "Celsius (°C)"
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change display units for metrics."
|
||||
msgstr ""
|
||||
msgstr "Ändra enheter för mätvärden."
|
||||
|
||||
#: src/components/routes/settings/general.tsx
|
||||
msgid "Change general application options."
|
||||
@@ -259,7 +259,7 @@ msgstr "Bekräfta lösenord"
|
||||
|
||||
#: src/components/routes/home.tsx
|
||||
msgid "Connection is down"
|
||||
msgstr ""
|
||||
msgstr "Ej ansluten"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/routes/settings/alerts-history-data-table.tsx
|
||||
@@ -296,6 +296,10 @@ msgstr "Kopiera värd"
|
||||
msgid "Copy Linux command"
|
||||
msgstr "Kopiera Linux-kommando"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Copy name"
|
||||
msgstr "Kopiera namn"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
msgstr "Kopiera text"
|
||||
@@ -310,7 +314,7 @@ msgstr ""
|
||||
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgid "Copy YAML"
|
||||
msgstr ""
|
||||
msgstr "Kopiera YAML"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "CPU"
|
||||
@@ -329,7 +333,7 @@ msgstr "Skapa konto"
|
||||
#. Context: date created
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Created"
|
||||
msgstr ""
|
||||
msgstr "Skapad"
|
||||
|
||||
#. Dark theme
|
||||
#: src/components/mode-toggle.tsx
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: tr\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: Turkish\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -296,6 +296,10 @@ msgstr "Ana bilgisayarı kopyala"
|
||||
msgid "Copy Linux command"
|
||||
msgstr "Linux komutunu kopyala"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Copy name"
|
||||
msgstr "Adı kopyala"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
msgstr "Metni kopyala"
|
||||
@@ -960,7 +964,7 @@ msgstr "Temayı değiştir"
|
||||
#: 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
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: uk\n"
|
||||
"Project-Id-Version: beszel\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-03-15 04:14\n"
|
||||
"PO-Revision-Date: 2025-07-25 22:44\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Ukrainian\n"
|
||||
"Plural-Forms: nplurals=4; plural=((n%10==1 && n%100!=11) ? 0 : ((n%10 >= 2 && n%10 <=4 && (n%100 < 12 || n%100 > 14)) ? 1 : ((n%10 == 0 || (n%10 >= 5 && n%10 <=9)) || (n%100 >= 11 && n%100 <= 14)) ? 2 : 3));\n"
|
||||
@@ -285,7 +285,7 @@ msgstr "Копіювати docker run"
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Environment variables"
|
||||
msgid "Copy env"
|
||||
msgstr "Копіювати змінні середовища"
|
||||
msgstr "Копіювати env"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Copy host"
|
||||
@@ -296,6 +296,10 @@ msgstr "Копіювати хост"
|
||||
msgid "Copy Linux command"
|
||||
msgstr "Копіювати команду Linux"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Copy name"
|
||||
msgstr "Копіювати імʼя"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
msgstr "Копіювати текст"
|
||||
@@ -1078,7 +1082,7 @@ 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/notifications.tsx
|
||||
msgid "Webhook / Push notifications"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: vi\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: Vietnamese\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
@@ -296,6 +296,10 @@ msgstr "Sao chép máy chủ"
|
||||
msgid "Copy Linux command"
|
||||
msgstr "Sao chép lệnh Linux"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Copy name"
|
||||
msgstr "Sao chép tên"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
msgstr "Sao chép văn bản"
|
||||
@@ -960,7 +964,7 @@ msgstr "Chuyển đổi chủ đề"
|
||||
#: 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
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: zh\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: Chinese Simplified\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
@@ -296,6 +296,10 @@ msgstr "复制主机名"
|
||||
msgid "Copy Linux command"
|
||||
msgstr "复制 Linux 安装命令"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Copy name"
|
||||
msgstr "复制名称"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
msgstr "复制文本"
|
||||
@@ -398,7 +402,7 @@ msgstr "文档"
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Down"
|
||||
msgstr "停止"
|
||||
msgstr "离线"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Duration"
|
||||
@@ -552,7 +556,7 @@ msgstr "系统负载"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 15m"
|
||||
msgstr "15分钟负载平均值"
|
||||
msgstr "15分钟内的平均负载"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 1m"
|
||||
@@ -560,7 +564,7 @@ msgstr "1分钟负载平均值"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Load Average 5m"
|
||||
msgstr "5分钟负载平均值"
|
||||
msgstr "5分钟内的平均负载"
|
||||
|
||||
#. Short label for load average
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
@@ -986,7 +990,7 @@ msgstr "当15分钟负载平均值超过阈值时触发"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when 5 minute load average exceeds a threshold"
|
||||
msgstr "当5分钟负载平均值超过阈值时触发"
|
||||
msgstr "当5分钟内的平均负载超过阈值时触发"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when any sensor exceeds a threshold"
|
||||
@@ -1025,7 +1029,7 @@ msgstr "通用令牌"
|
||||
#: src/components/systems-table/systems-table.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."
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: zh\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: Chinese Traditional, Hong Kong\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
@@ -296,6 +296,10 @@ msgstr "複製主機"
|
||||
msgid "Copy Linux command"
|
||||
msgstr "複製 Linux 指令"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Copy name"
|
||||
msgstr "複製名稱"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
msgstr "複製文本"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: zh\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: Chinese Traditional\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
@@ -274,13 +274,13 @@ 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"
|
||||
@@ -296,6 +296,10 @@ msgstr "複製主機"
|
||||
msgid "Copy Linux command"
|
||||
msgstr "複製 Linux 指令"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Copy name"
|
||||
msgstr "複製名稱"
|
||||
|
||||
#: src/components/copy-to-clipboard.tsx
|
||||
msgid "Copy text"
|
||||
msgstr "複製文字"
|
||||
@@ -398,7 +402,7 @@ msgstr "文件"
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
#: src/components/routes/system.tsx
|
||||
msgid "Down"
|
||||
msgstr "中斷"
|
||||
msgstr "離線"
|
||||
|
||||
#: src/components/alerts-history-columns.tsx
|
||||
msgid "Duration"
|
||||
@@ -514,7 +518,7 @@ msgstr "網格"
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
msgid "Homebrew command"
|
||||
msgstr "Homebrew 命令"
|
||||
msgstr "Homebrew 指令"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
msgid "Host / IP"
|
||||
@@ -715,7 +719,7 @@ msgstr "暫停"
|
||||
|
||||
#: src/components/systems-table/systems-table.tsx
|
||||
msgid "Paused"
|
||||
msgstr "已暂停"
|
||||
msgstr "已暫停"
|
||||
|
||||
#: src/components/routes/settings/notifications.tsx
|
||||
msgid "Please <0>configure an SMTP server</0> to ensure alerts are delivered."
|
||||
@@ -794,7 +798,7 @@ 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"
|
||||
@@ -970,11 +974,11 @@ 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 "令牌和指紋用於驗證到中心的WebSocket連接。"
|
||||
msgstr "令牌和指紋被用於驗證到 Hub 的 WebSocket 連線。"
|
||||
|
||||
#: src/lib/utils.ts
|
||||
msgid "Triggers when 1 minute load average exceeds a threshold"
|
||||
@@ -1078,7 +1082,7 @@ 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/notifications.tsx
|
||||
msgid "Webhook / Push notifications"
|
||||
@@ -1086,13 +1090,13 @@ 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 "啟用時,此令牌允許代理程式在沒有預先建立系統的情況下自動註冊。在一小時後或 Hub 重啟時過期。"
|
||||
|
||||
#: src/components/add-system.tsx
|
||||
#: src/components/routes/settings/tokens-fingerprints.tsx
|
||||
msgctxt "Button to copy install command"
|
||||
msgid "Windows command"
|
||||
msgstr "Windows 命令"
|
||||
msgstr "Windows 指令"
|
||||
|
||||
#. Disk write
|
||||
#: src/components/routes/system.tsx
|
||||
|
||||
12
beszel/site/src/types.d.ts
vendored
12
beszel/site/src/types.d.ts
vendored
@@ -50,6 +50,8 @@ export interface SystemInfo {
|
||||
l5?: number
|
||||
/** load average 15 minutes */
|
||||
l15?: number
|
||||
/** load average */
|
||||
la?: [number, number, number]
|
||||
/** operating system */
|
||||
o?: string
|
||||
/** uptime */
|
||||
@@ -79,12 +81,15 @@ export interface SystemStats {
|
||||
cpu: number
|
||||
/** peak cpu */
|
||||
cpum?: number
|
||||
// TODO: remove these in future release in favor of la
|
||||
/** load average 1 minute */
|
||||
l1?: number
|
||||
/** load average 5 minutes */
|
||||
l5?: number
|
||||
/** load average 15 minutes */
|
||||
l15?: number
|
||||
/** load average */
|
||||
la?: [number, number, number]
|
||||
/** total memory (gb) */
|
||||
m: number
|
||||
/** memory used (gb) */
|
||||
@@ -234,7 +239,14 @@ type ChartDataContainer = {
|
||||
[key: string]: key extends "created" ? never : ContainerStats
|
||||
}
|
||||
|
||||
export interface SemVer {
|
||||
major: number
|
||||
minor: number
|
||||
patch: number
|
||||
}
|
||||
|
||||
export interface ChartData {
|
||||
agentVersion: SemVer
|
||||
systemStats: SystemStatsRecord[]
|
||||
containerData: ChartDataContainer[]
|
||||
orientation: "right" | "left"
|
||||
|
||||
@@ -3,7 +3,7 @@ package beszel
|
||||
import "github.com/blang/semver"
|
||||
|
||||
const (
|
||||
Version = "0.12.0"
|
||||
Version = "0.12.2"
|
||||
AppName = "beszel"
|
||||
)
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ It has a friendly web interface, simple configuration, and is ready to use out o
|
||||
- **Lightweight**: Smaller and less resource-intensive than leading solutions.
|
||||
- **Simple**: Easy setup with little manual configuration required.
|
||||
- **Docker stats**: Tracks CPU, memory, and network usage history for each container.
|
||||
- **Alerts**: Configurable alerts for CPU, memory, disk, bandwidth, temperature, and status.
|
||||
- **Alerts**: Configurable alerts for CPU, memory, disk, bandwidth, temperature, load average, and status.
|
||||
- **Multi-user**: Users manage their own systems. Admins can share systems across users.
|
||||
- **OAuth / OIDC**: Supports many OAuth2 providers. Password auth can be disabled.
|
||||
- **Automatic backups**: Save to and restore from disk or S3-compatible storage.
|
||||
|
||||
@@ -31,10 +31,12 @@ if ! getent passwd "$SERVICE_USER" >/dev/null; then
|
||||
--gecos "System user for $SERVICE"
|
||||
fi
|
||||
|
||||
# Enable docker
|
||||
if ! getent group docker | grep -q "$SERVICE_USER"; then
|
||||
echo "Adding $SERVICE_USER to docker group"
|
||||
usermod -aG docker "$SERVICE_USER"
|
||||
# Enable docker (only if docker group exists)
|
||||
if getent group docker >/dev/null 2>&1; then
|
||||
if ! getent group docker | grep -q "$SERVICE_USER"; then
|
||||
echo "Adding $SERVICE_USER to docker group"
|
||||
usermod -aG docker "$SERVICE_USER"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Create config file if it doesn't already exist
|
||||
|
||||
373
supplemental/licenses/LibreHardwareMonitor/LICENSE
Normal file
373
supplemental/licenses/LibreHardwareMonitor/LICENSE
Normal file
@@ -0,0 +1,373 @@
|
||||
Mozilla Public License Version 2.0
|
||||
==================================
|
||||
|
||||
1. Definitions
|
||||
--------------
|
||||
|
||||
1.1. "Contributor"
|
||||
means each individual or legal entity that creates, contributes to
|
||||
the creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
means the combination of the Contributions of others (if any) used
|
||||
by a Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
means Source Code Form to which the initial Contributor has attached
|
||||
the notice in Exhibit A, the Executable Form of such Source Code
|
||||
Form, and Modifications of such Source Code Form, in each case
|
||||
including portions thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
(a) that the initial Contributor has attached the notice described
|
||||
in Exhibit B to the Covered Software; or
|
||||
|
||||
(b) that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the
|
||||
terms of a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
means a work that combines Covered Software with other material, in
|
||||
a separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
means having the right to grant, to the maximum extent possible,
|
||||
whether at the time of the initial grant or subsequently, any and
|
||||
all of the rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
means any of the following:
|
||||
|
||||
(a) any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered
|
||||
Software; or
|
||||
|
||||
(b) any new file in Source Code Form that contains any Covered
|
||||
Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the
|
||||
License, by the making, using, selling, offering for sale, having
|
||||
made, import, or transfer of either its Contributions or its
|
||||
Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
means either the GNU General Public License, Version 2.0, the GNU
|
||||
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||
Public License, Version 3.0, or any later versions of those
|
||||
licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that
|
||||
controls, is controlled by, or is under common control with You. For
|
||||
purposes of this definition, "control" means (a) the power, direct
|
||||
or indirect, to cause the direction or management of such entity,
|
||||
whether by contract or otherwise, or (b) ownership of more than
|
||||
fifty percent (50%) of the outstanding shares or beneficial
|
||||
ownership of such entity.
|
||||
|
||||
2. License Grants and Conditions
|
||||
--------------------------------
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
(a) under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||
for sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
(a) for any code that a Contributor has removed from Covered Software;
|
||||
or
|
||||
|
||||
(b) for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights
|
||||
to grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||
in Section 2.1.
|
||||
|
||||
3. Responsibilities
|
||||
-------------------
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
(a) such Covered Software must also be made available in Source Code
|
||||
Form, as described in Section 3.1, and You must inform recipients of
|
||||
the Executable Form how they can obtain a copy of such Source Code
|
||||
Form by reasonable means in a timely manner, at a charge no more
|
||||
than the cost of distribution to the recipient; and
|
||||
|
||||
(b) You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter
|
||||
the recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty,
|
||||
or limitations of liability) contained within the Source Code Form of
|
||||
the Covered Software, except that You may alter any license notices to
|
||||
the extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
---------------------------------------------------
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this
|
||||
License with respect to some or all of the Covered Software due to
|
||||
statute, judicial order, or regulation then You must: (a) comply with
|
||||
the terms of this License to the maximum extent possible; and (b)
|
||||
describe the limitations and the code they affect. Such description must
|
||||
be placed in a text file included with all distributions of the Covered
|
||||
Software under this License. Except to the extent prohibited by statute
|
||||
or regulation, such description must be sufficiently detailed for a
|
||||
recipient of ordinary skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
--------------
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically
|
||||
if You fail to comply with any of its terms. However, if You become
|
||||
compliant, then the rights granted under this License from a particular
|
||||
Contributor are reinstated (a) provisionally, unless and until such
|
||||
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||
ongoing basis, if such Contributor fails to notify You of the
|
||||
non-compliance by some reasonable means prior to 60 days after You have
|
||||
come back into compliance. Moreover, Your grants from a particular
|
||||
Contributor are reinstated on an ongoing basis if such Contributor
|
||||
notifies You of the non-compliance by some reasonable means, this is the
|
||||
first time You have received notice of non-compliance with this License
|
||||
from such Contributor, and You become compliant prior to 30 days after
|
||||
Your receipt of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||
end user license agreements (excluding distributors and resellers) which
|
||||
have been validly granted by You or Your distributors under this License
|
||||
prior to termination shall survive termination.
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 6. Disclaimer of Warranty *
|
||||
* ------------------------- *
|
||||
* *
|
||||
* Covered Software is provided under this License on an "as is" *
|
||||
* basis, without warranty of any kind, either expressed, implied, or *
|
||||
* statutory, including, without limitation, warranties that the *
|
||||
* Covered Software is free of defects, merchantable, fit for a *
|
||||
* particular purpose or non-infringing. The entire risk as to the *
|
||||
* quality and performance of the Covered Software is with You. *
|
||||
* Should any Covered Software prove defective in any respect, You *
|
||||
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||
* essential part of this License. No use of any Covered Software is *
|
||||
* authorized under this License except under this disclaimer. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 7. Limitation of Liability *
|
||||
* -------------------------- *
|
||||
* *
|
||||
* Under no circumstances and under no legal theory, whether tort *
|
||||
* (including negligence), contract, or otherwise, shall any *
|
||||
* Contributor, or anyone who distributes Covered Software as *
|
||||
* permitted above, be liable to You for any direct, indirect, *
|
||||
* special, incidental, or consequential damages of any character *
|
||||
* including, without limitation, damages for lost profits, loss of *
|
||||
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||
* and all other commercial damages or losses, even if such party *
|
||||
* shall have been informed of the possibility of such damages. This *
|
||||
* limitation of liability shall not apply to liability for death or *
|
||||
* personal injury resulting from such party's negligence to the *
|
||||
* extent applicable law prohibits such limitation. Some *
|
||||
* jurisdictions do not allow the exclusion or limitation of *
|
||||
* incidental or consequential damages, so this exclusion and *
|
||||
* limitation may not apply to You. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
8. Litigation
|
||||
-------------
|
||||
|
||||
Any litigation relating to this License may be brought only in the
|
||||
courts of a jurisdiction where the defendant maintains its principal
|
||||
place of business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions.
|
||||
Nothing in this Section shall prevent a party's ability to bring
|
||||
cross-claims or counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
----------------
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides
|
||||
that the language of a contract shall be construed against the drafter
|
||||
shall not be used to construe this License against a Contributor.
|
||||
|
||||
10. Versions of the License
|
||||
---------------------------
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses
|
||||
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
-------------------------------------------
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular
|
||||
file, then You may include the notice in a location (such as a LICENSE
|
||||
file in a relevant directory) where a recipient would be likely to look
|
||||
for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
---------------------------------------------------------
|
||||
|
||||
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
defined by the Mozilla Public License, v. 2.0.
|
||||
@@ -5,7 +5,7 @@ is_alpine() {
|
||||
}
|
||||
|
||||
is_openwrt() {
|
||||
cat /etc/os-release | grep -q "OpenWrt"
|
||||
grep -qi "OpenWrt" /etc/os-release
|
||||
}
|
||||
|
||||
# If SELinux is enabled, set the context of the binary
|
||||
@@ -227,8 +227,8 @@ if [ "$UNINSTALL" = true ]; then
|
||||
rm -f /var/log/beszel-agent.log /var/log/beszel-agent.err
|
||||
elif is_openwrt; then
|
||||
echo "Stopping and disabling the agent service..."
|
||||
service beszel-agent stop
|
||||
service beszel-agent disable
|
||||
/etc/init.d/beszel-agent stop
|
||||
/etc/init.d/beszel-agent disable
|
||||
|
||||
echo "Removing the OpenWRT service files..."
|
||||
rm -f /etc/init.d/beszel-agent
|
||||
@@ -288,13 +288,13 @@ package_installed() {
|
||||
}
|
||||
|
||||
# Check for package manager and install necessary packages if not installed
|
||||
if is_alpine; then
|
||||
if ! package_installed tar || ! package_installed curl || ! package_installed coreutils; then
|
||||
if package_installed apk; then
|
||||
if ! package_installed tar || ! package_installed curl || ! package_installed sha256sum; then
|
||||
apk update
|
||||
apk add tar curl coreutils shadow
|
||||
fi
|
||||
elif is_openwrt; then
|
||||
if ! package_installed tar || ! package_installed curl || ! package_installed coreutils; then
|
||||
elif package_installed opkg; then
|
||||
if ! package_installed tar || ! package_installed curl || ! package_installed sha256sum; then
|
||||
opkg update
|
||||
opkg install tar curl coreutils
|
||||
fi
|
||||
@@ -335,11 +335,10 @@ else
|
||||
fi
|
||||
|
||||
# Create a dedicated user for the service if it doesn't exist
|
||||
echo "Creating a dedicated user for the Beszel Agent service..."
|
||||
if is_alpine; then
|
||||
if ! id -u beszel >/dev/null 2>&1; then
|
||||
echo "Creating a dedicated group for the Beszel Agent service..."
|
||||
addgroup beszel
|
||||
echo "Creating a dedicated user for the Beszel Agent service..."
|
||||
adduser -S -D -H -s /sbin/nologin -G beszel beszel
|
||||
fi
|
||||
# Add the user to the docker group to allow access to the Docker socket if group docker exists
|
||||
@@ -347,10 +346,37 @@ if is_alpine; then
|
||||
echo "Adding beszel to docker group"
|
||||
usermod -aG docker beszel
|
||||
fi
|
||||
|
||||
elif is_openwrt; then
|
||||
# Create beszel group first if it doesn't exist (check /etc/group directly)
|
||||
if ! grep -q "^beszel:" /etc/group >/dev/null 2>&1; then
|
||||
echo "beszel:x:999:" >> /etc/group
|
||||
fi
|
||||
|
||||
# Create beszel user if it doesn't exist (double-check to prevent duplicates)
|
||||
if ! id -u beszel >/dev/null 2>&1 && ! grep -q "^beszel:" /etc/passwd >/dev/null 2>&1; then
|
||||
echo "beszel:x:999:999::/nonexistent:/bin/false" >> /etc/passwd
|
||||
fi
|
||||
|
||||
# Add the user to the docker group if docker group exists and user is not already in it
|
||||
if grep -q "^docker:" /etc/group >/dev/null 2>&1; then
|
||||
echo "Adding beszel to docker group"
|
||||
# Check if beszel is already in docker group
|
||||
if ! grep "^docker:" /etc/group | grep -q "beszel"; then
|
||||
# Add beszel to docker group by modifying /etc/group
|
||||
# Handle both cases: group with existing members and group without members
|
||||
if grep "^docker:" /etc/group | grep -q ":.*:.*$"; then
|
||||
# Group has existing members, append with comma
|
||||
sed -i 's/^docker:\([^:]*:[^:]*:\)\(.*\)$/docker:\1\2,beszel/' /etc/group
|
||||
else
|
||||
# Group has no members, just append
|
||||
sed -i 's/^docker:\([^:]*:[^:]*:\)$/docker:\1beszel/' /etc/group
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
else
|
||||
if ! id -u beszel >/dev/null 2>&1; then
|
||||
echo "Creating a dedicated user for the Beszel Agent service..."
|
||||
useradd --system --home-dir /nonexistent --shell /bin/false beszel
|
||||
fi
|
||||
# Add the user to the docker group to allow access to the Docker socket if group docker exists
|
||||
@@ -427,6 +453,11 @@ set_selinux_context
|
||||
# Cleanup
|
||||
rm -rf "$TEMP_DIR"
|
||||
|
||||
# Make sure /etc/machine-id exists for persistent fingerprint
|
||||
if [ ! -f /etc/machine-id ]; then
|
||||
cat /proc/sys/kernel/random/uuid | tr -d '-' > /etc/machine-id
|
||||
fi
|
||||
|
||||
# Check for NVIDIA GPUs and grant device permissions for systemd service
|
||||
detect_nvidia_devices() {
|
||||
local devices=""
|
||||
@@ -546,10 +577,7 @@ start_service() {
|
||||
procd_set_param command /opt/beszel-agent/beszel-agent
|
||||
procd_set_param user beszel
|
||||
procd_set_param pidfile /var/run/beszel-agent.pid
|
||||
procd_set_param env PORT="$PORT"
|
||||
procd_set_param env KEY="$KEY"
|
||||
procd_set_param env TOKEN="$TOKEN"
|
||||
procd_set_param env HUB_URL="$HUB_URL"
|
||||
procd_set_param env PORT="$PORT" KEY="$KEY" TOKEN="$TOKEN" HUB_URL="$HUB_URL"
|
||||
procd_set_param stdout 1
|
||||
procd_set_param stderr 1
|
||||
procd_close_instance
|
||||
@@ -573,10 +601,10 @@ EOF
|
||||
|
||||
# Enable the service
|
||||
chmod +x /etc/init.d/beszel-agent
|
||||
service beszel-agent enable
|
||||
/etc/init.d/beszel-agent enable
|
||||
|
||||
# Start the service
|
||||
service beszel-agent restart
|
||||
/etc/init.d/beszel-agent restart
|
||||
|
||||
# Auto-update service for OpenWRT using a crontab job
|
||||
if [ "$AUTO_UPDATE_FLAG" = "true" ]; then
|
||||
@@ -604,9 +632,9 @@ EOF
|
||||
esac
|
||||
|
||||
# Check service status
|
||||
if ! service beszel-agent running >/dev/null 2>&1; then
|
||||
if ! /etc/init.d/beszel-agent running >/dev/null 2>&1; then
|
||||
echo "Error: The Beszel Agent service is not running."
|
||||
service beszel-agent status
|
||||
/etc/init.d/beszel-agent status
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
@@ -217,13 +217,6 @@ function Update-ServicePath {
|
||||
throw "NSSM is not available in PATH and no valid NSSMPath was provided"
|
||||
}
|
||||
|
||||
# Stop the service
|
||||
Write-Host "Stopping beszel-agent service..."
|
||||
& $nssmCommand stop beszel-agent
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Host "Warning: Failed to stop service, continuing anyway..." -ForegroundColor Yellow
|
||||
}
|
||||
|
||||
# Update the application path
|
||||
& $nssmCommand set beszel-agent Application $NewAgentPath
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
@@ -233,7 +226,25 @@ function Update-ServicePath {
|
||||
Write-Host "Service path updated to: $NewAgentPath"
|
||||
|
||||
# Start the service
|
||||
Start-BeszelAgentService -NSSMPath $nssmCommand
|
||||
}
|
||||
|
||||
# Function to start and monitor the service
|
||||
function Start-BeszelAgentService {
|
||||
param (
|
||||
[string]$NSSMPath = ""
|
||||
)
|
||||
|
||||
Write-Host "Starting beszel-agent service..."
|
||||
|
||||
# Determine the NSSM executable to use
|
||||
$nssmCommand = "nssm"
|
||||
if ($NSSMPath -and (Test-Path $NSSMPath)) {
|
||||
$nssmCommand = $NSSMPath
|
||||
} elseif (-not (Test-CommandExists "nssm")) {
|
||||
throw "NSSM is not available in PATH and no valid NSSMPath was provided"
|
||||
}
|
||||
|
||||
& $nssmCommand start beszel-agent
|
||||
$startResult = $LASTEXITCODE
|
||||
|
||||
@@ -255,7 +266,7 @@ function Update-ServicePath {
|
||||
|
||||
if ($serviceStatus -eq "SERVICE_RUNNING") {
|
||||
$serviceStarted = $true
|
||||
Write-Host "Success! The beszel-agent service is now running with the updated path." -ForegroundColor Green
|
||||
Write-Host "Success! The beszel-agent service is now running." -ForegroundColor Green
|
||||
}
|
||||
elseif ($serviceStatus -like "*PENDING*") {
|
||||
Write-Host "Service is still starting (status: $serviceStatus)... waiting" -ForegroundColor Yellow
|
||||
@@ -273,7 +284,7 @@ function Update-ServicePath {
|
||||
}
|
||||
} else {
|
||||
# NSSM start command was successful
|
||||
Write-Host "Success! The beszel-agent service is running with the updated path." -ForegroundColor Green
|
||||
Write-Host "Success! The beszel-agent service is running properly." -ForegroundColor Green
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,7 +298,6 @@ $isAdmin = Test-Admin
|
||||
try {
|
||||
Write-Host "Beszel Agent Upgrade Script" -ForegroundColor Cyan
|
||||
Write-Host "===========================" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
|
||||
# First: Check if service exists (doesn't require admin)
|
||||
$existingService = Get-Service -Name "beszel-agent" -ErrorAction SilentlyContinue
|
||||
@@ -312,6 +322,13 @@ try {
|
||||
Write-Host "Retrieving current service configuration..."
|
||||
$currentConfig = Get-ServiceConfiguration -NSSMPath $nssmPath
|
||||
|
||||
# Stop the service before upgrade
|
||||
Write-Host "Stopping beszel-agent service..."
|
||||
& $nssmPath stop beszel-agent
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Host "Warning: Failed to stop service, continuing anyway..." -ForegroundColor Yellow
|
||||
}
|
||||
|
||||
# Upgrade the agent (doesn't require admin)
|
||||
Write-Host "Upgrading beszel-agent..."
|
||||
$newAgentPath = $null
|
||||
@@ -340,7 +357,8 @@ try {
|
||||
|
||||
# Check if the path has changed
|
||||
if ($currentConfig.CurrentPath -eq $newAgentPath) {
|
||||
Write-Host "Agent path has not changed. Service update not needed." -ForegroundColor Green
|
||||
Write-Host "Agent path has not changed. Restarting service..." -ForegroundColor Green
|
||||
Start-BeszelAgentService -NSSMPath $nssmPath
|
||||
Write-Host "Upgrade completed successfully!" -ForegroundColor Green
|
||||
exit 0
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user