mirror of
https://github.com/Dvorinka/beszel.git
synced 2026-06-03 21:02:56 +00:00
Add public monitoring features and CI updates
- Add status pages, incidents, badges, maintenance, bulk ops, and metrics - Add Docker packaging, env example, and frontend routes - Refresh GitHub workflows and project metadata
This commit is contained in:
@@ -46,8 +46,22 @@ const formSchema = z.object({
|
||||
current_value: z.coerce.number().min(0).optional(),
|
||||
renewal_cost: z.coerce.number().min(0).optional(),
|
||||
auto_renew: z.boolean(),
|
||||
monitor_type: z.enum(["expiry", "watchlist", "portfolio"]),
|
||||
// Expiry alerts
|
||||
alert_days_before: z.coerce.number().min(1).max(365),
|
||||
ssl_alert_enabled: z.boolean(),
|
||||
ssl_alert_days: z.coerce.number().min(1).max(90),
|
||||
// Notification settings
|
||||
notify_on_expiry: z.boolean(),
|
||||
notify_on_ssl_expiry: z.boolean(),
|
||||
notify_on_dns_change: z.boolean(),
|
||||
notify_on_registrar_change: z.boolean(),
|
||||
notify_on_value_change: z.boolean(),
|
||||
value_change_threshold: z.coerce.number().min(1).optional(),
|
||||
// Quiet hours
|
||||
quiet_hours_enabled: z.boolean(),
|
||||
quiet_hours_start: z.string(),
|
||||
quiet_hours_end: z.string(),
|
||||
})
|
||||
|
||||
type FormData = z.infer<typeof formSchema>
|
||||
@@ -76,8 +90,22 @@ export function DomainDialog({ open, onOpenChange, domain, isEdit = false }: Dom
|
||||
current_value: 0,
|
||||
renewal_cost: 0,
|
||||
auto_renew: false,
|
||||
monitor_type: "expiry",
|
||||
// Expiry alerts
|
||||
alert_days_before: 30,
|
||||
ssl_alert_enabled: true,
|
||||
ssl_alert_days: 14,
|
||||
// Notification settings
|
||||
notify_on_expiry: true,
|
||||
notify_on_ssl_expiry: true,
|
||||
notify_on_dns_change: false,
|
||||
notify_on_registrar_change: false,
|
||||
notify_on_value_change: false,
|
||||
value_change_threshold: 10,
|
||||
// Quiet hours
|
||||
quiet_hours_enabled: false,
|
||||
quiet_hours_start: "22:00",
|
||||
quiet_hours_end: "08:00",
|
||||
},
|
||||
})
|
||||
|
||||
@@ -91,8 +119,22 @@ export function DomainDialog({ open, onOpenChange, domain, isEdit = false }: Dom
|
||||
current_value: domain.current_value || 0,
|
||||
renewal_cost: domain.renewal_cost || 0,
|
||||
auto_renew: domain.auto_renew || false,
|
||||
monitor_type: domain.monitor_type || "expiry",
|
||||
// Expiry alerts
|
||||
alert_days_before: domain.alert_days_before || 30,
|
||||
ssl_alert_enabled: domain.ssl_alert_enabled || true,
|
||||
ssl_alert_days: domain.ssl_alert_days || 14,
|
||||
// Notification settings
|
||||
notify_on_expiry: domain.notify_on_expiry !== false,
|
||||
notify_on_ssl_expiry: domain.notify_on_ssl_expiry !== false,
|
||||
notify_on_dns_change: domain.notify_on_dns_change || false,
|
||||
notify_on_registrar_change: domain.notify_on_registrar_change || false,
|
||||
notify_on_value_change: domain.notify_on_value_change || false,
|
||||
value_change_threshold: domain.value_change_threshold || 10,
|
||||
// Quiet hours
|
||||
quiet_hours_enabled: domain.quiet_hours_enabled || false,
|
||||
quiet_hours_start: domain.quiet_hours_start || "22:00",
|
||||
quiet_hours_end: domain.quiet_hours_end || "08:00",
|
||||
})
|
||||
} else if (open && !isEdit) {
|
||||
form.reset({
|
||||
@@ -103,8 +145,22 @@ export function DomainDialog({ open, onOpenChange, domain, isEdit = false }: Dom
|
||||
current_value: 0,
|
||||
renewal_cost: 0,
|
||||
auto_renew: false,
|
||||
monitor_type: "expiry",
|
||||
// Expiry alerts
|
||||
alert_days_before: 30,
|
||||
ssl_alert_enabled: true,
|
||||
ssl_alert_days: 14,
|
||||
// Notification settings
|
||||
notify_on_expiry: true,
|
||||
notify_on_ssl_expiry: true,
|
||||
notify_on_dns_change: false,
|
||||
notify_on_registrar_change: false,
|
||||
notify_on_value_change: false,
|
||||
value_change_threshold: 10,
|
||||
// Quiet hours
|
||||
quiet_hours_enabled: false,
|
||||
quiet_hours_start: "22:00",
|
||||
quiet_hours_end: "08:00",
|
||||
})
|
||||
setLookupData(null)
|
||||
}
|
||||
@@ -173,8 +229,22 @@ export function DomainDialog({ open, onOpenChange, domain, isEdit = false }: Dom
|
||||
current_value: data.current_value,
|
||||
renewal_cost: data.renewal_cost,
|
||||
auto_renew: data.auto_renew,
|
||||
monitor_type: data.monitor_type,
|
||||
// Expiry alerts
|
||||
alert_days_before: data.alert_days_before,
|
||||
ssl_alert_enabled: data.ssl_alert_enabled,
|
||||
ssl_alert_days: data.ssl_alert_days,
|
||||
// Notification settings
|
||||
notify_on_expiry: data.notify_on_expiry,
|
||||
notify_on_ssl_expiry: data.notify_on_ssl_expiry,
|
||||
notify_on_dns_change: data.notify_on_dns_change,
|
||||
notify_on_registrar_change: data.notify_on_registrar_change,
|
||||
notify_on_value_change: data.notify_on_value_change,
|
||||
value_change_threshold: data.notify_on_value_change ? data.value_change_threshold : undefined,
|
||||
// Quiet hours
|
||||
quiet_hours_enabled: data.quiet_hours_enabled,
|
||||
quiet_hours_start: data.quiet_hours_enabled ? data.quiet_hours_start : undefined,
|
||||
quiet_hours_end: data.quiet_hours_enabled ? data.quiet_hours_end : undefined,
|
||||
}
|
||||
|
||||
if (isEdit && domain) {
|
||||
@@ -187,8 +257,22 @@ export function DomainDialog({ open, onOpenChange, domain, isEdit = false }: Dom
|
||||
current_value: payload.current_value,
|
||||
renewal_cost: payload.renewal_cost,
|
||||
auto_renew: payload.auto_renew,
|
||||
monitor_type: payload.monitor_type,
|
||||
// Expiry alerts
|
||||
alert_days_before: payload.alert_days_before,
|
||||
ssl_alert_enabled: payload.ssl_alert_enabled,
|
||||
ssl_alert_days: payload.ssl_alert_days,
|
||||
// Notification settings
|
||||
notify_on_expiry: payload.notify_on_expiry,
|
||||
notify_on_ssl_expiry: payload.notify_on_ssl_expiry,
|
||||
notify_on_dns_change: payload.notify_on_dns_change,
|
||||
notify_on_registrar_change: payload.notify_on_registrar_change,
|
||||
notify_on_value_change: payload.notify_on_value_change,
|
||||
value_change_threshold: payload.value_change_threshold,
|
||||
// Quiet hours
|
||||
quiet_hours_enabled: payload.quiet_hours_enabled,
|
||||
quiet_hours_start: payload.quiet_hours_start,
|
||||
quiet_hours_end: payload.quiet_hours_end,
|
||||
},
|
||||
})
|
||||
} else {
|
||||
@@ -231,7 +315,7 @@ export function DomainDialog({ open, onOpenChange, domain, isEdit = false }: Dom
|
||||
<FormItem className="flex-1">
|
||||
<FormLabel>Domain Name</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="example.com" {...field} />
|
||||
<Input placeholder="example.com" autoFocus tabIndex={0} {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@@ -381,37 +465,259 @@ export function DomainDialog({ open, onOpenChange, domain, isEdit = false }: Dom
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="alerts" className="space-y-4 mt-4">
|
||||
{/* Monitor Type */}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="alert_days_before"
|
||||
name="monitor_type"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Alert Days Before Expiry</FormLabel>
|
||||
<FormItem className="rounded-lg border p-4">
|
||||
<FormLabel className="font-medium">Monitoring Purpose</FormLabel>
|
||||
<FormControl>
|
||||
<Input type="number" min={1} max={365} {...field} />
|
||||
<div className="grid grid-cols-3 gap-2 mt-2">
|
||||
<Button
|
||||
type="button"
|
||||
variant={field.value === "expiry" ? "default" : "outline"}
|
||||
onClick={() => field.onChange("expiry")}
|
||||
className="flex-col h-auto py-3"
|
||||
>
|
||||
<span className="text-xs">Track Expiry</span>
|
||||
<span className="text-[10px] opacity-70">Monitor expiration</span>
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant={field.value === "watchlist" ? "default" : "outline"}
|
||||
onClick={() => field.onChange("watchlist")}
|
||||
className="flex-col h-auto py-3"
|
||||
>
|
||||
<span className="text-xs">Watch to Buy</span>
|
||||
<span className="text-[10px] opacity-70">Track availability</span>
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant={field.value === "portfolio" ? "default" : "outline"}
|
||||
onClick={() => field.onChange("portfolio")}
|
||||
className="flex-col h-auto py-3"
|
||||
>
|
||||
<span className="text-xs">Portfolio</span>
|
||||
<span className="text-[10px] opacity-70">Value tracking</span>
|
||||
</Button>
|
||||
</div>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="ssl_alert_enabled"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex items-center justify-between rounded-lg border p-3">
|
||||
<div className="space-y-0.5">
|
||||
<FormLabel>SSL Expiry Alerts</FormLabel>
|
||||
</div>
|
||||
<FormControl>
|
||||
<Switch
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
/>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
{/* Domain Expiry Alerts */}
|
||||
<div className="space-y-4 rounded-lg border p-4">
|
||||
<h4 className="font-medium text-sm">Domain Expiry Alerts</h4>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="notify_on_expiry"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex items-center justify-between">
|
||||
<div className="space-y-0.5">
|
||||
<FormLabel>Notify before expiry</FormLabel>
|
||||
<p className="text-xs text-muted-foreground">Alert when domain is about to expire</p>
|
||||
</div>
|
||||
<FormControl>
|
||||
<Switch
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
/>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
{form.watch("notify_on_expiry") && (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="alert_days_before"
|
||||
render={({ field }) => (
|
||||
<FormItem className="pl-4 border-l-2">
|
||||
<FormLabel>Days before expiry</FormLabel>
|
||||
<FormControl>
|
||||
<Input type="number" min={1} max={365} className="w-32" {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* SSL Alerts */}
|
||||
<div className="space-y-4 rounded-lg border p-4">
|
||||
<h4 className="font-medium text-sm">SSL Certificate Alerts</h4>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="notify_on_ssl_expiry"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex items-center justify-between">
|
||||
<div className="space-y-0.5">
|
||||
<FormLabel>Notify on SSL expiry</FormLabel>
|
||||
<p className="text-xs text-muted-foreground">Alert when SSL certificate expires</p>
|
||||
</div>
|
||||
<FormControl>
|
||||
<Switch
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
/>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
{form.watch("notify_on_ssl_expiry") && (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="ssl_alert_days"
|
||||
render={({ field }) => (
|
||||
<FormItem className="pl-4 border-l-2">
|
||||
<FormLabel>Days before SSL expiry</FormLabel>
|
||||
<FormControl>
|
||||
<Input type="number" min={1} max={90} className="w-32" {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Change Detection Alerts */}
|
||||
<div className="space-y-4 rounded-lg border p-4">
|
||||
<h4 className="font-medium text-sm">Change Detection</h4>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="notify_on_dns_change"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex items-center justify-between">
|
||||
<div className="space-y-0.5">
|
||||
<FormLabel>DNS changes</FormLabel>
|
||||
<p className="text-xs text-muted-foreground">Alert when DNS records change</p>
|
||||
</div>
|
||||
<FormControl>
|
||||
<Switch
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
/>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="notify_on_registrar_change"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex items-center justify-between">
|
||||
<div className="space-y-0.5">
|
||||
<FormLabel>Registrar changes</FormLabel>
|
||||
<p className="text-xs text-muted-foreground">Alert when registrar information changes</p>
|
||||
</div>
|
||||
<FormControl>
|
||||
<Switch
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
/>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="notify_on_value_change"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex items-center justify-between">
|
||||
<div className="space-y-0.5">
|
||||
<FormLabel>Value changes</FormLabel>
|
||||
<p className="text-xs text-muted-foreground">Alert when estimated value changes significantly</p>
|
||||
</div>
|
||||
<FormControl>
|
||||
<Switch
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
/>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
{form.watch("notify_on_value_change") && (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="value_change_threshold"
|
||||
render={({ field }) => (
|
||||
<FormItem className="pl-4 border-l-2">
|
||||
<FormLabel>Change threshold (%)</FormLabel>
|
||||
<FormControl>
|
||||
<Input type="number" min={1} max={100} className="w-32" {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Quiet Hours */}
|
||||
<div className="space-y-4 rounded-lg border p-4">
|
||||
<h4 className="font-medium text-sm">Quiet Hours</h4>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="quiet_hours_enabled"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex items-center justify-between">
|
||||
<div className="space-y-0.5">
|
||||
<FormLabel>Enable quiet hours</FormLabel>
|
||||
<p className="text-xs text-muted-foreground">Suppress notifications during specific hours</p>
|
||||
</div>
|
||||
<FormControl>
|
||||
<Switch
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
/>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
{form.watch("quiet_hours_enabled") && (
|
||||
<div className="grid grid-cols-2 gap-4 pl-4 border-l-2">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="quiet_hours_start"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Start time</FormLabel>
|
||||
<FormControl>
|
||||
<Input type="time" {...field} />
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="quiet_hours_end"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>End time</FormLabel>
|
||||
<FormControl>
|
||||
<Input type="time" {...field} />
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user