fix: reduce infinite canvas rate limit bursts
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled

This commit is contained in:
Your Name 2026-03-25 15:35:04 +08:00
parent b08b98102a
commit 23c326b1fb
2 changed files with 55 additions and 25 deletions

View File

@ -393,6 +393,9 @@ export default function CanvasEditorPage({ params }: PageProps) {
const previousNodeIdsRef = useRef<Set<string>>(new Set());
const previousBrandForModelRef = useRef("");
const autoSelectedBrandModelBrandRef = useRef("");
const autoTitleAttemptedRef = useRef<Set<string>>(new Set());
const autoTitleInFlightRef = useRef<string | null>(null);
const autoThumbnailSyncedRef = useRef<string | null>(null);
const lastLoadedCanvasIdRef = useRef<string>("");
const requestedAgentMode = mode === "agent";
const isAgentWorkspaceEnabled = siteSettingsData?.agentEnabled !== false;
@ -1325,31 +1328,52 @@ export default function CanvasEditorPage({ params }: PageProps) {
// 智能命名与缩略图逻辑
useEffect(() => {
if (isAgentMode) return;
const hasDefaultTitle =
projectTitle === "未命名画布" || projectTitle === "新画布";
if (!hasDefaultTitle || nodes.length === 0) return;
const imageNode = nodes.find((n) => n.type === "image" && (n.data as any)?.src);
const imageSrc = (imageNode?.data as any)?.src;
if (!imageSrc || typeof imageSrc !== "string") return;
const autoTitleKey = `${id}:${imageSrc}`;
if (
autoTitleAttemptedRef.current.has(autoTitleKey) ||
autoTitleInFlightRef.current === autoTitleKey
) {
return;
}
const autoTitleAndThumbnail = async () => {
// 如果标题是默认值,且有图片节点
if ((projectTitle === "未命名画布" || projectTitle === "新画布") && nodes.length > 0) {
const imageNode = nodes.find(n => n.type === 'image' && (n.data as any)?.src);
const imageSrc = (imageNode?.data as any)?.src;
if (imageSrc) {
try {
// 1. 设置缩略图
await canvasService.updateProject(id, { thumbnail: imageSrc });
// 2. 智能命名
const res = await fetch('/api/canvas/auto-title', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ imageUrl: imageSrc })
});
const data = await res.json();
if (data.title) {
setProjectTitle(data.title);
await canvasService.updateProject(id, { title: data.title });
toast.success(`画布已自动命名为:${data.title}`);
}
} catch (error) {
console.error("Auto title failed:", error);
}
autoTitleAttemptedRef.current.add(autoTitleKey);
autoTitleInFlightRef.current = autoTitleKey;
try {
if (autoThumbnailSyncedRef.current !== autoTitleKey) {
await canvasService.updateProject(id, { thumbnail: imageSrc });
autoThumbnailSyncedRef.current = autoTitleKey;
}
const res = await fetch("/api/canvas/auto-title", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ imageUrl: imageSrc }),
});
if (!res.ok) {
throw new Error(`Auto title request failed: ${res.status}`);
}
const data = await res.json();
if (data.title) {
setProjectTitle(data.title);
await canvasService.updateProject(id, { title: data.title });
toast.success(`画布已自动命名为:${data.title}`);
}
} catch (error) {
console.error("Auto title failed:", error);
} finally {
if (autoTitleInFlightRef.current === autoTitleKey) {
autoTitleInFlightRef.current = null;
}
}
};

View File

@ -2203,7 +2203,6 @@ export function CanvasChatPanel({
// 获取纯 UUID(去掉 canvas- 前缀)用于 API 调用
const rawChatId = toRawCanvasChatId(activeChatId);
const voteSWRKey = rawChatId ? `/api/vote?chatId=${rawChatId}` : null;
const { mutate } = useSWRConfig();
const [votingMessageId, setVotingMessageId] = useState<string | null>(null);
const handleDownloadOriginalImage = useCallback(async (imageUrl: string) => {
@ -2249,6 +2248,13 @@ export function CanvasChatPanel({
revalidateOnReconnect: false,
dedupingInterval: 10000,
});
const initialCanvasMessageCount =
activeChatId === chatId ? initialMessages.length : 0;
const voteSWRKey =
rawChatId &&
(historyData?.messages?.length ?? initialCanvasMessageCount) >= 2
? `/api/vote?chatId=${rawChatId}`
: null;
const { data: messageVotes } = useSWR<MessageVote[]>(
voteSWRKey,
fetcher,