mirror of
https://github.com/xCyanGrizzly/DragonsStash.git
synced 2026-07-24 07:42:47 +00:00
feat(telegram): add "skip & disable topic" on the live worker panel
Track the forum topic currently being processed on IngestionRun (currentTopicId + currentAccountChannelMapId; additive migration) and expose it on the live status. processArchiveSets gains an optional shouldStop callback polled before each archive set; the forum branch passes a live isTopicFetchEnabled check, so disabling a topic mid-run lets the in-flight file finish, then skips the rest of that topic. A new disableActiveTopic server action sets the topic's fetchEnabled false (upsert), and the worker status panel shows a "Skip & disable this topic" button while a topic is being processed. Future runs skip the topic via the existing live per-topic read. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -9,13 +9,14 @@ import {
|
||||
Radio,
|
||||
AlertTriangle,
|
||||
RefreshCw,
|
||||
SkipForward,
|
||||
} from "lucide-react";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { toast } from "sonner";
|
||||
import { triggerIngestion } from "../actions";
|
||||
import { triggerIngestion, disableActiveTopic } from "../actions";
|
||||
import type { IngestionAccountStatus } from "@/lib/telegram/types";
|
||||
|
||||
interface WorkerStatusPanelProps {
|
||||
@@ -218,6 +219,24 @@ function RunningStatus({
|
||||
}: {
|
||||
run: NonNullable<IngestionAccountStatus["currentRun"]>;
|
||||
}) {
|
||||
const [isDisabling, startDisable] = useTransition();
|
||||
|
||||
const handleSkipTopic = () => {
|
||||
const acmId = run.currentAccountChannelMapId;
|
||||
const topicId = run.currentTopicId;
|
||||
if (!acmId || !topicId) return;
|
||||
startDisable(async () => {
|
||||
const result = await disableActiveTopic(acmId, topicId);
|
||||
if (result.success) {
|
||||
toast.success(
|
||||
"Topic disabled — the current file finishes, then the rest is skipped"
|
||||
);
|
||||
} else {
|
||||
toast.error(result.error ?? "Failed to disable topic");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center gap-2">
|
||||
@@ -273,6 +292,27 @@ function RunningStatus({
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Skip & disable the topic currently being processed */}
|
||||
{run.currentTopicId && run.currentAccountChannelMapId && (
|
||||
<div className="pl-6 pt-1">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="h-7 gap-1.5 text-xs"
|
||||
onClick={handleSkipTopic}
|
||||
disabled={isDisabling}
|
||||
title="Finish the current file, skip the rest of this topic, and don't fetch it in future runs"
|
||||
>
|
||||
{isDisabling ? (
|
||||
<Loader2 className="h-3 w-3 animate-spin" />
|
||||
) : (
|
||||
<SkipForward className="h-3 w-3" />
|
||||
)}
|
||||
Skip & disable this topic
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -462,6 +462,49 @@ export async function setTopicFetchEnabled(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable the topic currently being processed by the worker, identified by its
|
||||
* account-channel mapping + Telegram topic id (as exposed on the live run
|
||||
* status). Upserts so a disabled row exists even if the worker hasn't persisted
|
||||
* the topic yet. The worker honours this live: it finishes the in-flight file
|
||||
* then skips the rest of the topic, and future runs skip it entirely.
|
||||
*/
|
||||
export async function disableActiveTopic(
|
||||
accountChannelMapId: string,
|
||||
topicId: string
|
||||
): Promise<ActionResult> {
|
||||
const admin = await requireAdmin();
|
||||
if (!admin.success) return admin;
|
||||
|
||||
let topicIdBig: bigint;
|
||||
try {
|
||||
topicIdBig = BigInt(topicId);
|
||||
} catch {
|
||||
return { success: false, error: "Invalid topic id" };
|
||||
}
|
||||
|
||||
try {
|
||||
await prisma.topicProgress.upsert({
|
||||
where: {
|
||||
accountChannelMapId_topicId: {
|
||||
accountChannelMapId,
|
||||
topicId: topicIdBig,
|
||||
},
|
||||
},
|
||||
create: {
|
||||
accountChannelMapId,
|
||||
topicId: topicIdBig,
|
||||
fetchEnabled: false,
|
||||
},
|
||||
update: { fetchEnabled: false },
|
||||
});
|
||||
revalidatePath(REVALIDATE_PATH);
|
||||
return { success: true, data: undefined };
|
||||
} catch {
|
||||
return { success: false, error: "Failed to disable topic" };
|
||||
}
|
||||
}
|
||||
|
||||
// ── Account-Channel link actions ──
|
||||
|
||||
export async function linkChannel(
|
||||
|
||||
Reference in New Issue
Block a user