Spaces:
Sleeping
Sleeping
Download server.js from Sarvesh3529/Matrix-backend-node.js: direct link, hf CLI and curl.
- Browser
- Download file 4.92 kB
-
https://huggingface.co/spaces/Sarvesh3529/Matrix-backend-node.js/resolve/main/server.js
- Command line
-
hf download hf://spaces/Sarvesh3529/Matrix-backend-node.js/server.js
-
curl -L -o server.js https://huggingface.co/spaces/Sarvesh3529/Matrix-backend-node.js/resolve/main/server.js
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}`); | |
| }); | |