Add clear stuck jobs admin feature and simplify worker scheduling

- Add POST /api/admin/settings/clear-stuck-jobs endpoint to clear
  stuck/failed asynq worker jobs from Redis (retry queue, archived,
  orphaned task metadata)
- Add "Clear Stuck Jobs" button to admin settings UI
- Remove TASK_REMINDER_MINUTE config - all jobs now run at minute 0
- Simplify formatCron to only take hour parameter
- Update default notification times to CST-friendly hours

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Trey t
2025-12-06 23:05:48 -06:00
parent 3b9e37d12b
commit af87bd943e
6 changed files with 150 additions and 17 deletions
+62 -1
View File
@@ -1,7 +1,7 @@
'use client';
import { useMutation } from '@tanstack/react-query';
import { Database, TestTube, Trash2 } from 'lucide-react';
import { Database, TestTube, Trash2, RefreshCw } from 'lucide-react';
import { settingsApi } from '@/lib/api';
import { Button } from '@/components/ui/button';
@@ -56,6 +56,20 @@ export default function SettingsPage() {
},
});
const clearStuckJobsMutation = useMutation({
mutationFn: settingsApi.clearStuckJobs,
onSuccess: (data) => {
if (data.keys_deleted > 0) {
toast.success(`${data.message} (${data.keys_deleted} keys cleared)`);
} else {
toast.info('No stuck jobs found');
}
},
onError: () => {
toast.error('Failed to clear stuck jobs');
},
});
return (
<div className="p-6 space-y-6">
<div>
@@ -163,6 +177,53 @@ export default function SettingsPage() {
</CardContent>
</Card>
{/* Clear Stuck Jobs */}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<RefreshCw className="h-5 w-5" />
Clear Stuck Jobs
</CardTitle>
<CardDescription>
Clear stuck or failed background worker jobs from Redis
</CardDescription>
</CardHeader>
<CardContent>
<AlertDialog>
<AlertDialogTrigger asChild>
<Button
variant="outline"
disabled={clearStuckJobsMutation.isPending}
>
{clearStuckJobsMutation.isPending ? 'Clearing...' : 'Clear Stuck Jobs'}
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Clear Stuck Jobs?</AlertDialogTitle>
<AlertDialogDescription>
This will clear stuck/failed worker jobs from Redis including:
<ul className="list-disc list-inside mt-2 space-y-1">
<li>Jobs in the retry queue</li>
<li>Archived/dead letter jobs</li>
<li>Orphaned task metadata</li>
</ul>
<span className="block mt-2">
This is safe to run and can fix issues with notifications not running at expected times.
</span>
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction onClick={() => clearStuckJobsMutation.mutate()}>
Clear Stuck Jobs
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</CardContent>
</Card>
{/* Clear All Data */}
<Card className="border-destructive">
<CardHeader>
+5
View File
@@ -684,6 +684,11 @@ export const settingsApi = {
const response = await api.post<{ message: string; users_deleted: number; preserved_users: number }>('/settings/clear-all-data');
return response.data;
},
clearStuckJobs: async (): Promise<{ message: string; keys_deleted: number; deleted_keys: string[] }> => {
const response = await api.post<{ message: string; keys_deleted: number; deleted_keys: string[] }>('/settings/clear-stuck-jobs');
return response.data;
},
};
// Limitations types