add probes page

This commit is contained in:
henrygd
2026-04-20 11:52:37 -04:00
parent 209bb4ebb4
commit 3a881e1d5e
7 changed files with 81 additions and 7 deletions

View File

@@ -319,18 +319,18 @@ func updateNetworkProbesRecords(app core.App, data map[string]probe.Result, syst
return nil
}
collectionName := "network_probes"
nowString := time.Now().UTC().Format(types.DefaultDateLayout)
for key := range data {
probe := data[key]
id := MakeStableHashId(systemId, key)
params := dbx.Params{
// "system": systemId,
"latency": probe[0],
"loss": probe[3],
"updated": time.Now().UTC().Format(types.DefaultDateLayout),
"updated": nowString,
}
_, err := app.DB().Update(collectionName, params, dbx.HashExp{"id": id}).Execute()
if err != nil {
app.Logger().Warn("Failed to update network probe record", "system", systemId, "probe", key, "err", err)
app.Logger().Warn("Failed to update probe", "system", systemId, "probe", key, "err", err)
}
}
return nil

View File

@@ -8,6 +8,7 @@ import {
LogOutIcon,
LogsIcon,
MenuIcon,
NetworkIcon,
PlusIcon,
SearchIcon,
ServerIcon,
@@ -109,6 +110,10 @@ export default function Navbar() {
<HardDriveIcon className="h-4 w-4 me-2.5" strokeWidth={1.5} />
<span>S.M.A.R.T.</span>
</DropdownMenuItem>
<DropdownMenuItem onClick={() => navigate(getPagePath($router, "probes"))} className="flex items-center">
<NetworkIcon className="h-4 w-4 me-2.5" strokeWidth={1.5} />
<Trans>Network Probes</Trans>
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => navigate(getPagePath($router, "settings", { name: "general" }))}
className="flex items-center"
@@ -180,6 +185,21 @@ export default function Navbar() {
</TooltipTrigger>
<TooltipContent>S.M.A.R.T.</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Link
href={getPagePath($router, "probes")}
className={cn("hidden md:grid", buttonVariants({ variant: "ghost", size: "icon" }))}
aria-label="Network Probes"
onMouseEnter={() => import("@/components/routes/probes")}
>
<NetworkIcon className="h-[1.2rem] w-[1.2rem]" strokeWidth={1.5} />
</Link>
</TooltipTrigger>
<TooltipContent>
<Trans>Network Probes</Trans>
</TooltipContent>
</Tooltip>
<LangToggle />
<ModeToggle />
<Tooltip>

View File

@@ -195,7 +195,7 @@ export default function NetworkProbesTableNew({ systemId }: { systemId?: string
className="ms-auto px-4 w-full max-w-full md:w-64"
/>
)}
{systemId && !isReadOnlyUser() ? <AddProbeDialog systemId={systemId} /> : null}
{!isReadOnlyUser() ? <AddProbeDialog systemId={systemId} /> : null}
</div>
</div>
</CardHeader>

View File

@@ -1,5 +1,6 @@
import { useState } from "react"
import { Trans, useLingui } from "@lingui/react/macro"
import { useStore } from "@nanostores/react"
import { pb } from "@/lib/api"
import {
Dialog,
@@ -16,8 +17,9 @@ import { Label } from "@/components/ui/label"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
import { PlusIcon } from "lucide-react"
import { useToast } from "@/components/ui/use-toast"
import { $systems } from "@/lib/stores"
export function AddProbeDialog({ systemId }: { systemId: string }) {
export function AddProbeDialog({ systemId }: { systemId?: string }) {
const [open, setOpen] = useState(false)
const [protocol, setProtocol] = useState<string>("icmp")
const [target, setTarget] = useState("")
@@ -25,6 +27,8 @@ export function AddProbeDialog({ systemId }: { systemId: string }) {
const [probeInterval, setProbeInterval] = useState("60")
const [name, setName] = useState("")
const [loading, setLoading] = useState(false)
const [selectedSystemId, setSelectedSystemId] = useState("")
const systems = useStore($systems)
const { toast } = useToast()
const { t } = useLingui()
@@ -34,6 +38,7 @@ export function AddProbeDialog({ systemId }: { systemId: string }) {
setPort("")
setProbeInterval("60")
setName("")
setSelectedSystemId("")
}
const handleSubmit = async (e: React.FormEvent) => {
@@ -41,7 +46,7 @@ export function AddProbeDialog({ systemId }: { systemId: string }) {
setLoading(true)
try {
await pb.collection("network_probes").create({
system: systemId,
system: systemId ?? selectedSystemId,
name,
target,
protocol,
@@ -76,6 +81,25 @@ export function AddProbeDialog({ systemId }: { systemId: string }) {
</DialogDescription>
</DialogHeader>
<form onSubmit={handleSubmit} className="grid gap-4 tabular-nums">
{!systemId && (
<div className="grid gap-2">
<Label>
<Trans>System</Trans>
</Label>
<Select value={selectedSystemId} onValueChange={setSelectedSystemId} required>
<SelectTrigger>
<SelectValue placeholder={t`Select a system`} />
</SelectTrigger>
<SelectContent>
{systems.map((sys) => (
<SelectItem key={sys.id} value={sys.id}>
{sys.name}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
)}
<div className="grid gap-2">
<Label>
<Trans>Target</Trans>
@@ -142,7 +166,7 @@ export function AddProbeDialog({ systemId }: { systemId: string }) {
/>
</div>
<DialogFooter>
<Button type="submit" disabled={loading}>
<Button type="submit" disabled={loading || (!systemId && !selectedSystemId)}>
{loading ? <Trans>Creating...</Trans> : <Trans>Add {{ foo: t`Probe` }}</Trans>}
</Button>
</DialogFooter>

View File

@@ -4,6 +4,7 @@ const routes = {
home: "/",
containers: "/containers",
smart: "/smart",
probes: "/probes",
system: `/system/:id`,
settings: `/settings/:name?`,
forgot_password: `/forgot-password`,

View File

@@ -0,0 +1,26 @@
import { useLingui } from "@lingui/react/macro"
import { memo, useEffect, useMemo } from "react"
import NetworkProbesTableNew from "@/components/network-probes-table/network-probes-table"
import { ActiveAlerts } from "@/components/active-alerts"
import { FooterRepoLink } from "@/components/footer-repo-link"
export default memo(() => {
const { t } = useLingui()
useEffect(() => {
document.title = `${t`Network Probes`} / Beszel`
}, [t])
return useMemo(
() => (
<>
<div className="grid gap-4">
<ActiveAlerts />
<NetworkProbesTableNew />
</div>
<FooterRepoLink />
</>
),
[]
)
})

View File

@@ -30,6 +30,7 @@ const LoginPage = lazy(() => import("@/components/login/login.tsx"))
const Home = lazy(() => import("@/components/routes/home.tsx"))
const Containers = lazy(() => import("@/components/routes/containers.tsx"))
const Smart = lazy(() => import("@/components/routes/smart.tsx"))
const Probes = lazy(() => import("@/components/routes/probes.tsx"))
const SystemDetail = lazy(() => import("@/components/routes/system.tsx"))
const CopyToClipboardDialog = lazy(() => import("@/components/copy-to-clipboard.tsx"))
@@ -79,6 +80,8 @@ const App = memo(() => {
return <Containers />
} else if (page.route === "smart") {
return <Smart />
} else if (page.route === "probes") {
return <Probes />
} else if (page.route === "settings") {
return <Settings />
}