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:
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user