From 1ef7b872e3b0d614e657d090708c76f19e520f24 Mon Sep 17 00:00:00 2001 From: liangxu Date: Mon, 27 Apr 2026 21:34:15 +0800 Subject: [PATCH] fix(publish/dongchedi): route imagex traffic through session.fetch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the account session for imagex apply/upload/commit so the requests carry the same cookies/UA the bind console saw, instead of falling out of the session via a bare Node fetch — bytedance imagex was rejecting these as unauthenticated under the bound session. Also align the binding console URL with the v2 publish article path. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../desktop-client/src/main/account-binder.ts | 2 +- .../src/main/adapters/dongchedi.ts | 55 +++++++++++++++---- 2 files changed, 44 insertions(+), 13 deletions(-) diff --git a/apps/desktop-client/src/main/account-binder.ts b/apps/desktop-client/src/main/account-binder.ts index 472d077..d50a175 100644 --- a/apps/desktop-client/src/main/account-binder.ts +++ b/apps/desktop-client/src/main/account-binder.ts @@ -2910,7 +2910,7 @@ const publishBindingDefinitions: Record(input: string, init?: RequestInit): Promise { - const response = await fetch(input, init); +async function imagexFetchText( + context: PublishAdapterContext, + input: string, + init?: RequestInit, +): Promise { + const headers = new Headers(init?.headers); + if (!headers.has("user-agent")) { + headers.set("user-agent", IMAGEX_USER_AGENT); + } + if (!headers.has("accept-language")) { + headers.set("accept-language", IMAGEX_ACCEPT_LANGUAGE); + } + if (!headers.has("accept")) { + headers.set("accept", "application/json, text/plain, */*"); + } + const response = await context.session.fetch(input, { ...init, headers }); const text = await response.text(); if (!response.ok) { throw new Error(text || `request_failed_${response.status}`); } + return text; +} + +async function imagexFetchJson( + context: PublishAdapterContext, + input: string, + init?: RequestInit, +): Promise { + const text = await imagexFetchText(context, input, init); if (!text) { return {} as T; } @@ -191,7 +217,7 @@ function signAWS4(input: { const amzDate = formatAmzDate(now); const dateStamp = formatDateStamp(now); const query = [...url.searchParams.entries()] - .sort(([left], [right]) => left.localeCompare(right)) + .sort(([left], [right]) => (left < right ? -1 : left > right ? 1 : 0)) .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`) .join("&"); const signedHeaders: Record = { @@ -325,7 +351,8 @@ async function uploadImage( date: new Date(token.CurrentTime), }); - const applyResponse = await mainFetchJson( + const applyResponse = await imagexFetchJson( + context, applyUrl.toString(), { headers: signedApplyHeaders, @@ -343,14 +370,17 @@ async function uploadImage( } const buffer = await image.blob.arrayBuffer(); - const putResponse = await fetch(`https://${uploadHost}/${storeUri}`, { + const putHeaders = new Headers({ + "content-disposition": 'attachment; filename="undefined"', + "content-type": "application/octet-stream", + "content-crc32": crc32(new Uint8Array(buffer)), + authorization: storeInfo.Auth, + "user-agent": IMAGEX_USER_AGENT, + "accept-language": IMAGEX_ACCEPT_LANGUAGE, + }); + const putResponse = await context.session.fetch(`https://${uploadHost}/${storeUri}`, { method: "PUT", - headers: { - "content-disposition": 'attachment; filename="undefined"', - "content-type": "application/octet-stream", - "content-crc32": crc32(new Uint8Array(buffer)), - authorization: storeInfo.Auth, - }, + headers: putHeaders, body: image.blob, }); if (!putResponse.ok) { @@ -371,7 +401,8 @@ async function uploadImage( securityToken: token.SessionToken, }); - await mainFetchJson>( + await imagexFetchJson>( + context, commitUrl.toString(), { method: "POST",