Compare commits

...

4 Commits

Author SHA1 Message Date
henrygd
3ac0b185d1 fix oidc icon display issue (#990) 2025-07-25 13:55:02 -04:00
henrygd
1e675cabb5 refactor agent data directory resolution (#991) 2025-07-25 13:37:23 -04:00
henrygd
5f44965c2c improve table formatting and fix #983
- should fix NaN display bug for dasboard cpu
- standardize decimals for dash meters
2025-07-25 00:29:08 -04:00
henrygd
f080929296 Update goreleaser configuration
- Removed the name field for brew.
- Enabled pull requests for winget.
2025-07-24 22:31:27 -04:00
5 changed files with 53 additions and 51 deletions

View File

@@ -173,7 +173,6 @@ brews:
error_log_path "#{Dir.home}/.cache/beszel/beszel-agent.log" error_log_path "#{Dir.home}/.cache/beszel/beszel-agent.log"
keep_alive true keep_alive true
restart_delay 5 restart_delay 5
name beszel-agent
process_type :background process_type :background
winget: winget:
@@ -204,7 +203,7 @@ winget:
name: beszel-winget name: beszel-winget
branch: henrygd.beszel-agent-{{ .Version }} branch: henrygd.beszel-agent-{{ .Version }}
pull_request: pull_request:
enabled: false enabled: true
draft: false draft: false
base: base:
owner: microsoft owner: microsoft

View File

@@ -9,35 +9,30 @@ import (
) )
// getDataDir returns the path to the data directory for the agent and an error // 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 // if the directory is not valid. Attempts to find the optimal data directory if
// optimal data directory. // no data directories are provided.
func getDataDir(dataDir string) (string, error) { func getDataDir(dataDirs ...string) (string, error) {
if dataDir == "" { if len(dataDirs) > 0 {
dataDir, _ = GetEnv("DATA_DIR") return testDataDirs(dataDirs)
} }
dataDir, _ := GetEnv("DATA_DIR")
if dataDir != "" { if dataDir != "" {
return testDataDirs([]string{dataDir}) dataDirs = append(dataDirs, dataDir)
} }
var dirsToTry []string
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
dirsToTry = []string{ dataDirs = append(dataDirs,
filepath.Join(os.Getenv("APPDATA"), "beszel-agent"), filepath.Join(os.Getenv("APPDATA"), "beszel-agent"),
filepath.Join(os.Getenv("LOCALAPPDATA"), "beszel-agent"), filepath.Join(os.Getenv("LOCALAPPDATA"), "beszel-agent"),
} )
} else { } else {
homeDir, err := os.UserHomeDir() dataDirs = append(dataDirs, "/var/lib/beszel-agent")
if err != nil { if homeDir, err := os.UserHomeDir(); err == nil {
return "", err dataDirs = append(dataDirs, filepath.Join(homeDir, ".config", "beszel"))
}
dirsToTry = []string{
"/var/lib/beszel-agent",
filepath.Join(homeDir, ".config", "beszel"),
} }
} }
return testDataDirs(dirsToTry) return testDataDirs(dataDirs)
} }
func testDataDirs(paths []string) (string, error) { func testDataDirs(paths []string) (string, error) {

View File

@@ -44,15 +44,15 @@ func TestGetDataDir(t *testing.T) {
oldValue := os.Getenv("DATA_DIR") oldValue := os.Getenv("DATA_DIR")
defer func() { defer func() {
if oldValue == "" { if oldValue == "" {
os.Unsetenv("DATA_DIR") os.Unsetenv("BESZEL_AGENT_DATA_DIR")
} else { } 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) require.NoError(t, err)
assert.Equal(t, tempDir, result) 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 // 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 // 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 // We don't assert success/failure here since it depends on system permissions
// Just verify we get a string result if no error // Just verify we get a string result if no error
if err == nil { if err == nil {

View File

@@ -1,5 +1,5 @@
import { t } from "@lingui/core/macro"; import { t } from "@lingui/core/macro"
import { Trans } from "@lingui/react/macro"; import { Trans } from "@lingui/react/macro"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
import { buttonVariants } from "@/components/ui/button" import { buttonVariants } from "@/components/ui/button"
import { Input } from "@/components/ui/input" 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({ export function UserAuthForm({
className, className,
isFirstRun, isFirstRun,
@@ -165,8 +173,8 @@ export function UserAuthForm({
}, []) }, [])
return ( return (
<div className={cn("grid gap-6", className)} {...props}> <div className={cn("grid gap-6", className)} {...props}>
{passwordEnabled && ( {passwordEnabled && (
<> <>
<form onSubmit={handleSubmit} onChange={() => setErrors({})}> <form onSubmit={handleSubmit} onChange={() => setErrors({})}>
<div className="grid gap-2.5"> <div className="grid gap-2.5">
@@ -242,20 +250,20 @@ export function UserAuthForm({
</form> </form>
{(isFirstRun || oauthEnabled) && ( {(isFirstRun || oauthEnabled) && (
// only show 'continue with' during onboarding or if we have auth providers // only show 'continue with' during onboarding or if we have auth providers
(<div className="relative"> <div className="relative">
<div className="absolute inset-0 flex items-center"> <div className="absolute inset-0 flex items-center">
<span className="w-full border-t" /> <span className="w-full border-t" />
</div> </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"> <span className="bg-background px-2 text-muted-foreground">
<Trans>Or continue with</Trans> <Trans>Or continue with</Trans>
</span> </span>
</div> </div>
</div>) </div>
)} )}
</> </>
)} )}
{oauthEnabled && ( {oauthEnabled && (
<div className="grid gap-2 -mt-1"> <div className="grid gap-2 -mt-1">
{authMethods.oauth2.providers.map((provider) => ( {authMethods.oauth2.providers.map((provider) => (
<button <button
@@ -273,7 +281,7 @@ export function UserAuthForm({
) : ( ) : (
<img <img
className="me-2 h-4 w-4 dark:brightness-0 dark:invert" className="me-2 h-4 w-4 dark:brightness-0 dark:invert"
src={prependBasePath(`/_/images/oauth2/${provider.name}.svg`)} src={getAuthProviderIcon(provider)}
alt="" alt=""
// onError={(e) => { // onError={(e) => {
// e.currentTarget.src = "/static/lock.svg" // e.currentTarget.src = "/static/lock.svg"
@@ -285,16 +293,16 @@ export function UserAuthForm({
))} ))}
</div> </div>
)} )}
{!oauthEnabled && isFirstRun && ( {!oauthEnabled && isFirstRun && (
// only show GitHub button / dialog during onboarding // only show GitHub button / dialog during onboarding
(<Dialog> <Dialog>
<DialogTrigger asChild> <DialogTrigger asChild>
<button type="button" className={cn(buttonVariants({ variant: "outline" }))}> <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="" /> <img className="me-2 h-4 w-4 dark:invert" src={prependBasePath("/_/images/oauth2/github.svg")} alt="" />
<span className="translate-y-[1px]">GitHub</span> <span className="translate-y-[1px]">GitHub</span>
</button> </button>
</DialogTrigger> </DialogTrigger>
<DialogContent style={{ maxWidth: 440, width: "90%" }}> <DialogContent style={{ maxWidth: 440, width: "90%" }}>
<DialogHeader> <DialogHeader>
<DialogTitle> <DialogTitle>
<Trans>OAuth 2 / OIDC support</Trans> <Trans>OAuth 2 / OIDC support</Trans>
@@ -318,9 +326,9 @@ export function UserAuthForm({
</p> </p>
</div> </div>
</DialogContent> </DialogContent>
</Dialog>) </Dialog>
)} )}
{passwordEnabled && !isFirstRun && ( {passwordEnabled && !isFirstRun && (
<Link <Link
href={getPagePath($router, "forgot_password")} href={getPagePath($router, "forgot_password")}
className="text-sm mx-auto hover:text-brand underline underline-offset-4 opacity-70 hover:opacity-100 transition-opacity" 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> <Trans>Forgot password?</Trans>
</Link> </Link>
)} )}
</div> </div>
); )
} }

View File

@@ -88,10 +88,10 @@ import { Dialog } from "../ui/dialog"
type ViewMode = "table" | "grid" type ViewMode = "table" | "grid"
function CellFormatter(info: CellContext<SystemRecord, unknown>) { function CellFormatter(info: CellContext<SystemRecord, unknown>) {
const val = (info.getValue() as number) || 0 const val = Number(info.getValue()) || 0
return ( return (
<div className="flex gap-2 items-center tabular-nums tracking-tight"> <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="grow min-w-8 block bg-muted h-[1em] relative rounded-sm overflow-hidden">
<span <span
className={cn( className={cn(
@@ -189,7 +189,7 @@ export default function SystemsTable() {
header: sortableHeader, header: sortableHeader,
}, },
{ {
accessorFn: ({ info }) => decimalString(info.cpu, info.cpu >= 10 ? 1 : 2), accessorFn: ({ info }) => info.cpu,
id: "cpu", id: "cpu",
name: () => t`CPU`, name: () => t`CPU`,
cell: CellFormatter, cell: CellFormatter,
@@ -198,7 +198,7 @@ export default function SystemsTable() {
}, },
{ {
// accessorKey: "info.mp", // accessorKey: "info.mp",
accessorFn: (originalRow) => originalRow.info.mp, accessorFn: ({ info }) => info.mp,
id: "memory", id: "memory",
name: () => t`Memory`, name: () => t`Memory`,
cell: CellFormatter, cell: CellFormatter,
@@ -206,7 +206,7 @@ export default function SystemsTable() {
header: sortableHeader, header: sortableHeader,
}, },
{ {
accessorFn: (originalRow) => originalRow.info.dp, accessorFn: ({ info }) => info.dp,
id: "disk", id: "disk",
name: () => t`Disk`, name: () => t`Disk`,
cell: CellFormatter, cell: CellFormatter,
@@ -214,7 +214,7 @@ export default function SystemsTable() {
header: sortableHeader, header: sortableHeader,
}, },
{ {
accessorFn: (originalRow) => originalRow.info.g, accessorFn: ({ info }) => info.g,
id: "gpu", id: "gpu",
name: () => "GPU", name: () => "GPU",
cell: CellFormatter, cell: CellFormatter,
@@ -281,7 +281,7 @@ export default function SystemsTable() {
}, },
}, },
{ {
accessorFn: (originalRow) => originalRow.info.dt, accessorFn: ({ info }) => info.dt,
id: "temp", id: "temp",
name: () => t({ message: "Temp", comment: "Temperature label in systems table" }), name: () => t({ message: "Temp", comment: "Temperature label in systems table" }),
size: 50, size: 50,
@@ -303,7 +303,7 @@ export default function SystemsTable() {
}, },
}, },
{ {
accessorFn: (originalRow) => originalRow.info.v, accessorFn: ({ info }) => info.v,
id: "agent", id: "agent",
name: () => t`Agent`, name: () => t`Agent`,
// invertSorting: true, // invertSorting: true,