Spaces:
Sleeping
Sleeping
File size: 4,922 Bytes
160d275 ce5356c 160d275 ce5356c 160d275 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | 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}`);
});
|