From 2a8125f6d732cb47a9c1e85d53631ee3391cdff9 Mon Sep 17 00:00:00 2001 From: "Khoa.vo" Date: Mon, 5 Jan 2026 15:01:24 +0700 Subject: [PATCH] fix: Cookie parsing for JSON format in upload and generate APIs - Fixed upload route to properly trim and parse JSON-formatted cookies - Added same cookie normalization logic to generate route - Handles pretty-printed JSON with leading newlines from Cookie-Editor export --- app/api/generate/route.ts | 18 ++++++- app/api/references/upload/route.ts | 9 ++-- scripts/test-whisk-upload.ts | 80 ++++++++++++++++++++++++++++++ 3 files changed, 103 insertions(+), 4 deletions(-) create mode 100644 scripts/test-whisk-upload.ts diff --git a/app/api/generate/route.ts b/app/api/generate/route.ts index 1d32a6b..eaffe00 100644 --- a/app/api/generate/route.ts +++ b/app/api/generate/route.ts @@ -12,12 +12,28 @@ export async function POST(req: NextRequest) { // Use cookies provided in request or fallback to server-side logic if implemented later // For now, we expect the frontend to pass the 'whisk_cookies' it has stored. - const cookieString = clientCookies || req.cookies.get('whisk_cookies')?.value; + let cookieString = clientCookies || req.cookies.get('whisk_cookies')?.value; if (!cookieString) { return NextResponse.json({ error: "Whisk cookies not found. Please configure settings." }, { status: 401 }); } + // Normalize cookies: handle JSON-formatted cookies (from Cookie-Editor export) + const trimmedCookies = cookieString.trim(); + if (trimmedCookies.startsWith('[') || trimmedCookies.startsWith('{')) { + try { + const cookieArray = JSON.parse(trimmedCookies); + if (Array.isArray(cookieArray)) { + cookieString = cookieArray + .map((c: any) => `${c.name}=${c.value}`) + .join('; '); + console.log(`[Generate] Parsed ${cookieArray.length} cookies from JSON.`); + } + } catch (e) { + console.warn("[Generate] Failed to parse cookie JSON:", e); + } + } + const client = new WhiskClient(cookieString); // Generate images in parallel if imageCount > 1 diff --git a/app/api/references/upload/route.ts b/app/api/references/upload/route.ts index c173365..0fb43e0 100644 --- a/app/api/references/upload/route.ts +++ b/app/api/references/upload/route.ts @@ -26,10 +26,12 @@ export async function POST(req: Request) { let parsedCookies = ""; // Auto-fix: Handle JSON array (e.g. from Cookie-Editor export) - if (validCookies.startsWith('[') || validCookies.startsWith('{')) { + // Check for JSON after trimming (handles pretty-printed JSON with leading newlines) + const trimmedForCheck = validCookies.trim(); + if (trimmedForCheck.startsWith('[') || trimmedForCheck.startsWith('{')) { isJson = true; try { - const cookieArray = JSON.parse(validCookies); + const cookieArray = JSON.parse(trimmedForCheck); if (Array.isArray(cookieArray)) { // Convert ALL cookies to a string, not just 1PSID @@ -71,7 +73,8 @@ export async function POST(req: Request) { } console.log(`[API] Uploading reference image (${category}, ${mimeType})...`); - console.log(`[API] Using cookies (first 50 chars): ${validCookies.substring(0, 50)}...`); + console.log(`[API] Using cookies (first 100 chars): ${validCookies.substring(0, 100)}...`); + console.log(`[API] Cookie was JSON: ${isJson}, parsed cookies count: ${parsedCookies.split(';').length}`); const client = new WhiskClient(validCookies); diff --git a/scripts/test-whisk-upload.ts b/scripts/test-whisk-upload.ts new file mode 100644 index 0000000..4c14d52 --- /dev/null +++ b/scripts/test-whisk-upload.ts @@ -0,0 +1,80 @@ + +import { WhiskClient } from '../lib/whisk-client'; + +// User-provided Whisk cookies +const WHISK_COOKIES = JSON.stringify([ + { + "domain": "labs.google", + "hostOnly": true, + "httpOnly": true, + "name": "__Host-next-auth.csrf-token", + "path": "/", + "sameSite": "lax", + "secure": true, + "session": true, + "storeId": null, + "value": "7485620ca6c96e35b3949dc2ddc79dcf4419285e50534bdba72575e4901324a1%7C1a14ffc355e181cb4ef8c01a6446d33c583ca940be54c20e157c1851a3313e8f" + }, + { + "domain": "labs.google", + "hostOnly": true, + "httpOnly": true, + "name": "__Secure-next-auth.callback-url", + "path": "/", + "sameSite": "lax", + "secure": true, + "session": true, + "storeId": null, + "value": "https%3A%2F%2Flabs.google%2Ffx%2Ftools%2Fmusic-fx%2Funsupported-country" + }, + { + "domain": "labs.google", + "expirationDate": 1770190006.527353, + "hostOnly": true, + "httpOnly": true, + "name": "__Secure-next-auth.session-token", + "path": "/", + "sameSite": "lax", + "secure": true, + "session": false, + "storeId": null, + "value": "eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIn0..sNTX3pzNatN34d3B.CNKFzpp_4ZYRyVnB2zyVa_4kGxtHgcIgYxD_zuV1oNW5bjQteCUm83gNr3o0kM4pVYk8zkrfeWvJj9D6JbH8pu2ylhtRlI8__iGhL6ZrS2iRZ8Ia1ojVMrXnFMttv6J6PVZEsPTBaI__O4Fqe4sVp8GIkBya5PTq_hmaYFKgAao4jplGrl7f63_IYzWwUmNs5BOwQtIxfJiTulm3333jVaeazfnq3jSC93aPiPM5z-XTtV2vE1gxJqNbAyJXgli_i-aoIUMB9d-sy7YWLAdwSPZoANp8tuXjd62AaZ74IJ-9lnKUoasBnU2oogDPcArIgD5W1Bacf-Snb131_uQ_uidmvGNmk6-JfFRpfjAVTL7c_S00IDowJzODQOJ2kLVYy8hFgGfuxDGFfOCBLztrTCTmmlWB_yGHjmUSr8mrI2_fn6cDOiAEoZ75M70NU-v3oJB14tr1h3r4jmUAVwbT7Knh2fCjpv-DT1yk3kzqPHpLrdL4cx3liq0mYoszZ8W5bM0IJiKueLVXulIEpHxUkKzDglMEud0ABGWFfWO4UfZnCc8XJXRf1aLIuA3q_DgvlnCH2Wo5go7OdPR6HFKqzOASGoxpm6f1qQMhiDzFn5-Fs_wyenGauJfwap9qwINZxQWSA1_5jl6VSRbFcHMQghB0tJiMhIwV9XPlENafIszvQAVIhpA9ACniG_Mxb8wBWuYFtnphlK4w3ehTxus15G8P0CtaPWJn4I40DyHZnZqulDU2y05ozW-D66LkIGFuUlO-PpHTPQvh2hlL5fq6ac92iN_eZuSLI3qPNCv11PHRhXzDj87S3J2fRc4dzKAN6PUooSZTSQSYTJa5MJVhP1wnRdfLoEfLJ7oCt7XnLNqIEas7J3hi55s3oATuT93fi-edjF7gnZaM4zEKQKqxGKFbOmuiglV7eFADRxkGz7vEZ-kjxvPgEtO4jVCxE-SJ_s1e067bpkYVy9IHZXhJOUO61bCQykIwjlZckP2Fz2VF1W6HYnc.AXU0oelImaOeGzkoH7OxRg" + } +]); + +// Use a small test image (1x1 red pixel PNG) +const TINY_TEST_IMAGE_BASE64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8DwHwAFBQIAX8jx0gAAAABJRU5ErkJggg=="; + +async function testUpload() { + console.log("=== Whisk Upload Debug Test ===\n"); + + const client = new WhiskClient(WHISK_COOKIES); + + try { + console.log("1. Testing Subject upload..."); + const subjectId = await client.uploadReferenceImage(TINY_TEST_IMAGE_BASE64, "image/png", "subject"); + console.log(` ✅ Subject upload SUCCESS: ${subjectId}`); + } catch (e: any) { + console.error(` ❌ Subject upload FAILED: ${e.message}`); + } + + try { + console.log("\n2. Testing Scene upload..."); + const sceneId = await client.uploadReferenceImage(TINY_TEST_IMAGE_BASE64, "image/png", "scene"); + console.log(` ✅ Scene upload SUCCESS: ${sceneId}`); + } catch (e: any) { + console.error(` ❌ Scene upload FAILED: ${e.message}`); + } + + try { + console.log("\n3. Testing Style upload..."); + const styleId = await client.uploadReferenceImage(TINY_TEST_IMAGE_BASE64, "image/png", "style"); + console.log(` ✅ Style upload SUCCESS: ${styleId}`); + } catch (e: any) { + console.error(` ❌ Style upload FAILED: ${e.message}`); + } + + console.log("\n=== Test Complete ==="); +} + +testUpload();