import { useMemo } from "react"; import { useForm } from "react-hook-form"; import { Button } from "~/components/ui/button"; import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "~/components/ui/card"; import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from "~/components/ui/form"; import { Input } from "~/components/ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "~/components/ui/select"; import { Switch } from "~/components/ui/switch"; import type { Volume } from "~/lib/types"; import { cn } from "~/lib/utils"; type BackupDestination = "s3" | "sftp" | "filesystem"; type BackupFrequency = "hourly" | "daily" | "weekly"; type BackupEncryption = "none" | "aes256" | "gpg"; type BackupFormValues = { isEnabled: boolean; destination: BackupDestination; frequency: BackupFrequency; dailyTime: string; weeklyDay: string; retentionCopies: string; retentionDays: string; notifyOnFailure: boolean; notificationWebhook: string; encryption: BackupEncryption; encryptionPassword: string; s3Bucket: string; s3Region: string; s3PathPrefix: string; sftpHost: string; sftpPort: string; sftpUsername: string; sftpPath: string; filesystemPath: string; }; type Props = { volume: Volume; }; const weeklyDays = [ { label: "Monday", value: "monday" }, { label: "Tuesday", value: "tuesday" }, { label: "Wednesday", value: "wednesday" }, { label: "Thursday", value: "thursday" }, { label: "Friday", value: "friday" }, { label: "Saturday", value: "saturday" }, { label: "Sunday", value: "sunday" }, ]; export const VolumeBackupsTabContent = ({ volume }: Props) => { const form = useForm({ defaultValues: { isEnabled: true, destination: "s3", frequency: "daily", dailyTime: "02:00", weeklyDay: "sunday", retentionCopies: "7", retentionDays: "30", notifyOnFailure: true, notificationWebhook: "", encryption: "aes256", encryptionPassword: "", s3Bucket: "", s3Region: "us-east-1", s3PathPrefix: `${volume.name}/backups`, sftpHost: "", sftpPort: "22", sftpUsername: "", sftpPath: `/backups/${volume.name}`, filesystemPath: `/var/backups/${volume.name}`, }, }); const destination = form.watch("destination"); const frequency = form.watch("frequency"); const encryption = form.watch("encryption"); const notifyOnFailure = form.watch("notifyOnFailure"); const values = form.watch(); const summary = useMemo(() => { const scheduleLabel = frequency === "hourly" ? "Every hour" : frequency === "daily" ? `Every day at ${values.dailyTime}` : `Every ${values.weeklyDay.charAt(0).toUpperCase()}${values.weeklyDay.slice(1)} at ${values.dailyTime}`; const destinationLabel = (() => { if (destination === "s3") { return `Amazon S3 → ${values.s3Bucket || ""} (${values.s3Region})`; } if (destination === "sftp") { return `SFTP → ${values.sftpUsername || "user"}@${values.sftpHost || "server"}:${values.sftpPath}`; } return `Filesystem → ${values.filesystemPath}`; })(); return { vol: volume.name, scheduleLabel, destinationLabel, encryptionLabel: encryption === "none" ? "Disabled" : encryption.toUpperCase(), retentionLabel: `${values.retentionCopies} copies \u2022 ${values.retentionDays} days`, notificationsLabel: notifyOnFailure ? values.notificationWebhook ? `Webhook to ${values.notificationWebhook}` : "Webhook pending configuration" : "Disabled", }; }, [ destination, encryption, frequency, notifyOnFailure, values.dailyTime, values.filesystemPath, values.notificationWebhook, values.retentionCopies, values.retentionDays, values.s3Bucket, values.s3Region, values.sftpHost, values.sftpPath, values.sftpUsername, values.weeklyDay, volume.name, ]); const handleSubmit = (formValues: BackupFormValues) => { console.info("Backup configuration", formValues); }; return (
Backup automation Enable scheduled snapshots and off-site replication for this volume.
(
{field.value ? "Enabled" : "Paused"}
)} />
( Destination provider Choose where backups for {volume.name} will be stored. )} /> ( Backup frequency Define how often snapshots should be taken. )} /> {frequency !== "hourly" && ( ( Execution time Time of day when the backup will run. )} /> )} {frequency === "weekly" && ( ( Execution day Choose which day of the week to run the backup. )} /> )} ( Max copies to retain field.onChange(event.target.value)} /> Oldest backups will be pruned after this many copies. )} /> ( Retention window (days) field.onChange(event.target.value)} /> Backups older than this window will be removed. )} />
{destination === "s3" && ( Amazon S3 bucket Define the bucket and path where compressed archives will be uploaded. ( Bucket name Ensure the bucket has versioning and lifecycle rules as needed. )} /> ( Region AWS region where the bucket resides. )} /> ( Object prefix Backups will be stored under this key prefix inside the bucket. )} /> )} {destination === "sftp" && ( SFTP target Connect to a remote host that will receive encrypted backup archives. ( Hostname )} /> ( Port )} /> ( Username )} /> ( Destination path Ensure the directory exists and has write permissions. )} /> )} {destination === "filesystem" && ( Filesystem target Persist archives to a directory on the host running Ironmount. ( Backup directory The directory must be mounted with sufficient capacity. )} /> )} Encryption & notifications Secure backups and stay informed when something goes wrong. ( Encryption Protect backups at rest with optional encryption. )} /> {encryption !== "none" && ( ( Encryption secret Store this password securely. It will be required to restore backups. )} /> )} ( Failure alerts

Webhook notifications

Send an HTTP POST when a backup fails.

)} /> {notifyOnFailure && ( ( Webhook URL Ironmount will POST a JSON payload with failure details. )} /> )}
Runbook summary Validate the automation before enabling it in production.

Volume

{summary.vol}

Schedule

{summary.scheduleLabel}

Destination

{summary.destinationLabel}

Retention

{summary.retentionLabel}

Encryption

{summary.encryptionLabel}

Notifications

{summary.notificationsLabel}

); };