import express from "express"; import fetch from "node-fetch"; import { checkUserQuota, recordUserHit, enqueue } from "../lib/rateLimiter.js"; const router = express.Router(); const INVOKE_URL = "https://ai.api.nvidia.com/v1/genai/black-forest-labs/flux.1-dev"; // POST /api/image { prompt, clientId } router.post("/image", async (req, res) => { try { const { prompt, clientId = "anon" } = req.body; if (!prompt || !prompt.trim()) { return res.status(400).json({ error: "missing_prompt" }); } // حد المستخدم: 3 صور / 3 ساعات const quota = checkUserQuota(clientId); if (!quota.allowed) { const mins = Math.ceil(quota.retryMs / 60000); return res.status(429).json({ error: "rate_limited", message: `وصلت إلى حد 3 صور كل 3 ساعات. حاول مجدداً بعد ${mins} دقيقة.`, retryMs: quota.retryMs, }); } // إعدادات واقعية ودقيقة جداً const payload = { prompt: `${prompt}, ultra realistic, highly detailed, photorealistic, 8k, sharp focus, natural lighting, professional photography`, mode: "base", cfg_scale: 3.5, width: 1024, height: 1024, seed: Math.floor(Math.random() * 1_000_000), steps: 50, }; // عبر الطابور العالمي (<= 40/دقيقة) const data = await enqueue(async () => { const r = await fetch(INVOKE_URL, { method: "POST", headers: { Authorization: `Bearer ${process.env.NVIDIA_API_KEY}`, Accept: "application/json", "Content-Type": "application/json", }, body: JSON.stringify(payload), }); if (r.status !== 200) { const errBody = await r.text(); throw new Error(`NVIDIA ${r.status}: ${errBody}`); } return r.json(); }); recordUserHit(clientId); // FLUX يرجع base64 في artifacts[0].base64 (أو image) const b64 = data?.artifacts?.[0]?.base64 || data?.image || data?.data?.[0]?.b64_json || null; if (!b64) { return res.status(502).json({ error: "no_image", raw: data }); } res.json({ image: `data:image/png;base64,${b64}`, remaining: quota.remaining - 1 }); } catch (err) { console.error("image error:", err); res.status(500).json({ error: "image_failed", message: String(err?.message || err) }); } }); export default router;