Sarvesh3529's picture
Update server.js
ce5356c verified
Raw History Blame Contribute Delete
4.92 kB
import fs from "fs";
import path from "path";
import express from "express";
import axios from "axios";
import sharp from "sharp";
import cors from "cors";
import FormData from "form-data";
import dotenv from "dotenv";
import os from "os";
dotenv.config({ path: ".env.local" });
const app = express();
const PORT = process.env.PORT || 5000;
const ACCOUNT_ID = process.env.ACCOUNT_ID;
const API_TOKEN = process.env.API_TOKEN;
console.log("ACCOUNT_ID:", process.env.ACCOUNT_ID);
console.log("API_TOKEN exists:", !!process.env.API_TOKEN);
console.log("HF_TOKEN exists:", !!process.env.HF_TOKEN);
app.use(cors({ origin: "*" }));
app.use(express.json());
function normalizePrompt(userPrompt) {
return `Create exactly this object and no other object: "${userPrompt}". Center it as one complete product-shaped object on a plain white background. Use a clean solid dark silhouette suitable for 3D reconstruction. Do not generate a bottle unless the requested object is a bottle. No text, no labels, no scene, no shadows, no extra objects.`;
}
app.post("/api/generate-heightmap", async (req, res) => {
const prompt = String(req.body?.prompt || "").trim();
if (!prompt) {
return res.status(400).json({ error: "Prompt is required" });
}
const normalizedPrompt = normalizePrompt(prompt);
res.set("Cache-Control", "no-store");
try {
console.log(`[HOP 1] βœ… Request received from frontend. Prompt: "${prompt}"`);
// 1. Generate image (Cloudflare)
console.log('⏳ [STEP 1] Calling Cloudflare...');
const response = await axios.post(
`https://api.cloudflare.com/client/v4/accounts/${ACCOUNT_ID}/ai/run/@cf/black-forest-labs/flux-1-schnell`,
{ prompt: normalizedPrompt },
{ headers: { Authorization: `Bearer ${API_TOKEN}` } }
);
console.log(`[HOP 2] βœ… Cloudflare responded. Status: ${response.status}`);
if (!response.data?.result?.image) {
return res.status(500).json({ error: "Cloudflare did not return an image" });
}
const base64Image = response.data.result.image;
const inputBuffer = Buffer.from(base64Image, "base64");
// 2. Send image to Python service for background removal (Explicitly targeting 8080)
console.log('⏳ [STEP 2] Calling Python Worker...');
const formData = new FormData();
formData.append('file', inputBuffer, {
filename: 'image.png',
contentType: 'image/png',
});
const pythonWorkerUrl = process.env.PYTHON_WORKER_URL || "http://localhost:8080";
const pythonServiceResponse = await axios.post(
`${pythonWorkerUrl.replace(/\/$/, "")}/remove-bg`,
formData,
{
headers: { ...formData.getHeaders() },
responseType: 'arraybuffer',
timeout: 10000 // 10s absolute network cutoff guard
}
);
console.log(`[HOP 3] βœ… Python service responded. Status: ${pythonServiceResponse.status}`);
const noBgBuffer = Buffer.from(pythonServiceResponse.data);
// Safety verification check of incoming image data bounds
if (!noBgBuffer || noBgBuffer.length < 100) {
throw new Error("Received an invalid or empty image buffer from the Python background removal worker.");
}
// 3. Clean image for heightmap with explicit promise wrapping protection
console.log(`[HOP 4] ⏳ Processing with Sharp...`);
const processed = await Promise.race([
sharp(noBgBuffer)
.ensureAlpha()
.flatten({ background: { r: 255, g: 255, b: 255 } })
.resize(128, 128)
.grayscale()
.threshold(240)
.negate({ alpha: false })
.png()
.toBuffer(),
new Promise((_, reject) => setTimeout(() => reject(new Error("Sharp pipeline timed out")), 5000))
]);
console.log(`[HOP 4] βœ… Sharp processing complete. Output size: ${processed.length} bytes`);
// --- DEBUG LAYER: SAVE TO DISK ---
const debugPath = path.join(os.tmpdir(), "debug_output.jpg");
fs.writeFileSync(debugPath, processed);
console.log(`[DEBUG] πŸ’Ύ Image saved locally to: ${debugPath}`);
// ---------------------------------
const finalBase64 = processed.toString("base64");
return res.json({ image: `data:image/png;base64,${finalBase64}`, prompt });
} catch (error) {
console.error(`[ERROR] ❌ Pipeline failed at some stage:`);
console.error(`[ERROR] Message: ${error.message}`);
return res.status(500).json({
error: "generation failed",
details: error.message
});
}
});
app.listen(PORT, () => {
console.log(`πŸš€ 3D PROXY ENGINE RUNNING ON port ${PORT}`);
});