|
|
@ -8,6 +8,24 @@ export const maxDuration = 300; // 5 minute timeout for large image operations |
|
|
const IMAGES_FOLDER = "inputs"; |
|
|
const IMAGES_FOLDER = "inputs"; |
|
|
const LEGACY_IMAGES_FOLDER = ".images"; // For backward compatibility
|
|
|
const LEGACY_IMAGES_FOLDER = ".images"; // For backward compatibility
|
|
|
|
|
|
|
|
|
|
|
|
// Helper to extract MIME type and extension from data URL
|
|
|
|
|
|
function getMimeAndExtension(dataUrl: string): { mime: string; extension: string } { |
|
|
|
|
|
const match = dataUrl.match(/^data:(image\/\w+);base64,/); |
|
|
|
|
|
if (match) { |
|
|
|
|
|
const mime = match[1]; |
|
|
|
|
|
const mimeToExt: Record<string, string> = { |
|
|
|
|
|
"image/png": "png", |
|
|
|
|
|
"image/jpeg": "jpg", |
|
|
|
|
|
"image/jpg": "jpg", |
|
|
|
|
|
"image/gif": "gif", |
|
|
|
|
|
"image/webp": "webp", |
|
|
|
|
|
}; |
|
|
|
|
|
return { mime, extension: mimeToExt[mime] || "png" }; |
|
|
|
|
|
} |
|
|
|
|
|
// Default to PNG if no MIME type found
|
|
|
|
|
|
return { mime: "image/png", extension: "png" }; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// POST: Save an image to the workflow's inputs or generations folder
|
|
|
// POST: Save an image to the workflow's inputs or generations folder
|
|
|
export async function POST(request: NextRequest) { |
|
|
export async function POST(request: NextRequest) { |
|
|
let workflowPath: string | undefined; |
|
|
let workflowPath: string | undefined; |
|
|
@ -80,8 +98,9 @@ export async function POST(request: NextRequest) { |
|
|
); |
|
|
); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Construct file path
|
|
|
// Extract MIME type and determine file extension
|
|
|
const filename = `${imageId}.png`; |
|
|
const { extension } = getMimeAndExtension(imageData); |
|
|
|
|
|
const filename = `${imageId}.${extension}`; |
|
|
const filePath = path.join(targetFolder, filename); |
|
|
const filePath = path.join(targetFolder, filename); |
|
|
|
|
|
|
|
|
// Extract base64 data and convert to buffer
|
|
|
// Extract base64 data and convert to buffer
|
|
|
@ -157,8 +176,8 @@ export async function GET(request: NextRequest) { |
|
|
); |
|
|
); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Construct file path - check folders in order based on hint
|
|
|
// Construct file path - check folders and extensions in order
|
|
|
const filename = `${imageId}.png`; |
|
|
const possibleExtensions = ["png", "jpg", "jpeg", "gif", "webp"]; |
|
|
const inputsFolder = path.join(workflowPath, IMAGES_FOLDER); |
|
|
const inputsFolder = path.join(workflowPath, IMAGES_FOLDER); |
|
|
const generationsFolder = path.join(workflowPath, "generations"); |
|
|
const generationsFolder = path.join(workflowPath, "generations"); |
|
|
const legacyFolder = path.join(workflowPath, LEGACY_IMAGES_FOLDER); |
|
|
const legacyFolder = path.join(workflowPath, LEGACY_IMAGES_FOLDER); |
|
|
@ -169,20 +188,26 @@ export async function GET(request: NextRequest) { |
|
|
: [inputsFolder, generationsFolder, legacyFolder]; |
|
|
: [inputsFolder, generationsFolder, legacyFolder]; |
|
|
|
|
|
|
|
|
let filePath: string | null = null; |
|
|
let filePath: string | null = null; |
|
|
|
|
|
let foundExtension = "png"; // Track which extension was found
|
|
|
|
|
|
|
|
|
// Check each folder in order
|
|
|
// Check each folder and extension combination in order
|
|
|
for (const searchFolder of searchOrder) { |
|
|
for (const searchFolder of searchOrder) { |
|
|
const candidatePath = path.join(searchFolder, filename); |
|
|
for (const ext of possibleExtensions) { |
|
|
try { |
|
|
const filename = `${imageId}.${ext}`; |
|
|
await fs.access(candidatePath); |
|
|
const candidatePath = path.join(searchFolder, filename); |
|
|
filePath = candidatePath; |
|
|
try { |
|
|
if (searchFolder === legacyFolder) { |
|
|
await fs.access(candidatePath); |
|
|
logger.info('file.load', 'Found image in legacy .images folder', { filePath }); |
|
|
filePath = candidatePath; |
|
|
|
|
|
foundExtension = ext; |
|
|
|
|
|
if (searchFolder === legacyFolder) { |
|
|
|
|
|
logger.info('file.load', 'Found image in legacy .images folder', { filePath }); |
|
|
|
|
|
} |
|
|
|
|
|
break; |
|
|
|
|
|
} catch { |
|
|
|
|
|
// File not found with this extension, try next
|
|
|
} |
|
|
} |
|
|
break; |
|
|
|
|
|
} catch { |
|
|
|
|
|
// File not found in this folder, try next
|
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
if (filePath) break; // Stop searching if file was found
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (!filePath) { |
|
|
if (!filePath) { |
|
|
@ -202,9 +227,12 @@ export async function GET(request: NextRequest) { |
|
|
// Read the image file
|
|
|
// Read the image file
|
|
|
const buffer = await fs.readFile(filePath); |
|
|
const buffer = await fs.readFile(filePath); |
|
|
|
|
|
|
|
|
// Convert to base64 data URL
|
|
|
// Convert to base64 data URL with correct MIME type
|
|
|
const base64 = buffer.toString("base64"); |
|
|
const base64 = buffer.toString("base64"); |
|
|
const dataUrl = `data:image/png;base64,${base64}`; |
|
|
const mimeType = foundExtension === "jpg" || foundExtension === "jpeg" |
|
|
|
|
|
? "image/jpeg" |
|
|
|
|
|
: `image/${foundExtension}`; |
|
|
|
|
|
const dataUrl = `data:${mimeType};base64,${base64}`; |
|
|
|
|
|
|
|
|
logger.info('file.load', 'Workflow image loaded successfully', { |
|
|
logger.info('file.load', 'Workflow image loaded successfully', { |
|
|
filePath, |
|
|
filePath, |
|
|
|