From b7ea3e21a301cea4fcccb8b6a03496f9cd41916e Mon Sep 17 00:00:00 2001 From: J Date: Fri, 12 Jun 2026 17:32:08 +0800 Subject: [PATCH] fix: avoid persisting signed draft attachment urls Co-authored-by: multica-agent --- packages/views/modals/create-issue.test.tsx | 9 ++++++++- packages/views/modals/create-issue.tsx | 15 +++++++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/packages/views/modals/create-issue.test.tsx b/packages/views/modals/create-issue.test.tsx index 6ff022372..8e14adf72 100644 --- a/packages/views/modals/create-issue.test.tsx +++ b/packages/views/modals/create-issue.test.tsx @@ -490,10 +490,17 @@ describe("CreateIssueModal", () => { expect.objectContaining({ id: "11111111-2222-3333-4444-555555555555", filename: "shot.png", + download_url: "", }), ], }); }); + const draftAttachmentsCall = mockSetDraft.mock.calls.find( + ([patch]) => Array.isArray(patch.attachments), + )?.[0] as { attachments?: Array<{ download_url: string }> } | undefined; + expect(draftAttachmentsCall?.attachments?.[0]?.download_url).not.toContain( + "Signature=", + ); }); it("reuses draft attachments after reopening manual create so pasted images can render and bind", async () => { @@ -509,7 +516,7 @@ describe("CreateIssueModal", () => { uploader_id: "user-1", filename: "shot.png", url: "https://cdn.example.test/shot.png", - download_url: "https://cdn.example.test/shot.png?Signature=fresh", + download_url: "", markdown_url: "https://multica-api.copilothub.ai/api/attachments/11111111-2222-3333-4444-555555555555/download", content_type: "image/png", size_bytes: 123, diff --git a/packages/views/modals/create-issue.tsx b/packages/views/modals/create-issue.tsx index dac6f3326..8b6cc384d 100644 --- a/packages/views/modals/create-issue.tsx +++ b/packages/views/modals/create-issue.tsx @@ -18,7 +18,7 @@ import { } from "lucide-react"; import { cn } from "@multica/ui/lib/utils"; import { toast } from "sonner"; -import type { Issue, IssueStatus, IssuePriority, IssueAssigneeType } from "@multica/core/types"; +import type { Issue, IssueStatus, IssuePriority, IssueAssigneeType, Attachment } from "@multica/core/types"; import { contentReferencesAttachment } from "@multica/core/types"; import { DialogContent, @@ -58,6 +58,17 @@ import { PillButton } from "../common/pill-button"; import { IssuePickerModal } from "./issue-picker-modal"; import { useT } from "../i18n"; +function toDraftAttachment(attachment: Attachment): Attachment { + return { + ...attachment, + // `download_url` is minted for the current API response and may be a + // short-lived signed URL. Drafts survive across dialog closes and app + // restarts, so persist only durable fields and let render/download paths + // re-resolve through id/markdown_url when needed. + download_url: "", + }; +} + // --------------------------------------------------------------------------- // ManualCreatePanel — manual-mode body of the create-issue dialog. Renders // DialogContent + everything inside; the surrounding `` is owned by @@ -156,7 +167,7 @@ export function ManualCreatePanel({ useIssueDraftStore.getState().draft.attachments ?? []; const attachments = currentAttachments.some((a) => a.id === result.id) ? currentAttachments - : [...currentAttachments, result]; + : [...currentAttachments, toDraftAttachment(result)]; setDraft({ attachments }); } return result;