fix: harden scale adjust product lock uploads

This commit is contained in:
134 2026-07-07 23:40:34 +08:00
parent 30eef41dc4
commit 17f5485522
2 changed files with 189 additions and 102 deletions

View File

@ -3828,57 +3828,66 @@ async function createScaleAdjustPatchReference(params: {
});
const targetDraftBuffer = targetDraftReference.buffer;
const suffix = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
const targetDraftUploadBuffer = await sharp(targetDraftBuffer, { failOn: "none" })
.flatten({ background: "#ffffff" })
.jpeg({ quality: 90, mozjpeg: true })
.toBuffer();
const backgroundPlateUploadBuffer = targetDraftReference.backgroundPlateBuffer
? await sharp(targetDraftReference.backgroundPlateBuffer, { failOn: "none" })
.flatten({ background: "#ffffff" })
.jpeg({ quality: 90, mozjpeg: true })
.toBuffer()
: null;
const uploadOptionalScaleAdjustAsset = async (
filename: string,
buffer: Buffer | null | undefined
) => {
if (!buffer) return null;
try {
return await uploadFile(filename, toArrayBuffer(buffer));
} catch (error) {
console.warn("[Canvas Local Edit] Optional scale adjust asset upload failed:", {
error: error instanceof Error ? error.message : String(error),
filename,
});
return null;
}
};
if (params.fullFrameReferenceOnly && structureLockAppliedRef.value) {
const [
targetDraftFullUpload,
backgroundPlateFullUpload,
productLockOverlayUpload,
productLockProtectMaskUpload,
compositeClipMaskUpload,
productLockEditMaskUpload,
productLockFalEditMaskUpload,
] = await Promise.all([
uploadFile(
`canvas-scale-adjust-full-target-draft-${suffix}.png`,
toArrayBuffer(targetDraftBuffer)
),
targetDraftReference.backgroundPlateBuffer
? uploadFile(
`canvas-scale-adjust-background-plate-${suffix}.png`,
toArrayBuffer(targetDraftReference.backgroundPlateBuffer)
)
: Promise.resolve(null),
const targetDraftFullUpload = await uploadFile(
`canvas-scale-adjust-full-target-draft-${suffix}.jpg`,
toArrayBuffer(targetDraftUploadBuffer)
);
const productLockOverlayUpload = await uploadOptionalScaleAdjustAsset(
`canvas-scale-adjust-product-lock-overlay-${suffix}.png`,
targetDraftReference.productLockOverlayBuffer
? uploadFile(
`canvas-scale-adjust-product-lock-overlay-${suffix}.png`,
toArrayBuffer(targetDraftReference.productLockOverlayBuffer)
)
: Promise.resolve(null),
targetDraftReference.productLockProtectMaskBuffer
? uploadFile(
`canvas-scale-adjust-product-lock-protect-mask-${suffix}.png`,
toArrayBuffer(targetDraftReference.productLockProtectMaskBuffer)
)
: Promise.resolve(null),
targetDraftReference.compositeClipMaskBuffer
? uploadFile(
`canvas-scale-adjust-composite-clip-mask-${suffix}.png`,
toArrayBuffer(targetDraftReference.compositeClipMaskBuffer)
)
: Promise.resolve(null),
targetDraftReference.productLockEditMaskBuffer
? uploadFile(
`canvas-scale-adjust-product-lock-edit-mask-${suffix}.png`,
toArrayBuffer(targetDraftReference.productLockEditMaskBuffer)
)
: Promise.resolve(null),
targetDraftReference.productLockFalEditMaskBuffer
? uploadFile(
`canvas-scale-adjust-product-lock-fal-edit-mask-${suffix}.png`,
toArrayBuffer(targetDraftReference.productLockFalEditMaskBuffer)
)
: Promise.resolve(null),
]);
);
const [productLockEditMaskUpload, productLockFalEditMaskUpload] =
await Promise.all([
uploadOptionalScaleAdjustAsset(
`canvas-scale-adjust-product-lock-edit-mask-${suffix}.png`,
targetDraftReference.productLockEditMaskBuffer
),
uploadOptionalScaleAdjustAsset(
`canvas-scale-adjust-product-lock-fal-edit-mask-${suffix}.png`,
targetDraftReference.productLockFalEditMaskBuffer
),
]);
const [backgroundPlateFullUpload, productLockProtectMaskUpload, compositeClipMaskUpload] =
await Promise.all([
uploadOptionalScaleAdjustAsset(
`canvas-scale-adjust-background-plate-${suffix}.jpg`,
backgroundPlateUploadBuffer
),
uploadOptionalScaleAdjustAsset(
`canvas-scale-adjust-product-lock-protect-mask-${suffix}.png`,
targetDraftReference.productLockProtectMaskBuffer
),
uploadOptionalScaleAdjustAsset(
`canvas-scale-adjust-composite-clip-mask-${suffix}.png`,
targetDraftReference.compositeClipMaskBuffer
),
]);
return {
backgroundPlateFullUrl: backgroundPlateFullUpload?.url || null,
@ -3907,10 +3916,22 @@ async function createScaleAdjustPatchReference(params: {
.png({ compressionLevel: 6 })
.toBuffer(),
]);
const [sourceCropUpload, targetDraftUpload, targetDraftFullUpload] =
await Promise.all([
uploadFile(
`canvas-scale-adjust-workspace-source-${suffix}.png`,
toArrayBuffer(sourceCropBuffer)
),
uploadFile(
`canvas-scale-adjust-workspace-target-draft-${suffix}.png`,
toArrayBuffer(targetDraftCropBuffer)
),
uploadFile(
`canvas-scale-adjust-full-target-draft-${suffix}.jpg`,
toArrayBuffer(targetDraftUploadBuffer)
),
]);
const [
sourceCropUpload,
targetDraftUpload,
targetDraftFullUpload,
backgroundPlateFullUpload,
productLockOverlayUpload,
productLockProtectMaskUpload,
@ -3918,54 +3939,30 @@ async function createScaleAdjustPatchReference(params: {
productLockEditMaskUpload,
productLockFalEditMaskUpload,
] = await Promise.all([
uploadFile(
`canvas-scale-adjust-workspace-source-${suffix}.png`,
toArrayBuffer(sourceCropBuffer)
uploadOptionalScaleAdjustAsset(
`canvas-scale-adjust-background-plate-${suffix}.jpg`,
backgroundPlateUploadBuffer
),
uploadFile(
`canvas-scale-adjust-workspace-target-draft-${suffix}.png`,
toArrayBuffer(targetDraftCropBuffer)
uploadOptionalScaleAdjustAsset(
`canvas-scale-adjust-product-lock-overlay-${suffix}.png`,
targetDraftReference.productLockOverlayBuffer
),
uploadFile(
`canvas-scale-adjust-full-target-draft-${suffix}.png`,
toArrayBuffer(targetDraftBuffer)
uploadOptionalScaleAdjustAsset(
`canvas-scale-adjust-product-lock-protect-mask-${suffix}.png`,
targetDraftReference.productLockProtectMaskBuffer
),
uploadOptionalScaleAdjustAsset(
`canvas-scale-adjust-composite-clip-mask-${suffix}.png`,
targetDraftReference.compositeClipMaskBuffer
),
uploadOptionalScaleAdjustAsset(
`canvas-scale-adjust-product-lock-edit-mask-${suffix}.png`,
targetDraftReference.productLockEditMaskBuffer
),
uploadOptionalScaleAdjustAsset(
`canvas-scale-adjust-product-lock-fal-edit-mask-${suffix}.png`,
targetDraftReference.productLockFalEditMaskBuffer
),
targetDraftReference.backgroundPlateBuffer
? uploadFile(
`canvas-scale-adjust-background-plate-${suffix}.png`,
toArrayBuffer(targetDraftReference.backgroundPlateBuffer)
)
: Promise.resolve(null),
targetDraftReference.productLockOverlayBuffer
? uploadFile(
`canvas-scale-adjust-product-lock-overlay-${suffix}.png`,
toArrayBuffer(targetDraftReference.productLockOverlayBuffer)
)
: Promise.resolve(null),
targetDraftReference.productLockProtectMaskBuffer
? uploadFile(
`canvas-scale-adjust-product-lock-protect-mask-${suffix}.png`,
toArrayBuffer(targetDraftReference.productLockProtectMaskBuffer)
)
: Promise.resolve(null),
targetDraftReference.compositeClipMaskBuffer
? uploadFile(
`canvas-scale-adjust-composite-clip-mask-${suffix}.png`,
toArrayBuffer(targetDraftReference.compositeClipMaskBuffer)
)
: Promise.resolve(null),
targetDraftReference.productLockEditMaskBuffer
? uploadFile(
`canvas-scale-adjust-product-lock-edit-mask-${suffix}.png`,
toArrayBuffer(targetDraftReference.productLockEditMaskBuffer)
)
: Promise.resolve(null),
targetDraftReference.productLockFalEditMaskBuffer
? uploadFile(
`canvas-scale-adjust-product-lock-fal-edit-mask-${suffix}.png`,
toArrayBuffer(targetDraftReference.productLockFalEditMaskBuffer)
)
: Promise.resolve(null),
]);
return {
@ -5349,14 +5346,18 @@ export async function POST(request: NextRequest) {
regions.some((region) =>
isScaleAdjustFurnitureProductLikePrompt(region.prompt)
);
const useScaleAdjustProductLockComposite =
const canUseScaleAdjustProductLockMaskEdit =
useScaleAdjustFullFrameStructurePatch &&
Boolean(scaleAdjustPatchReference?.backgroundPlateFullUrl) &&
Boolean(scaleAdjustPatchReference?.productLockOverlayUrl);
const useScaleAdjustProductLockMaskEdit =
useScaleAdjustProductLockComposite &&
isScaleAdjustMaskCapableModel(resolvedScaleAdjustModelEntry) &&
Boolean(scaleAdjustPatchReference?.productLockEditMaskUrl);
const useScaleAdjustProductLockComposite =
useScaleAdjustFullFrameStructurePatch &&
Boolean(scaleAdjustPatchReference?.productLockOverlayUrl) &&
(Boolean(scaleAdjustPatchReference?.backgroundPlateFullUrl) ||
canUseScaleAdjustProductLockMaskEdit);
const useScaleAdjustProductLockMaskEdit =
useScaleAdjustProductLockComposite &&
canUseScaleAdjustProductLockMaskEdit;
const useScaleAdjustPatchTargetDraftPrimary = Boolean(scaleAdjustPatchReference);
const includeScaleAdjustPatchSourceReference =
Boolean(scaleAdjustPatchReference);

View File

@ -9,6 +9,7 @@ import {
} from "@/lib/db/queries";
import {
enqueueStorageMigrationTask,
getEnabledS3StorageConfigs,
getPreferredAliyunStorageConfig,
isAliyunStorageConfig,
} from "@/lib/storage/migration-db";
@ -491,6 +492,43 @@ async function uploadWithBackupStorageConfig(
}
}
async function uploadWithAnyEnabledStorageConfig(
filename: string,
buffer: ArrayBuffer,
skipStorageConfigIds: Array<string | undefined>
): Promise<
| {
uploaded: { url: string; pathname: string; contentType: string };
storageConfig?: StorageConfig;
}
| null
> {
const skipped = new Set(skipStorageConfigIds.filter(Boolean) as string[]);
try {
const configs = await getEnabledS3StorageConfigs();
for (const storageConfig of configs) {
if (skipped.has(storageConfig.id)) continue;
const s3Config = toDbS3UploadConfig(storageConfig);
if (!s3Config) continue;
try {
logger.debug(
`[Upload] Retrying with enabled S3 config: ${storageConfig.name ?? storageConfig.id}`
);
const uploaded = await uploadToS3(filename, buffer, s3Config);
lastKnownGoodDbS3Config = s3Config;
return { uploaded, storageConfig };
} catch (error) {
logger.error("[Upload] Enabled S3 config retry failed:", error);
}
}
} catch (error) {
logger.error("[Upload] Failed to list enabled S3 configs for retry:", error);
}
return null;
}
// 上传文件到存储服务
export async function uploadFile(
filename: string,
@ -546,6 +584,22 @@ export async function uploadFile(
return uploadedFromBackup.uploaded;
}
const uploadedFromEnabledConfig = await uploadWithAnyEnabledStorageConfig(
filename,
buffer,
[primaryStorageConfigId]
);
if (uploadedFromEnabledConfig) {
await maybeEnqueueAliyunMigrationTask({
uploaded: uploadedFromEnabledConfig.uploaded,
storageConfig: uploadedFromEnabledConfig.storageConfig,
migrationConfig,
}).catch((enqueueError) => {
logger.error("[Upload] Failed to enqueue enabled S3 OSS migration task:", enqueueError);
});
return uploadedFromEnabledConfig.uploaded;
}
if (lastKnownGoodDbS3Config) {
try {
logger.debug("[Upload] Retrying with last known good database S3 config");
@ -572,6 +626,22 @@ export async function uploadFile(
});
return uploadedFromBackup.uploaded;
}
const uploadedFromEnabledConfig = await uploadWithAnyEnabledStorageConfig(
filename,
buffer,
[primaryStorageConfigId]
);
if (uploadedFromEnabledConfig) {
await maybeEnqueueAliyunMigrationTask({
uploaded: uploadedFromEnabledConfig.uploaded,
storageConfig: uploadedFromEnabledConfig.storageConfig,
migrationConfig,
}).catch((enqueueError) => {
logger.error("[Upload] Failed to enqueue enabled S3 OSS migration task:", enqueueError);
});
return uploadedFromEnabledConfig.uploaded;
}
}
const envS3Config = buildEnvS3Config();
@ -651,6 +721,22 @@ export async function createStorageUploadSession(): Promise<StorageUploadFunctio
return uploadedFromBackup.uploaded;
}
const uploadedFromEnabledConfig = await uploadWithAnyEnabledStorageConfig(
filename,
buffer,
[primaryStorageConfigId]
);
if (uploadedFromEnabledConfig) {
await maybeEnqueueAliyunMigrationTask({
uploaded: uploadedFromEnabledConfig.uploaded,
storageConfig: uploadedFromEnabledConfig.storageConfig,
migrationConfig,
}).catch((migrationError) => {
logger.error("[Upload] Failed to enqueue enabled S3 OSS migration task:", migrationError);
});
return uploadedFromEnabledConfig.uploaded;
}
throw error;
}
};