Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 51 additions & 30 deletions app/api/upload/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { v2 as cloudinary, UploadApiResponse } from "cloudinary";
import ImageTracer from "imagetracerjs";
import { NextResponse } from "next/server";
import fetch from "node-fetch";
import { PNG } from "pngjs";
import sharp from "sharp";

cloudinary.config({
cloud_name: process.env.CLOUDINARY_CLOUD_NAME!,
Expand All @@ -17,44 +17,65 @@ async function cloudinaryToSVG(imageUrl: string, options = { scale: 5 }) {
throw new Error(`Failed to fetch image: ${response.statusText}`);
const buffer = Buffer.from(await response.arrayBuffer());

const png = PNG.sync.read(buffer);
const image = sharp(buffer).ensureAlpha(); // ensures RGBA format
const { width, height } = await image.metadata();

if (!width || !height) {
throw new Error("Unable to read image metadata.");
}

const raw = await image.raw().toBuffer();
Comment on lines +20 to +27
Copy link

Copilot AI Oct 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Sharp instance is created twice - once for metadata extraction and once for raw buffer processing. Consider reusing the same instance or chaining operations to avoid redundant processing.

Copilot uses AI. Check for mistakes.

const myImageData = {
width: png.width,
height: png.height,
data: png.data,
width,
height,
data: raw,
};

const svgstring = ImageTracer.imagedataToSVG(myImageData, options);
return svgstring;
}

export async function POST(req: Request) {
const formData = await req.formData();
const file = formData.get("image") as File;
try {
const formData = await req.formData();
const file = formData.get("image") as File;

if (!file) {
return NextResponse.json({ error: "No file uploaded" }, { status: 400 });
}
if (!file) {
return NextResponse.json({ error: "No file uploaded" }, { status: 400 });
}

// Read file as buffer
const bytes = await file.arrayBuffer();
const buffer = Buffer.from(bytes);

const cloudinaryResponse: UploadApiResponse = await new Promise(
(resolve, reject) => {
const uploadStream = cloudinary.uploader.upload_stream((err, result) => {
if (err || !result) return reject(err);
resolve(result);
});
uploadStream.end(buffer);
},
);

const svgString = await cloudinaryToSVG(cloudinaryResponse.secure_url);

return NextResponse.json(
{ url: cloudinaryResponse.secure_url, svg: svgString, success: true },
{ status: 201 },
);
const bytes = await file.arrayBuffer();
const buffer = Buffer.from(bytes);

const cloudinaryResponse: UploadApiResponse = await new Promise(
(resolve, reject) => {
const uploadStream = cloudinary.uploader.upload_stream(
{ resource_type: "image" },
(err, result) => {
if (err || !result) return reject(err);
resolve(result);
}
);
uploadStream.end(buffer);
}
);

const svgString = await cloudinaryToSVG(cloudinaryResponse.secure_url);

return NextResponse.json(
{
url: cloudinaryResponse.secure_url,
svg: svgString,
success: true,
},
{ status: 201 }
);
} catch (err: any) {
console.error("Upload error:", err);
return NextResponse.json(
{ success: false, error: err.message || "Internal Server Error" },
{ status: 500 }
);
}
}
2 changes: 1 addition & 1 deletion app/generate/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ export default function UploadPage() {
{/* Image Preview */}
<div className="mb-4">
<div className="bg-slate-100 dark:bg-neutral-700 rounded-lg p-4 flex items-center justify-center min-h-[200px]">
<Image
<img
src={file.preview}
alt={file.name}
className="max-w-full max-h-64 object-contain rounded-lg shadow-sm"
Expand Down
5 changes: 1 addition & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"pngjs": "^7.0.0",
"react": "19.1.0",
"react-dom": "19.1.0",
"sharp": "^0.34.4",
"tailwind-merge": "^3.3.1"
},
"devDependencies": {
Expand Down
Loading