From f953963b3b74bd7b6507e7fb23823e8ddc079bcc Mon Sep 17 00:00:00 2001 From: Mannu Date: Thu, 28 May 2026 22:37:46 +0530 Subject: [PATCH] Fix memories disappearing + always-processing state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three bugs fixed: 1. Vision failure was marking memories as 'failed', triggering orphan cleanup that permanently deleted them. Vision is optional — on error, now marks 'ready' so the image stays visible without AI captions. 2. Thumbnail failure was blocking vision from running (rejected promise swallowed the chain). Fixed by catching thumbnail error separately so processMemoryVision always executes and always sets a final status. 3. /api/img proxy was rejecting thumbnail keys (families/{id}/thumbnails/…) because 'families/' was not in ALLOWED_PREFIXES. Added it. Also: replaced the full-bleed 'Processing…' overlay with a small corner badge so the uploaded photo is visible immediately after upload. Co-Authored-By: Claude Sonnet 4.6 --- src/app/(app)/memories/page.tsx | 6 +++--- src/app/api/img/route.ts | 2 +- src/app/api/memories/[id]/confirm/route.ts | 4 +++- src/lib/ai/vision.ts | 4 +++- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/app/(app)/memories/page.tsx b/src/app/(app)/memories/page.tsx index 4a4675f..03ba0f9 100644 --- a/src/app/(app)/memories/page.tsx +++ b/src/app/(app)/memories/page.tsx @@ -563,9 +563,9 @@ function MemoryTile({ memory, folder, onClick }: { memory: Memory; folder: Folde {memory.processingStatus === "processing" && ( -
- Processing… -
+ + ⏳ + )} {memory.isPrivate && 🔒} diff --git a/src/app/api/img/route.ts b/src/app/api/img/route.ts index 6999d01..32b2986 100644 --- a/src/app/api/img/route.ts +++ b/src/app/api/img/route.ts @@ -1,7 +1,7 @@ import { S3Client, GetObjectCommand } from "@aws-sdk/client-s3"; import { NextRequest, NextResponse } from "next/server"; -const ALLOWED_PREFIXES = ["avatars/", "profiles/", "memories/", "thumbnails/"]; +const ALLOWED_PREFIXES = ["avatars/", "profiles/", "memories/", "thumbnails/", "families/"]; export async function GET(req: NextRequest) { const key = req.nextUrl.searchParams.get("key"); diff --git a/src/app/api/memories/[id]/confirm/route.ts b/src/app/api/memories/[id]/confirm/route.ts index 2262b04..3989c25 100644 --- a/src/app/api/memories/[id]/confirm/route.ts +++ b/src/app/api/memories/[id]/confirm/route.ts @@ -84,10 +84,12 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id: } } - // Mark as processing and start the media pipeline + // Mark as processing and start the media pipeline. + // thumbnail failure is non-fatal — always proceed to vision, which marks 'ready'. await sql`UPDATE memories SET processing_status = 'processing', updated_at = now() WHERE id = ${id}`; generateThumbnail(id) + .catch(e => console.error(`[thumbnail] id=${id}`, e)) // swallow so vision always runs .then(() => processMemoryVision(id)) .catch(e => console.error(`[memory pipeline] id=${id}`, e)); diff --git a/src/lib/ai/vision.ts b/src/lib/ai/vision.ts index b36547e..4576015 100644 --- a/src/lib/ai/vision.ts +++ b/src/lib/ai/vision.ts @@ -125,7 +125,9 @@ export async function processMemoryVision(memoryId: string): Promise { `; } catch (e) { console.error(`[vision] Failed for memory ${memoryId}:`, e); - await sql`UPDATE memories SET processing_status = 'failed', updated_at = now() WHERE id = ${memoryId}`; + // Vision is optional — mark as ready so the image is still visible. + // Only the confirm step (HeadObject failure) should mark as 'failed'. + await sql`UPDATE memories SET processing_status = 'ready', updated_at = now() WHERE id = ${memoryId}`; await logAudit({ action: "vision_processing_failed", metadata: { memoryId, error: String(e) },