mirror of
https://github.com/henrygd/beszel.git
synced 2026-04-25 22:11:49 +02:00
195 lines
5.3 KiB
Go
195 lines
5.3 KiB
Go
import { useState } from "react"
|
|
import { Trans, useLingui } from "@lingui/react/macro"
|
|
import { useStore } from "@nanostores/react"
|
|
import { pb } from "@/lib/api"
|
|
import {
|
|
Dialog,
|
|
DialogContent,
|
|
DialogDescription,
|
|
DialogFooter,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
DialogTrigger,
|
|
} from "@/components/ui/dialog"
|
|
import { Button } from "@/components/ui/button"
|
|
import { Input } from "@/components/ui/input"
|
|
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"
|
|
import * as v from "valibot"
|
|
|
|
const Schema = v.object({
|
|
system: v.string(),
|
|
target: v.string(),
|
|
protocol: v.picklist(["icmp", "tcp", "http"]),
|
|
port: v.number(),
|
|
interval: v.pipe(v.string(), v.toNumber(), v.minValue(1), v.maxValue(3600)),
|
|
enabled: v.boolean(),
|
|
name: v.optional(v.string()),
|
|
})
|
|
|
|
export function AddProbeDialog({ systemId }: { systemId?: string }) {
|
|
const [open, setOpen] = useState(false)
|
|
const [protocol, setProtocol] = useState<string>("icmp")
|
|
const [target, setTarget] = useState("")
|
|
const [port, setPort] = useState("")
|
|
const [probeInterval, setProbeInterval] = useState("30")
|
|
const [name, setName] = useState("")
|
|
const [loading, setLoading] = useState(false)
|
|
const [selectedSystemId, setSelectedSystemId] = useState("")
|
|
const systems = useStore($systems)
|
|
const { toast } = useToast()
|
|
const { t } = useLingui()
|
|
const targetName = target.replace(/^https?:\/\//, "")
|
|
|
|
const resetForm = () => {
|
|
setProtocol("icmp")
|
|
setTarget("")
|
|
setPort("")
|
|
setProbeInterval("30")
|
|
setName("")
|
|
setSelectedSystemId("")
|
|
}
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault()
|
|
setLoading(true)
|
|
|
|
try {
|
|
const payload = v.parse(Schema, {
|
|
system: systemId ?? selectedSystemId,
|
|
target,
|
|
protocol,
|
|
port: protocol === "tcp" ? Number(port) : 0,
|
|
interval: probeInterval,
|
|
enabled: true,
|
|
})
|
|
if (name && name !== target) {
|
|
payload.name = name
|
|
}
|
|
await pb.collection("network_probes").create(payload)
|
|
resetForm()
|
|
setOpen(false)
|
|
} catch (err: unknown) {
|
|
toast({ variant: "destructive", title: t`Error`, description: (err as Error)?.message })
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<Dialog open={open} onOpenChange={setOpen}>
|
|
<DialogTrigger asChild>
|
|
<Button variant="outline">
|
|
<PlusIcon className="size-4 me-1" />
|
|
<Trans>Add {{ foo: t`Probe` }}</Trans>
|
|
</Button>
|
|
</DialogTrigger>
|
|
<DialogContent className="max-w-md">
|
|
<DialogHeader>
|
|
<DialogTitle>
|
|
<Trans>Add {{ foo: t`Network Probe` }}</Trans>
|
|
</DialogTitle>
|
|
<DialogDescription>
|
|
<Trans>Configure response monitoring from this agent.</Trans>
|
|
</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>
|
|
</Label>
|
|
<Input
|
|
value={target}
|
|
onChange={(e) => setTarget(e.target.value)}
|
|
placeholder={protocol === "http" ? "https://example.com" : "1.1.1.1"}
|
|
required
|
|
/>
|
|
</div>
|
|
<div className="grid gap-2">
|
|
<Label>
|
|
<Trans>Protocol</Trans>
|
|
</Label>
|
|
|
|
<Select value={protocol} onValueChange={setProtocol}>
|
|
<SelectTrigger>
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="icmp">ICMP</SelectItem>
|
|
<SelectItem value="tcp">TCP</SelectItem>
|
|
<SelectItem value="http">HTTP</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
{protocol === "tcp" && (
|
|
<div className="grid gap-2">
|
|
<Label>
|
|
<Trans>Port</Trans>
|
|
</Label>
|
|
<Input
|
|
type="number"
|
|
value={port}
|
|
onChange={(e) => setPort(e.target.value)}
|
|
placeholder="443"
|
|
min={1}
|
|
max={65535}
|
|
required
|
|
/>
|
|
</div>
|
|
)}
|
|
<div className="grid gap-2">
|
|
<Label>
|
|
<Trans>Interval (seconds)</Trans>
|
|
</Label>
|
|
<Input
|
|
type="number"
|
|
value={probeInterval}
|
|
onChange={(e) => setProbeInterval(e.target.value)}
|
|
min={1}
|
|
max={3600}
|
|
required
|
|
/>
|
|
</div>
|
|
<div className="grid gap-2">
|
|
<Label>
|
|
<Trans>Name (optional)</Trans>
|
|
</Label>
|
|
<Input
|
|
value={name}
|
|
onChange={(e) => setName(e.target.value)}
|
|
placeholder={targetName || t`e.g. Cloudflare DNS`}
|
|
/>
|
|
</div>
|
|
<DialogFooter>
|
|
<Button type="submit" disabled={loading || (!systemId && !selectedSystemId)}>
|
|
{loading ? <Trans>Creating...</Trans> : <Trans>Add {{ foo: t`Probe` }}</Trans>}
|
|
</Button>
|
|
</DialogFooter>
|
|
</form>
|
|
</DialogContent>
|
|
</Dialog>
|
|
)
|
|
}
|