Compare commits

...

18 Commits

Author SHA1 Message Date
henrygd
3e73399b87 fix battery detection on newer macs (#1170) 2025-09-15 12:02:50 -04:00
Ryan W
e149366451 Fixing service name in helm chart and making default values unopinionated (#1166) 2025-09-13 12:09:36 -04:00
henrygd
8da1ded73e strip whitespace from TOKEN_FILE (#984) 2025-09-12 12:59:53 -04:00
henrygd
efa37b2312 web: extra check for valid system before adding (#1063) 2025-09-11 15:37:11 -04:00
henrygd
bcdb4c92b5 add freebsd to list of copyable commands 2025-09-11 15:07:37 -04:00
henrygd
a7d07310b6 Add AUTO_LOGIN environment variable for automatic login. (#399) 2025-09-11 14:01:09 -04:00
hank
8db87e5497 Update Crowdin configuration file 2025-09-11 12:45:43 -04:00
henrygd
e601a0d564 add TRUSTED_AUTH_HEADER for auth forwarding (#399) 2025-09-10 21:26:59 -04:00
Fankesyooni
07491108cd new zh-CN translations (#1160) 2025-09-10 13:34:17 -04:00
henrygd
42ab17de1f move i18n.yml to project root 2025-09-10 13:30:42 -04:00
henrygd
2d14174f61 update i18n.yml 2025-09-10 13:28:06 -04:00
henrygd
a19ccc9263 comments 2025-09-09 17:13:15 -04:00
henrygd
956880aa59 improve container filtering performance
- also a few instances of autoformat
2025-09-09 16:59:16 -04:00
henrygd
b2b54db409 update makefile with proper site src path 2025-09-09 15:41:15 -04:00
henrygd
32d5188eef path updates after reorganization 2025-09-09 13:53:36 -04:00
henrygd
46dab7f531 fix gitignore after reorganization 2025-09-09 13:51:46 -04:00
henrygd
c898a9ebbc update /src to /internal 2025-09-09 13:35:34 -04:00
henrygd
8a13b05c20 rename /src to /internal (sorry i'll fix the prs) 2025-09-09 13:29:07 -04:00
204 changed files with 433 additions and 182 deletions

View File

@@ -1,6 +1,6 @@
# Node.js dependencies # Node.js dependencies
node_modules node_modules
src/site/node_modules internalsite/node_modules
# Go build artifacts and binaries # Go build artifacts and binaries
build build

View File

@@ -14,21 +14,21 @@ jobs:
include: include:
- image: henrygd/beszel - image: henrygd/beszel
context: ./ context: ./
dockerfile: ./src/dockerfile_hub dockerfile: ./internal/dockerfile_hub
registry: docker.io registry: docker.io
username_secret: DOCKERHUB_USERNAME username_secret: DOCKERHUB_USERNAME
password_secret: DOCKERHUB_TOKEN password_secret: DOCKERHUB_TOKEN
- image: henrygd/beszel-agent - image: henrygd/beszel-agent
context: ./ context: ./
dockerfile: ./src/dockerfile_agent dockerfile: ./internal/dockerfile_agent
registry: docker.io registry: docker.io
username_secret: DOCKERHUB_USERNAME username_secret: DOCKERHUB_USERNAME
password_secret: DOCKERHUB_TOKEN password_secret: DOCKERHUB_TOKEN
- image: henrygd/beszel-agent-nvidia - image: henrygd/beszel-agent-nvidia
context: ./ context: ./
dockerfile: ./src/dockerfile_agent_nvidia dockerfile: ./internal/dockerfile_agent_nvidia
platforms: linux/amd64 platforms: linux/amd64
registry: docker.io registry: docker.io
username_secret: DOCKERHUB_USERNAME username_secret: DOCKERHUB_USERNAME
@@ -36,21 +36,21 @@ jobs:
- image: ghcr.io/${{ github.repository }}/beszel - image: ghcr.io/${{ github.repository }}/beszel
context: ./ context: ./
dockerfile: ./src/dockerfile_hub dockerfile: ./internal/dockerfile_hub
registry: ghcr.io registry: ghcr.io
username: ${{ github.actor }} username: ${{ github.actor }}
password_secret: GITHUB_TOKEN password_secret: GITHUB_TOKEN
- image: ghcr.io/${{ github.repository }}/beszel-agent - image: ghcr.io/${{ github.repository }}/beszel-agent
context: ./ context: ./
dockerfile: ./src/dockerfile_agent dockerfile: ./internal/dockerfile_agent
registry: ghcr.io registry: ghcr.io
username: ${{ github.actor }} username: ${{ github.actor }}
password_secret: GITHUB_TOKEN password_secret: GITHUB_TOKEN
- image: ghcr.io/${{ github.repository }}/beszel-agent-nvidia - image: ghcr.io/${{ github.repository }}/beszel-agent-nvidia
context: ./ context: ./
dockerfile: ./src/dockerfile_agent_nvidia dockerfile: ./internal/dockerfile_agent_nvidia
platforms: linux/amd64 platforms: linux/amd64
registry: ghcr.io registry: ghcr.io
username: ${{ github.actor }} username: ${{ github.actor }}
@@ -68,10 +68,10 @@ jobs:
uses: oven-sh/setup-bun@v2 uses: oven-sh/setup-bun@v2
- name: Install dependencies - name: Install dependencies
run: bun install --no-save --cwd ./src/site run: bun install --no-save --cwd ./internal/site
- name: Build site - name: Build site
run: bun run --cwd ./src/site build run: bun run --cwd ./internal/site build
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v3 uses: docker/setup-qemu-action@v3

View File

@@ -21,10 +21,10 @@ jobs:
uses: oven-sh/setup-bun@v2 uses: oven-sh/setup-bun@v2
- name: Install dependencies - name: Install dependencies
run: bun install --no-save --cwd ./src/site run: bun install --no-save --cwd ./internal/site
- name: Build site - name: Build site
run: bun run --cwd ./src/site build run: bun run --cwd ./internal/site build
- name: Set up Go - name: Set up Go
uses: actions/setup-go@v5 uses: actions/setup-go@v5

6
.gitignore vendored
View File

@@ -8,13 +8,13 @@ beszel_data
beszel_data* beszel_data*
dist dist
*.exe *.exe
src/cmd/hub/hub internal/cmd/hub/hub
src/cmd/agent/agent internal/cmd/agent/agent
node_modules node_modules
build build
*timestamp* *timestamp*
.swc .swc
src/site/src/locales/**/*.ts internal/site/src/locales/**/*.ts
*.bak *.bak
__debug_* __debug_*
agent/lhm/obj agent/lhm/obj

View File

@@ -9,7 +9,7 @@ before:
builds: builds:
- id: beszel - id: beszel
binary: beszel binary: beszel
main: src/cmd/hub/hub.go main: internal/cmd/hub/hub.go
env: env:
- CGO_ENABLED=0 - CGO_ENABLED=0
goos: goos:
@@ -22,7 +22,7 @@ builds:
- id: beszel-agent - id: beszel-agent
binary: beszel-agent binary: beszel-agent
main: src/cmd/agent/agent.go main: internal/cmd/agent/agent.go
env: env:
- CGO_ENABLED=0 - CGO_ENABLED=0
goos: goos:

View File

@@ -26,11 +26,11 @@ tidy:
build-web-ui: build-web-ui:
@if command -v bun >/dev/null 2>&1; then \ @if command -v bun >/dev/null 2>&1; then \
bun install --cwd ./src/site && \ bun install --cwd ./internal/site && \
bun run --cwd ./src/site build; \ bun run --cwd ./internal/site build; \
else \ else \
npm install --prefix ./src/site && \ npm install --prefix ./internal/site && \
npm run --prefix ./src/site build; \ npm run --prefix ./internal/site build; \
fi fi
# Conditional .NET build - only for Windows # Conditional .NET build - only for Windows
@@ -48,45 +48,45 @@ build-dotnet-conditional:
# Update build-agent to include conditional .NET build # Update build-agent to include conditional .NET build
build-agent: tidy build-dotnet-conditional build-agent: tidy build-dotnet-conditional
GOOS=$(OS) GOARCH=$(ARCH) go build -o ./build/beszel-agent_$(OS)_$(ARCH)$(EXE_EXT) -ldflags "-w -s" ./src/cmd/agent GOOS=$(OS) GOARCH=$(ARCH) go build -o ./build/beszel-agent_$(OS)_$(ARCH)$(EXE_EXT) -ldflags "-w -s" ./internal/cmd/agent
build-hub: tidy $(if $(filter false,$(SKIP_WEB)),build-web-ui) build-hub: tidy $(if $(filter false,$(SKIP_WEB)),build-web-ui)
GOOS=$(OS) GOARCH=$(ARCH) go build -o ./build/beszel_$(OS)_$(ARCH)$(EXE_EXT) -ldflags "-w -s" ./src/cmd/hub GOOS=$(OS) GOARCH=$(ARCH) go build -o ./build/beszel_$(OS)_$(ARCH)$(EXE_EXT) -ldflags "-w -s" ./internal/cmd/hub
build-hub-dev: tidy build-hub-dev: tidy
mkdir -p ./src/site/dist && touch ./src/site/dist/index.html mkdir -p ./internal/site/dist && touch ./internal/site/dist/index.html
GOOS=$(OS) GOARCH=$(ARCH) go build -tags development -o ./build/beszel-dev_$(OS)_$(ARCH)$(EXE_EXT) -ldflags "-w -s" ./src/cmd/hub GOOS=$(OS) GOARCH=$(ARCH) go build -tags development -o ./build/beszel-dev_$(OS)_$(ARCH)$(EXE_EXT) -ldflags "-w -s" ./internal/cmd/hub
build: build-agent build-hub build: build-agent build-hub
generate-locales: generate-locales:
@if [ ! -f ./src/site/src/locales/en/en.ts ]; then \ @if [ ! -f ./internal/site/src/locales/en/en.ts ]; then \
echo "Generating locales..."; \ echo "Generating locales..."; \
command -v bun >/dev/null 2>&1 && cd ./src/site && bun install && bun run sync || cd ./src/site && npm install && npm run sync; \ command -v bun >/dev/null 2>&1 && cd ./internal/site && bun install && bun run sync || cd ./internal/site && npm install && npm run sync; \
fi fi
dev-server: generate-locales dev-server: generate-locales
cd ./src/site cd ./internal/site
@if command -v bun >/dev/null 2>&1; then \ @if command -v bun >/dev/null 2>&1; then \
cd ./src/site && bun run dev --host 0.0.0.0; \ cd ./internal/site && bun run dev --host 0.0.0.0; \
else \ else \
cd ./src/site && npm run dev --host 0.0.0.0; \ cd ./internal/site && npm run dev --host 0.0.0.0; \
fi fi
dev-hub: export ENV=dev dev-hub: export ENV=dev
dev-hub: dev-hub:
mkdir -p ./src/site/dist && touch ./src/site/dist/index.html mkdir -p ./internal/site/dist && touch ./internal/site/dist/index.html
@if command -v entr >/dev/null 2>&1; then \ @if command -v entr >/dev/null 2>&1; then \
find ./src/cmd/hub/*.go ./src/{alerts,hub,records,users}/*.go | entr -r -s "cd ./src/cmd/hub && go run -tags development . serve --http 0.0.0.0:8090"; \ find ./internal/cmd/hub/*.go ./internal/{alerts,hub,records,users}/*.go | entr -r -s "cd ./internal/cmd/hub && go run -tags development . serve --http 0.0.0.0:8090"; \
else \ else \
cd ./src/cmd/hub && go run -tags development . serve --http 0.0.0.0:8090; \ cd ./internal/cmd/hub && go run -tags development . serve --http 0.0.0.0:8090; \
fi fi
dev-agent: dev-agent:
@if command -v entr >/dev/null 2>&1; then \ @if command -v entr >/dev/null 2>&1; then \
find ./src/cmd/agent/*.go ./agent/*.go | entr -r go run github.com/henrygd/beszel/src/cmd/agent; \ find ./internal/cmd/agent/*.go ./agent/*.go | entr -r go run github.com/henrygd/beszel/internal/cmd/agent; \
else \ else \
go run github.com/henrygd/beszel/src/cmd/agent; \ go run github.com/henrygd/beszel/internal/cmd/agent; \
fi fi
build-dotnet: build-dotnet:

View File

@@ -1,4 +1,7 @@
// Package agent handles the agent's SSH server and system stats collection. // Package agent implements the Beszel monitoring agent that collects and serves system metrics.
//
// The agent runs on monitored systems and communicates collected data
// to the Beszel hub for centralized monitoring and alerting.
package agent package agent
import ( import (
@@ -13,7 +16,7 @@ import (
"github.com/gliderlabs/ssh" "github.com/gliderlabs/ssh"
"github.com/henrygd/beszel" "github.com/henrygd/beszel"
"github.com/henrygd/beszel/src/entities/system" "github.com/henrygd/beszel/internal/entities/system"
"github.com/shirou/gopsutil/v4/host" "github.com/shirou/gopsutil/v4/host"
gossh "golang.org/x/crypto/ssh" gossh "golang.org/x/crypto/ssh"
) )

View File

@@ -3,7 +3,7 @@ package agent
import ( import (
"time" "time"
"github.com/henrygd/beszel/src/entities/system" "github.com/henrygd/beszel/internal/entities/system"
) )
// Not thread safe since we only access from gatherStats which is already locked // Not thread safe since we only access from gatherStats which is already locked

View File

@@ -8,7 +8,7 @@ import (
"testing/synctest" "testing/synctest"
"time" "time"
"github.com/henrygd/beszel/src/entities/system" "github.com/henrygd/beszel/internal/entities/system"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"

View File

@@ -20,9 +20,8 @@ func HasReadableBattery() bool {
} }
haveCheckedBattery = true haveCheckedBattery = true
bat, err := battery.Get(0) bat, err := battery.Get(0)
if err == nil && bat != nil { systemHasBattery = err == nil && bat != nil && bat.Design != 0 && bat.Full != 0
systemHasBattery = true if !systemHasBattery {
} else {
slog.Debug("No battery found", "err", err) slog.Debug("No battery found", "err", err)
} }
return systemHasBattery return systemHasBattery

View File

@@ -14,7 +14,7 @@ import (
"time" "time"
"github.com/henrygd/beszel" "github.com/henrygd/beszel"
"github.com/henrygd/beszel/src/common" "github.com/henrygd/beszel/internal/common"
"github.com/fxamacker/cbor/v2" "github.com/fxamacker/cbor/v2"
"github.com/lxzan/gws" "github.com/lxzan/gws"
@@ -85,7 +85,7 @@ func getToken() (string, error) {
if err != nil { if err != nil {
return "", err return "", err
} }
return string(tokenBytes), nil return strings.TrimSpace(string(tokenBytes)), nil
} }
// getOptions returns the WebSocket client options, creating them if necessary. // getOptions returns the WebSocket client options, creating them if necessary.

View File

@@ -13,7 +13,7 @@ import (
"github.com/henrygd/beszel" "github.com/henrygd/beszel"
"github.com/henrygd/beszel/src/common" "github.com/henrygd/beszel/internal/common"
"github.com/fxamacker/cbor/v2" "github.com/fxamacker/cbor/v2"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@@ -537,4 +537,25 @@ func TestGetToken(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, "", token, "Empty file should return empty string") assert.Equal(t, "", token, "Empty file should return empty string")
}) })
t.Run("strips whitespace from TOKEN_FILE", func(t *testing.T) {
unsetEnvVars()
tokenWithWhitespace := " test-token-with-whitespace \n\t"
expectedToken := "test-token-with-whitespace"
tokenFile, err := os.CreateTemp("", "token-test-*.txt")
require.NoError(t, err)
defer os.Remove(tokenFile.Name())
_, err = tokenFile.WriteString(tokenWithWhitespace)
require.NoError(t, err)
tokenFile.Close()
os.Setenv("TOKEN_FILE", tokenFile.Name())
defer os.Unsetenv("TOKEN_FILE")
token, err := getToken()
assert.NoError(t, err)
assert.Equal(t, expectedToken, token, "Whitespace should be stripped from token file content")
})
} }

View File

@@ -8,7 +8,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/henrygd/beszel/src/entities/system" "github.com/henrygd/beszel/internal/entities/system"
"github.com/shirou/gopsutil/v4/disk" "github.com/shirou/gopsutil/v4/disk"
) )

View File

@@ -14,7 +14,7 @@ import (
"sync" "sync"
"time" "time"
"github.com/henrygd/beszel/src/entities/container" "github.com/henrygd/beszel/internal/entities/container"
"github.com/blang/semver" "github.com/blang/semver"
) )

View File

@@ -12,7 +12,7 @@ import (
"sync" "sync"
"time" "time"
"github.com/henrygd/beszel/src/entities/system" "github.com/henrygd/beszel/internal/entities/system"
"golang.org/x/exp/slog" "golang.org/x/exp/slog"
) )

View File

@@ -9,7 +9,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/henrygd/beszel/src/entities/system" "github.com/henrygd/beszel/internal/entities/system"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"

View File

@@ -10,7 +10,7 @@ import (
"strings" "strings"
"unicode/utf8" "unicode/utf8"
"github.com/henrygd/beszel/src/entities/system" "github.com/henrygd/beszel/internal/entities/system"
"github.com/shirou/gopsutil/v4/common" "github.com/shirou/gopsutil/v4/common"
"github.com/shirou/gopsutil/v4/sensors" "github.com/shirou/gopsutil/v4/sensors"

View File

@@ -9,7 +9,7 @@ import (
"os" "os"
"testing" "testing"
"github.com/henrygd/beszel/src/entities/system" "github.com/henrygd/beszel/internal/entities/system"
"github.com/shirou/gopsutil/v4/common" "github.com/shirou/gopsutil/v4/common"
"github.com/shirou/gopsutil/v4/sensors" "github.com/shirou/gopsutil/v4/sensors"

View File

@@ -12,8 +12,8 @@ import (
"time" "time"
"github.com/henrygd/beszel" "github.com/henrygd/beszel"
"github.com/henrygd/beszel/src/common" "github.com/henrygd/beszel/internal/common"
"github.com/henrygd/beszel/src/entities/system" "github.com/henrygd/beszel/internal/entities/system"
"github.com/blang/semver" "github.com/blang/semver"
"github.com/fxamacker/cbor/v2" "github.com/fxamacker/cbor/v2"

View File

@@ -13,8 +13,8 @@ import (
"testing" "testing"
"time" "time"
"github.com/henrygd/beszel/src/entities/container" "github.com/henrygd/beszel/internal/entities/container"
"github.com/henrygd/beszel/src/entities/system" "github.com/henrygd/beszel/internal/entities/system"
"github.com/blang/semver" "github.com/blang/semver"
"github.com/fxamacker/cbor/v2" "github.com/fxamacker/cbor/v2"

View File

@@ -11,7 +11,7 @@ import (
"github.com/henrygd/beszel" "github.com/henrygd/beszel"
"github.com/henrygd/beszel/agent/battery" "github.com/henrygd/beszel/agent/battery"
"github.com/henrygd/beszel/src/entities/system" "github.com/henrygd/beszel/internal/entities/system"
"github.com/shirou/gopsutil/v4/cpu" "github.com/shirou/gopsutil/v4/cpu"
"github.com/shirou/gopsutil/v4/disk" "github.com/shirou/gopsutil/v4/disk"

View File

@@ -8,7 +8,7 @@ import (
"runtime" "runtime"
"strings" "strings"
"github.com/henrygd/beszel/src/ghupdate" "github.com/henrygd/beszel/internal/ghupdate"
) )
// restarter knows how to restart the beszel-agent service. // restarter knows how to restart the beszel-agent service.

View File

@@ -1,10 +1,15 @@
// Package beszel provides core application constants and version information
// which are used throughout the application.
package beszel package beszel
import "github.com/blang/semver" import "github.com/blang/semver"
const ( const (
// Version is the current version of the application.
Version = "0.12.7" Version = "0.12.7"
// AppName is the name of the application.
AppName = "beszel" AppName = "beszel"
) )
// MinVersionCbor is the minimum supported version for CBOR compatibility.
var MinVersionCbor = semver.MustParse("0.12.0") var MinVersionCbor = semver.MustParse("0.12.0")

View File

@@ -1,3 +1,3 @@
files: files:
- source: /beszel/site/src/locales/en/en.po - source: /internal/site/src/locales/en/
translation: /beszel/site/src/locales/%two_letters_code%/%two_letters_code%.po translation: /internal/site/src/locales/%two_letters_code%/%two_letters_code%.po

View File

@@ -6,7 +6,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/henrygd/beszel/src/entities/system" "github.com/henrygd/beszel/internal/entities/system"
"github.com/pocketbase/dbx" "github.com/pocketbase/dbx"
"github.com/pocketbase/pocketbase/core" "github.com/pocketbase/pocketbase/core"

View File

@@ -13,7 +13,7 @@ import (
"testing/synctest" "testing/synctest"
"time" "time"
beszelTests "github.com/henrygd/beszel/src/tests" beszelTests "github.com/henrygd/beszel/internal/tests"
"github.com/pocketbase/dbx" "github.com/pocketbase/dbx"
"github.com/pocketbase/pocketbase/core" "github.com/pocketbase/pocketbase/core"

View File

@@ -8,8 +8,8 @@ import (
"time" "time"
"github.com/henrygd/beszel" "github.com/henrygd/beszel"
"github.com/henrygd/beszel/src/hub" "github.com/henrygd/beszel/internal/hub"
_ "github.com/henrygd/beszel/src/migrations" _ "github.com/henrygd/beszel/internal/migrations"
"github.com/pocketbase/pocketbase" "github.com/pocketbase/pocketbase"
"github.com/pocketbase/pocketbase/plugins/migratecmd" "github.com/pocketbase/pocketbase/plugins/migratecmd"

View File

@@ -10,7 +10,7 @@ COPY . ./
# Build # Build
ARG TARGETOS TARGETARCH ARG TARGETOS TARGETARCH
RUN CGO_ENABLED=0 GOGC=75 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -ldflags "-w -s" -o /agent ./src/cmd/agent RUN CGO_ENABLED=0 GOGC=75 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -ldflags "-w -s" -o /agent ./internal/cmd/agent
RUN rm -rf /tmp/* RUN rm -rf /tmp/*

View File

@@ -17,7 +17,7 @@ RUN update-ca-certificates
# Build # Build
ARG TARGETOS TARGETARCH ARG TARGETOS TARGETARCH
RUN CGO_ENABLED=0 GOGC=75 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -ldflags "-w -s" -o /beszel ./src/cmd/hub RUN CGO_ENABLED=0 GOGC=75 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -ldflags "-w -s" -o /beszel ./internal/cmd/hub
# ? ------------------------- # ? -------------------------
FROM scratch FROM scratch

View File

@@ -5,7 +5,7 @@ package system
import ( import (
"time" "time"
"github.com/henrygd/beszel/src/entities/container" "github.com/henrygd/beszel/internal/entities/container"
) )
type Stats struct { type Stats struct {

View File

@@ -8,9 +8,9 @@ import (
"sync" "sync"
"time" "time"
"github.com/henrygd/beszel/src/common" "github.com/henrygd/beszel/internal/common"
"github.com/henrygd/beszel/src/hub/expirymap" "github.com/henrygd/beszel/internal/hub/expirymap"
"github.com/henrygd/beszel/src/hub/ws" "github.com/henrygd/beszel/internal/hub/ws"
"github.com/blang/semver" "github.com/blang/semver"
"github.com/lxzan/gws" "github.com/lxzan/gws"

View File

@@ -15,8 +15,8 @@ import (
"time" "time"
"github.com/henrygd/beszel/agent" "github.com/henrygd/beszel/agent"
"github.com/henrygd/beszel/src/common" "github.com/henrygd/beszel/internal/common"
"github.com/henrygd/beszel/src/hub/ws" "github.com/henrygd/beszel/internal/hub/ws"
"github.com/pocketbase/pocketbase/core" "github.com/pocketbase/pocketbase/core"
pbtests "github.com/pocketbase/pocketbase/tests" pbtests "github.com/pocketbase/pocketbase/tests"

View File

@@ -8,7 +8,7 @@ import (
"path/filepath" "path/filepath"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/henrygd/beszel/src/entities/system" "github.com/henrygd/beszel/internal/entities/system"
"github.com/pocketbase/dbx" "github.com/pocketbase/dbx"
"github.com/pocketbase/pocketbase/core" "github.com/pocketbase/pocketbase/core"

View File

@@ -8,9 +8,9 @@ import (
"path/filepath" "path/filepath"
"testing" "testing"
"github.com/henrygd/beszel/src/tests" "github.com/henrygd/beszel/internal/tests"
"github.com/henrygd/beszel/src/hub/config" "github.com/henrygd/beszel/internal/hub/config"
"github.com/pocketbase/pocketbase/core" "github.com/pocketbase/pocketbase/core"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"

View File

@@ -13,11 +13,11 @@ import (
"time" "time"
"github.com/henrygd/beszel" "github.com/henrygd/beszel"
"github.com/henrygd/beszel/src/alerts" "github.com/henrygd/beszel/internal/alerts"
"github.com/henrygd/beszel/src/hub/config" "github.com/henrygd/beszel/internal/hub/config"
"github.com/henrygd/beszel/src/hub/systems" "github.com/henrygd/beszel/internal/hub/systems"
"github.com/henrygd/beszel/src/records" "github.com/henrygd/beszel/internal/records"
"github.com/henrygd/beszel/src/users" "github.com/henrygd/beszel/internal/users"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/pocketbase/pocketbase" "github.com/pocketbase/pocketbase"
@@ -69,6 +69,8 @@ func (h *Hub) StartHub() error {
if err := config.SyncSystems(e); err != nil { if err := config.SyncSystems(e); err != nil {
return err return err
} }
// register middlewares
h.registerMiddlewares(e)
// register api routes // register api routes
if err := h.registerApiRoutes(e); err != nil { if err := h.registerApiRoutes(e); err != nil {
return err return err
@@ -171,6 +173,37 @@ func (h *Hub) registerCronJobs(_ *core.ServeEvent) error {
return nil return nil
} }
// custom middlewares
func (h *Hub) registerMiddlewares(se *core.ServeEvent) {
// authorizes request with user matching the provided email
authorizeRequestWithEmail := func(e *core.RequestEvent, email string) (err error) {
if e.Auth != nil || email == "" {
return e.Next()
}
isAuthRefresh := e.Request.URL.Path == "/api/collections/users/auth-refresh" && e.Request.Method == http.MethodPost
e.Auth, err = e.App.FindFirstRecordByData("users", "email", email)
if err != nil || !isAuthRefresh {
return e.Next()
}
// auth refresh endpoint, make sure token is set in header
token, _ := e.Auth.NewAuthToken()
e.Request.Header.Set("Authorization", token)
return e.Next()
}
// authenticate with trusted header
if autoLogin, _ := GetEnv("AUTO_LOGIN"); autoLogin != "" {
se.Router.BindFunc(func(e *core.RequestEvent) error {
return authorizeRequestWithEmail(e, autoLogin)
})
}
// authenticate with trusted header
if trustedHeader, _ := GetEnv("TRUSTED_AUTH_HEADER"); trustedHeader != "" {
se.Router.BindFunc(func(e *core.RequestEvent) error {
return authorizeRequestWithEmail(e, e.Request.Header.Get(trustedHeader))
})
}
}
// custom api routes // custom api routes
func (h *Hub) registerApiRoutes(se *core.ServeEvent) error { func (h *Hub) registerApiRoutes(se *core.ServeEvent) error {
// auth protected routes // auth protected routes

View File

@@ -15,8 +15,8 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/henrygd/beszel/src/migrations" "github.com/henrygd/beszel/internal/migrations"
beszelTests "github.com/henrygd/beszel/src/tests" beszelTests "github.com/henrygd/beszel/internal/tests"
"github.com/pocketbase/pocketbase/core" "github.com/pocketbase/pocketbase/core"
pbTests "github.com/pocketbase/pocketbase/tests" pbTests "github.com/pocketbase/pocketbase/tests"
@@ -711,3 +711,117 @@ func TestCreateUserEndpointAvailability(t *testing.T) {
scenario.Test(t) scenario.Test(t)
}) })
} }
func TestAutoLoginMiddleware(t *testing.T) {
var hubs []*beszelTests.TestHub
defer func() {
defer os.Unsetenv("AUTO_LOGIN")
for _, hub := range hubs {
hub.Cleanup()
}
}()
os.Setenv("AUTO_LOGIN", "user@test.com")
testAppFactory := func(t testing.TB) *pbTests.TestApp {
hub, _ := beszelTests.NewTestHub(t.TempDir())
hubs = append(hubs, hub)
hub.StartHub()
return hub.TestApp
}
scenarios := []beszelTests.ApiScenario{
{
Name: "GET /getkey - without auto login should fail",
Method: http.MethodGet,
URL: "/api/beszel/getkey",
ExpectedStatus: 401,
ExpectedContent: []string{"requires valid"},
TestAppFactory: testAppFactory,
},
{
Name: "GET /getkey - with auto login should fail if no matching user",
Method: http.MethodGet,
URL: "/api/beszel/getkey",
ExpectedStatus: 401,
ExpectedContent: []string{"requires valid"},
TestAppFactory: testAppFactory,
},
{
Name: "GET /getkey - with auto login should succeed",
Method: http.MethodGet,
URL: "/api/beszel/getkey",
ExpectedStatus: 200,
ExpectedContent: []string{"\"key\":", "\"v\":"},
TestAppFactory: testAppFactory,
BeforeTestFunc: func(t testing.TB, app *pbTests.TestApp, e *core.ServeEvent) {
beszelTests.CreateUser(app, "user@test.com", "password123")
},
},
}
for _, scenario := range scenarios {
scenario.Test(t)
}
}
func TestTrustedHeaderMiddleware(t *testing.T) {
var hubs []*beszelTests.TestHub
defer func() {
defer os.Unsetenv("TRUSTED_AUTH_HEADER")
for _, hub := range hubs {
hub.Cleanup()
}
}()
os.Setenv("TRUSTED_AUTH_HEADER", "X-Beszel-Trusted")
testAppFactory := func(t testing.TB) *pbTests.TestApp {
hub, _ := beszelTests.NewTestHub(t.TempDir())
hubs = append(hubs, hub)
hub.StartHub()
return hub.TestApp
}
scenarios := []beszelTests.ApiScenario{
{
Name: "GET /getkey - without trusted header should fail",
Method: http.MethodGet,
URL: "/api/beszel/getkey",
ExpectedStatus: 401,
ExpectedContent: []string{"requires valid"},
TestAppFactory: testAppFactory,
},
{
Name: "GET /getkey - with trusted header should fail if no matching user",
Method: http.MethodGet,
URL: "/api/beszel/getkey",
Headers: map[string]string{
"X-Beszel-Trusted": "user@test.com",
},
ExpectedStatus: 401,
ExpectedContent: []string{"requires valid"},
TestAppFactory: testAppFactory,
},
{
Name: "GET /getkey - with trusted header should succeed",
Method: http.MethodGet,
URL: "/api/beszel/getkey",
Headers: map[string]string{
"X-Beszel-Trusted": "user@test.com",
},
ExpectedStatus: 200,
ExpectedContent: []string{"\"key\":", "\"v\":"},
TestAppFactory: testAppFactory,
BeforeTestFunc: func(t testing.TB, app *pbTests.TestApp, e *core.ServeEvent) {
beszelTests.CreateUser(app, "user@test.com", "password123")
},
},
}
for _, scenario := range scenarios {
scenario.Test(t)
}
}

View File

@@ -3,7 +3,7 @@
package hub package hub
import "github.com/henrygd/beszel/src/hub/systems" import "github.com/henrygd/beszel/internal/hub/systems"
// TESTING ONLY: GetSystemManager returns the system manager // TESTING ONLY: GetSystemManager returns the system manager
func (h *Hub) GetSystemManager() *systems.SystemManager { func (h *Hub) GetSystemManager() *systems.SystemManager {

View File

@@ -9,7 +9,7 @@ import (
"strings" "strings"
"github.com/henrygd/beszel" "github.com/henrygd/beszel"
"github.com/henrygd/beszel/src/site" "github.com/henrygd/beszel/internal/site"
"github.com/pocketbase/pocketbase/apis" "github.com/pocketbase/pocketbase/apis"
"github.com/pocketbase/pocketbase/core" "github.com/pocketbase/pocketbase/core"

View File

@@ -10,9 +10,9 @@ import (
"strings" "strings"
"time" "time"
"github.com/henrygd/beszel/src/hub/ws" "github.com/henrygd/beszel/internal/hub/ws"
"github.com/henrygd/beszel/src/entities/system" "github.com/henrygd/beszel/internal/entities/system"
"github.com/henrygd/beszel" "github.com/henrygd/beszel"

View File

@@ -5,11 +5,11 @@ import (
"fmt" "fmt"
"time" "time"
"github.com/henrygd/beszel/src/hub/ws" "github.com/henrygd/beszel/internal/hub/ws"
"github.com/henrygd/beszel/src/entities/system" "github.com/henrygd/beszel/internal/entities/system"
"github.com/henrygd/beszel/src/common" "github.com/henrygd/beszel/internal/common"
"github.com/henrygd/beszel" "github.com/henrygd/beszel"

View File

@@ -10,10 +10,10 @@ import (
"testing/synctest" "testing/synctest"
"time" "time"
"github.com/henrygd/beszel/src/entities/container" "github.com/henrygd/beszel/internal/entities/container"
"github.com/henrygd/beszel/src/entities/system" "github.com/henrygd/beszel/internal/entities/system"
"github.com/henrygd/beszel/src/hub/systems" "github.com/henrygd/beszel/internal/hub/systems"
"github.com/henrygd/beszel/src/tests" "github.com/henrygd/beszel/internal/tests"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"

View File

@@ -7,7 +7,7 @@ import (
"context" "context"
"fmt" "fmt"
entities "github.com/henrygd/beszel/src/entities/system" entities "github.com/henrygd/beszel/internal/entities/system"
) )
// TESTING ONLY: GetSystemCount returns the number of systems in the store // TESTING ONLY: GetSystemCount returns the number of systems in the store

View File

@@ -6,7 +6,7 @@ import (
"os" "os"
"os/exec" "os/exec"
"github.com/henrygd/beszel/src/ghupdate" "github.com/henrygd/beszel/internal/ghupdate"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )

View File

@@ -5,9 +5,9 @@ import (
"time" "time"
"weak" "weak"
"github.com/henrygd/beszel/src/entities/system" "github.com/henrygd/beszel/internal/entities/system"
"github.com/henrygd/beszel/src/common" "github.com/henrygd/beszel/internal/common"
"github.com/fxamacker/cbor/v2" "github.com/fxamacker/cbor/v2"
"github.com/lxzan/gws" "github.com/lxzan/gws"

View File

@@ -8,7 +8,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/henrygd/beszel/src/common" "github.com/henrygd/beszel/internal/common"
"github.com/fxamacker/cbor/v2" "github.com/fxamacker/cbor/v2"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"

View File

@@ -9,8 +9,8 @@ import (
"strings" "strings"
"time" "time"
"github.com/henrygd/beszel/src/entities/container" "github.com/henrygd/beszel/internal/entities/container"
"github.com/henrygd/beszel/src/entities/system" "github.com/henrygd/beszel/internal/entities/system"
"github.com/pocketbase/dbx" "github.com/pocketbase/dbx"
"github.com/pocketbase/pocketbase/core" "github.com/pocketbase/pocketbase/core"

View File

@@ -8,8 +8,8 @@ import (
"testing" "testing"
"time" "time"
"github.com/henrygd/beszel/src/records" "github.com/henrygd/beszel/internal/records"
"github.com/henrygd/beszel/src/tests" "github.com/henrygd/beszel/internal/tests"
"github.com/pocketbase/dbx" "github.com/pocketbase/dbx"
"github.com/pocketbase/pocketbase/core" "github.com/pocketbase/pocketbase/core"

View File

@@ -17,7 +17,10 @@
"linter": { "linter": {
"enabled": true, "enabled": true,
"rules": { "rules": {
"recommended": true "recommended": true,
"correctness": {
"useUniqueElementIds": "off"
}
} }
}, },
"javascript": { "javascript": {
@@ -35,4 +38,4 @@
} }
} }
} }
} }

View File

Before

Width:  |  Height:  |  Size: 906 B

After

Width:  |  Height:  |  Size: 906 B

View File

Before

Width:  |  Height:  |  Size: 906 B

After

Width:  |  Height:  |  Size: 906 B

View File

Before

Width:  |  Height:  |  Size: 903 B

After

Width:  |  Height:  |  Size: 903 B

View File

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

@@ -22,7 +22,7 @@ import { memo, useEffect, useRef, useState } from "react"
import { $router, basePath, Link, navigate } from "./router" import { $router, basePath, Link, navigate } from "./router"
import { SystemRecord } from "@/types" import { SystemRecord } from "@/types"
import { SystemStatus } from "@/lib/enums" import { SystemStatus } from "@/lib/enums"
import { AppleIcon, DockerIcon, TuxIcon, WindowsIcon } from "./ui/icons" import { AppleIcon, DockerIcon, FreeBsdIcon, TuxIcon, WindowsIcon } from "./ui/icons"
import { InputCopy } from "./ui/input-copy" import { InputCopy } from "./ui/input-copy"
import { getPagePath } from "@nanostores/router" import { getPagePath } from "@nanostores/router"
import { import {
@@ -253,6 +253,12 @@ export const SystemDialog = ({ setOpen, system }: { setOpen: (open: boolean) =>
copyWindowsCommand(isUnixSocket ? hostValue : port.current?.value, publicKey, token), copyWindowsCommand(isUnixSocket ? hostValue : port.current?.value, publicKey, token),
icons: [WindowsIcon], icons: [WindowsIcon],
}, },
{
text: t({ message: "FreeBSD command", context: "Button to copy install command" }),
onClick: async () =>
copyLinuxCommand(isUnixSocket ? hostValue : port.current?.value, publicKey, token),
icons: [FreeBsdIcon],
},
{ {
text: t`Manual setup instructions`, text: t`Manual setup instructions`,
url: "https://beszel.dev/guide/agent-installation#binary", url: "https://beszel.dev/guide/agent-installation#binary",

View File

@@ -1,11 +1,11 @@
import { Area, AreaChart, CartesianGrid, YAxis } from "recharts" import { Area, AreaChart, CartesianGrid, YAxis } from "recharts"
import { ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent, xAxis } from "@/components/ui/chart" import { type ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent, xAxis } from "@/components/ui/chart"
import { memo, useMemo } from "react" import { memo, useMemo } from "react"
import { cn, formatShortDate, chartMargin, toFixedFloat, formatBytes, decimalString } from "@/lib/utils" import { cn, formatShortDate, chartMargin, toFixedFloat, formatBytes, decimalString } from "@/lib/utils"
// import Spinner from '../spinner' // import Spinner from '../spinner'
import { useStore } from "@nanostores/react" import { useStore } from "@nanostores/react"
import { $containerFilter, $userSettings } from "@/lib/stores" import { $containerFilter, $userSettings } from "@/lib/stores"
import { ChartData } from "@/types" import type { ChartData } from "@/types"
import { Separator } from "../ui/separator" import { Separator } from "../ui/separator"
import { ChartType, Unit } from "@/lib/enums" import { ChartType, Unit } from "@/lib/enums"
import { useYAxisWidth } from "./hooks" import { useYAxisWidth } from "./hooks"
@@ -31,6 +31,7 @@ export default memo(function ContainerChart({
const isNetChart = chartType === ChartType.Network const isNetChart = chartType === ChartType.Network
// biome-ignore lint/correctness/useExhaustiveDependencies: not necessary
const { toolTipFormatter, dataFunction, tickFormatter } = useMemo(() => { const { toolTipFormatter, dataFunction, tickFormatter } = useMemo(() => {
const obj = {} as { const obj = {} as {
toolTipFormatter: (item: any, key: string) => React.ReactNode | string toolTipFormatter: (item: any, key: string) => React.ReactNode | string
@@ -47,7 +48,7 @@ export default memo(function ContainerChart({
const chartUnit = isNetChart ? userSettings.unitNet : Unit.Bytes const chartUnit = isNetChart ? userSettings.unitNet : Unit.Bytes
obj.tickFormatter = (val) => { obj.tickFormatter = (val) => {
const { value, unit } = formatBytes(val, isNetChart, chartUnit, true) const { value, unit } = formatBytes(val, isNetChart, chartUnit, true)
return updateYAxisWidth(toFixedFloat(value, value >= 10 ? 0 : 1) + " " + unit) return updateYAxisWidth(`${toFixedFloat(value, value >= 10 ? 0 : 1)} ${unit}`)
} }
} }
// tooltip formatter // tooltip formatter
@@ -74,10 +75,10 @@ export default memo(function ContainerChart({
} else if (chartType === ChartType.Memory) { } else if (chartType === ChartType.Memory) {
obj.toolTipFormatter = (item: any) => { obj.toolTipFormatter = (item: any) => {
const { value, unit } = formatBytes(item.value, false, Unit.Bytes, true) const { value, unit } = formatBytes(item.value, false, Unit.Bytes, true)
return decimalString(value) + " " + unit return `${decimalString(value)} ${unit}`
} }
} else { } else {
obj.toolTipFormatter = (item: any) => decimalString(item.value) + unit obj.toolTipFormatter = (item: any) => `${decimalString(item.value)} ${unit}`
} }
// data function // data function
if (isNetChart) { if (isNetChart) {
@@ -133,7 +134,7 @@ export default memo(function ContainerChart({
animationDuration={150} animationDuration={150}
truncate={true} truncate={true}
labelFormatter={(_, data) => formatShortDate(data[0].payload.created)} labelFormatter={(_, data) => formatShortDate(data[0].payload.created)}
// @ts-ignore // @ts-expect-error
itemSorter={(a, b) => b.value - a.value} itemSorter={(a, b) => b.value - a.value}
content={<ChartTooltipContent filter={filter} contentFormatter={toolTipFormatter} />} content={<ChartTooltipContent filter={filter} contentFormatter={toolTipFormatter} />}
/> />

Some files were not shown because too many files have changed in this diff Show More