feat: add configurable backup time for local and USB backups
Replace hardcoded 8 PM / 9 PM backup schedules with user-selectable hour dropdowns. Adds autoBackupHour and usbBackupHour fields to User model. Cron jobs now check every hour against the configured time. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -48,20 +48,16 @@ export function BackupDestinationManager() {
|
||||
});
|
||||
|
||||
const usbBackupEnabled = usbSettingData?.usbBackupEnabled ?? false;
|
||||
const usbBackupHour = usbSettingData?.usbBackupHour ?? 21;
|
||||
|
||||
const usbToggleMutation = useMutation({
|
||||
mutationFn: async (enabled: boolean) => {
|
||||
const res = await apiRequest("PUT", "/api/database-management/usb-backup-setting", {
|
||||
usbBackupEnabled: enabled,
|
||||
});
|
||||
mutationFn: async (patch: { usbBackupEnabled?: boolean; usbBackupHour?: number }) => {
|
||||
const res = await apiRequest("PUT", "/api/database-management/usb-backup-setting", patch);
|
||||
return res.json();
|
||||
},
|
||||
onSuccess: (data) => {
|
||||
queryClient.setQueryData(["/db/usb-backup-setting"], data);
|
||||
toast({
|
||||
title: "Setting Saved",
|
||||
description: `USB backup ${data.usbBackupEnabled ? "enabled" : "disabled"}.`,
|
||||
});
|
||||
toast({ title: "Setting Saved" });
|
||||
},
|
||||
onError: () => {
|
||||
toast({
|
||||
@@ -136,11 +132,11 @@ export function BackupDestinationManager() {
|
||||
<CardTitle>External Backup Destination</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className="flex flex-wrap items-center gap-3">
|
||||
<Switch
|
||||
id="usb-backup-toggle"
|
||||
checked={usbBackupEnabled}
|
||||
onCheckedChange={(checked) => usbToggleMutation.mutate(checked)}
|
||||
onCheckedChange={(checked) => usbToggleMutation.mutate({ usbBackupEnabled: checked })}
|
||||
disabled={usbToggleMutation.isPending}
|
||||
/>
|
||||
<label
|
||||
@@ -149,14 +145,24 @@ export function BackupDestinationManager() {
|
||||
>
|
||||
USB Backup
|
||||
</label>
|
||||
<span className="text-xs text-gray-400">
|
||||
(daily at 9 PM → saves to the "USB Backup" folder on your drive)
|
||||
</span>
|
||||
<div className="flex items-center gap-2">
|
||||
<label className="text-sm text-gray-600">at</label>
|
||||
<select
|
||||
className="border rounded px-2 py-1 text-sm text-gray-700 bg-white"
|
||||
value={usbBackupHour}
|
||||
onChange={(e) => usbToggleMutation.mutate({ usbBackupHour: Number(e.target.value) })}
|
||||
>
|
||||
{Array.from({ length: 24 }, (_, h) => {
|
||||
const label = h === 0 ? "12:00 AM" : h < 12 ? `${h}:00 AM` : h === 12 ? "12:00 PM" : `${h - 12}:00 PM`;
|
||||
return <option key={h} value={h}>{label}</option>;
|
||||
})}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="text-sm text-gray-500">
|
||||
Enter the root path of your USB drive below. The app will automatically back up to the{" "}
|
||||
<span className="font-medium text-gray-700">USB Backup</span> folder inside it every night at 9 PM when the toggle is on.
|
||||
<span className="font-medium text-gray-700">USB Backup</span> folder inside it at the scheduled time when the toggle is on.
|
||||
</p>
|
||||
|
||||
<div className="flex gap-2">
|
||||
|
||||
@@ -88,20 +88,16 @@ export default function DatabaseManagementPage() {
|
||||
});
|
||||
|
||||
const autoBackupEnabled = autoBackupData?.autoBackupEnabled ?? true;
|
||||
const autoBackupHour = autoBackupData?.autoBackupHour ?? 20;
|
||||
|
||||
const autoBackupMutation = useMutation({
|
||||
mutationFn: async (enabled: boolean) => {
|
||||
const res = await apiRequest("PUT", "/api/database-management/auto-backup-setting", {
|
||||
autoBackupEnabled: enabled,
|
||||
});
|
||||
mutationFn: async (patch: { autoBackupEnabled?: boolean; autoBackupHour?: number }) => {
|
||||
const res = await apiRequest("PUT", "/api/database-management/auto-backup-setting", patch);
|
||||
return res.json();
|
||||
},
|
||||
onSuccess: (data) => {
|
||||
queryClient.setQueryData(["/db/auto-backup-setting"], data);
|
||||
toast({
|
||||
title: "Setting Saved",
|
||||
description: `Automatic backup ${data.autoBackupEnabled ? "enabled" : "disabled"}.`,
|
||||
});
|
||||
toast({ title: "Setting Saved" });
|
||||
},
|
||||
onError: () => {
|
||||
toast({
|
||||
@@ -215,11 +211,11 @@ export default function DatabaseManagementPage() {
|
||||
including patients, appointments, claims, and all related data.
|
||||
</p>
|
||||
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className="flex flex-wrap items-center gap-3">
|
||||
<Switch
|
||||
id="auto-backup-toggle"
|
||||
checked={autoBackupEnabled}
|
||||
onCheckedChange={(checked) => autoBackupMutation.mutate(checked)}
|
||||
onCheckedChange={(checked) => autoBackupMutation.mutate({ autoBackupEnabled: checked })}
|
||||
disabled={autoBackupMutation.isPending}
|
||||
/>
|
||||
<label
|
||||
@@ -228,7 +224,19 @@ export default function DatabaseManagementPage() {
|
||||
>
|
||||
Automatic Backup
|
||||
</label>
|
||||
<span className="text-xs text-gray-400">(daily at 8 PM to server backup folder)</span>
|
||||
<div className="flex items-center gap-2">
|
||||
<label className="text-sm text-gray-600">at</label>
|
||||
<select
|
||||
className="border rounded px-2 py-1 text-sm text-gray-700 bg-white"
|
||||
value={autoBackupHour}
|
||||
onChange={(e) => autoBackupMutation.mutate({ autoBackupHour: Number(e.target.value) })}
|
||||
>
|
||||
{Array.from({ length: 24 }, (_, h) => {
|
||||
const label = h === 0 ? "12:00 AM" : h < 12 ? `${h}:00 AM` : h === 12 ? "12:00 PM" : `${h - 12}:00 PM`;
|
||||
return <option key={h} value={h}>{label}</option>;
|
||||
})}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-4">
|
||||
|
||||
Reference in New Issue
Block a user