fix(moderation): remove manual reanalyze triggers — auto-recovery only
Manual per-message and batch reanalyze buttons/endpoints let anyone re-queue arbitrary messages for LLM analysis, burning AI credits on spam. Removed: - FE: Reanalyze buttons in message list, search panel, and messages page - FE: useReanalyze/useReanalyzeBatch hooks + messagesApi methods - BE: POST /api/messages/:id/reanalyze and /reanalyze-batch endpoints - BE: markForReanalysis/reanalyzeErrorBatch service+repository methods Recovery of failed messages is fully automatic: the discord-gateway startPendingAIAnalysisWorker retries 'pending' (batch path) and 'error/analysis_incomplete' (individual path) messages on AI_ANALYSIS_RECOVERY_INTERVAL_MS.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { Flag, Image, Loader2, RefreshCw, Search } from "lucide-react";
|
||||
import { Flag, Image, Loader2, Search } from "lucide-react";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { GlassCard } from "@/components/glass/card";
|
||||
@@ -13,7 +13,6 @@ import { MessageList } from "@/components/messages/message-list";
|
||||
import { SearchOverlay } from "@/components/messages/search-overlay";
|
||||
import { EmptyState, ErrorState, LoadingSkeleton } from "@/components/shared";
|
||||
import { GuildSelector } from "@/components/shared/guild-selector";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
@@ -28,8 +27,6 @@ import {
|
||||
useMessages,
|
||||
useMessagesHasMore,
|
||||
useMessagesWsSync,
|
||||
useReanalyze,
|
||||
useReanalyzeBatch,
|
||||
useReview,
|
||||
useTextChannels,
|
||||
} from "@/hooks";
|
||||
@@ -74,8 +71,6 @@ export default function MessagesPage() {
|
||||
const loadMoreMut = useLoadMore();
|
||||
const { data: images } = useImages(guildId);
|
||||
const { data: reviews } = useReview(selectedChannel || undefined);
|
||||
const reanalyzeMut = useReanalyze();
|
||||
const reanalyzeBatchMut = useReanalyzeBatch();
|
||||
|
||||
const {
|
||||
message: detailMessage,
|
||||
@@ -164,14 +159,6 @@ export default function MessagesPage() {
|
||||
⌘K
|
||||
</span>
|
||||
</button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => reanalyzeBatchMut.mutate(guildId)}
|
||||
className="h-8 text-xs"
|
||||
>
|
||||
<RefreshCw className="mr-1 size-3" /> Reanalyze
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* ── Sub navigation ── */}
|
||||
@@ -197,7 +184,6 @@ export default function MessagesPage() {
|
||||
messages={currentMessages}
|
||||
selectedId={detailId}
|
||||
onSelect={setDetailId}
|
||||
onReanalyze={(id) => reanalyzeMut.mutate(id)}
|
||||
hasMore={cursorData?.hasMore}
|
||||
onLoadMore={handleLoadMore}
|
||||
isLoadingMore={loadMoreMut.isPending}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { Loader2, RefreshCw, Search, Sparkles } from "lucide-react";
|
||||
import { Loader2, Search, Sparkles } from "lucide-react";
|
||||
import { useCallback, useState } from "react";
|
||||
|
||||
import { EmptyState, LoadingSkeleton } from "@/components/shared";
|
||||
@@ -10,14 +10,13 @@ import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Progress } from "@/components/ui/progress";
|
||||
import { useMessageSearch, useReanalyze } from "@/hooks";
|
||||
import { useMessageSearch } from "@/hooks";
|
||||
import { renderMessageContent, safeParseJsonArray } from "@/lib/format";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
export function SearchPanel() {
|
||||
const [query, setQuery] = useState("");
|
||||
const [enabled, setEnabled] = useState(false);
|
||||
const reanalyzeMut = useReanalyze();
|
||||
|
||||
const { data: results, isValidating: isFetching } = useMessageSearch(
|
||||
query,
|
||||
@@ -134,14 +133,6 @@ export function SearchPanel() {
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="xs"
|
||||
onClick={() => reanalyzeMut.mutate(msg.id)}
|
||||
>
|
||||
<RefreshCw className="size-3 mr-1" />
|
||||
Reanalyze
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import { Hash, RefreshCw } from "lucide-react";
|
||||
import { Hash } from "lucide-react";
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { Progress } from "@/components/ui/progress";
|
||||
import {
|
||||
@@ -18,11 +17,9 @@ import { AiStatusBadge } from "./ai-status-badge";
|
||||
export function MessageCard({
|
||||
message: msg,
|
||||
onClick,
|
||||
onReanalyze,
|
||||
}: {
|
||||
message: MessageRecord;
|
||||
onClick: (id: string) => void;
|
||||
onReanalyze: (id: string) => void;
|
||||
}) {
|
||||
const severity = (
|
||||
{
|
||||
@@ -156,16 +153,6 @@ export function MessageCard({
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="xs"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onReanalyze(msg.id);
|
||||
}}
|
||||
>
|
||||
<RefreshCw className="size-3 mr-1" /> Reanalyze
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
|
||||
@@ -9,7 +9,6 @@ interface MessageListProps {
|
||||
messages: MessageRecord[];
|
||||
selectedId: string | null;
|
||||
onSelect: (id: string) => void;
|
||||
onReanalyze?: (id: string) => void;
|
||||
hasMore?: boolean;
|
||||
onLoadMore?: () => void;
|
||||
isLoadingMore?: boolean;
|
||||
@@ -19,7 +18,6 @@ export function MessageList({
|
||||
messages,
|
||||
selectedId: _selectedId,
|
||||
onSelect,
|
||||
onReanalyze,
|
||||
hasMore,
|
||||
onLoadMore,
|
||||
isLoadingMore,
|
||||
@@ -27,12 +25,7 @@ export function MessageList({
|
||||
return (
|
||||
<>
|
||||
{messages.map((msg) => (
|
||||
<MessageCard
|
||||
key={msg.id}
|
||||
message={msg}
|
||||
onClick={onSelect}
|
||||
onReanalyze={(id) => onReanalyze?.(id)}
|
||||
/>
|
||||
<MessageCard key={msg.id} message={msg} onClick={onSelect} />
|
||||
))}
|
||||
{hasMore && (
|
||||
<div className="flex justify-center py-4">
|
||||
|
||||
@@ -24,8 +24,6 @@ export {
|
||||
useMessages,
|
||||
useMessagesHasMore,
|
||||
useMessagesWsSync,
|
||||
useReanalyze,
|
||||
useReanalyzeBatch,
|
||||
useReview,
|
||||
useTextChannels,
|
||||
} from "./use-messages";
|
||||
|
||||
@@ -155,16 +155,6 @@ export function useMessageDetail(id: string | null) {
|
||||
};
|
||||
}
|
||||
|
||||
// ── Mutations ────────────────────────────────────
|
||||
|
||||
export function useReanalyze() {
|
||||
return useAction((id: string) => messagesApi.reanalyze(id));
|
||||
}
|
||||
|
||||
export function useReanalyzeBatch() {
|
||||
return useAction((guildId: string) => messagesApi.reanalyzeBatch(guildId));
|
||||
}
|
||||
|
||||
// ── Search ───────────────────────────────────────
|
||||
|
||||
export function useMessageSearch(query: string, enabled: boolean) {
|
||||
|
||||
@@ -65,15 +65,6 @@ export const messagesApi = {
|
||||
);
|
||||
},
|
||||
|
||||
reanalyze: (id: string) =>
|
||||
api.post<{ ok: boolean }>(`/api/messages/${id}/reanalyze`, {}),
|
||||
|
||||
reanalyzeBatch: (guildId?: string, channelId?: string) =>
|
||||
api.post<{ ok: boolean; count: number }>("/api/messages/reanalyze-batch", {
|
||||
guildId,
|
||||
channelId,
|
||||
}),
|
||||
|
||||
search: (query: string, limit?: number) => {
|
||||
const params = new URLSearchParams({ q: query });
|
||||
if (limit) params.set("limit", String(limit));
|
||||
|
||||
Reference in New Issue
Block a user