const express = require("express"); const cors = require("cors"); const morgan = require("morgan"); const helmet = require("helmet"); const chalk = require("chalk"); const app = express(); // Security and Logging Middlewares app.use(helmet()); app.use(cors()); app.use(express.json({ limit: "20mb" })); // Custom Morgan token to log current local time cleanly morgan.token("local-time", () => chalk.gray(`[${new Date().toLocaleTimeString()}]`)); app.use(morgan(":local-time :method :url :status :res[content-length] - :response-time ms")); // Root endpoint to check status app.get("/", (req, res) => { res.json({ status: "ok", service: "Gemini REST Bridge", live: true }); }); // Main POST endpoint for Gemini Audio/Text Generation app.post("/", async (req, res) => { const { apiKey, model = "gemini-2.5-flash-preview-tts", prompt, generationConfig } = req.body; if (!apiKey) { console.log(chalk.yellow(` -> [Warning] Request rejected: Missing apiKey`)); return res.status(400).json({ error: "Missing apiKey" }); } if (!prompt) { console.log(chalk.yellow(` -> [Warning] Request rejected: Missing prompt`)); return res.status(400).json({ error: "Missing prompt" }); } const logPrefix = chalk.cyan(`[Gemini Request]`); console.log(`${logPrefix} Processing prompt (${prompt.length} chars) using model: ${chalk.magenta(model)}`); try { const url = `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${apiKey}`; const defaultConfig = { responseModalities: ["AUDIO"], speechConfig: { voiceConfig: { prebuiltVoiceConfig: { voiceName: "Kore" // Options: Puck, Charon, Kore, Fenrir, Aoede } } } }; const response = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ contents: [{ parts: [{ text: prompt }] }], generationConfig: generationConfig || defaultConfig }) }); const data = await response.json(); if (!response.ok) { console.log(chalk.red(` -> [Gemini API Error] Status: ${response.status}`)); // ERROR DEBUGGING LOG: Agar Google error de raha hai toh uska structure bhi print hoga console.log(chalk.red("šŸ‘‰ [DEBUG GOOGLE API ERROR]:"), JSON.stringify(data, null, 2)); return res.status(response.status).json({ error: "Gemini API Error", details: data }); } const parts = data?.candidates?.[0]?.content?.parts || []; const textPart = parts.find(p => p.text); const audioPart = parts.find(p => p.inlineData); const usage = data.usageMetadata || {}; // Advanced Success Logs console.log(chalk.green(` -> [Success] Model responded successfully.`)); console.log(` | Audio Generated: ${audioPart ? chalk.green("Yes") : chalk.yellow("No")}`); if (audioPart) { console.log(` | Audio MimeType: ${chalk.blue(audioPart.inlineData.mimeType)} (${audioPart.inlineData.data.length} base64 chars)`); } if (usage.totalTokenCount) { console.log(` | Token Usage -> Input: ${usage.promptTokenCount} | Output: ${usage.candidatesTokenCount} | Total: ${chalk.bold(usage.totalTokenCount)}`); } // --- NEW DEBUGGING LOG FOR FRONTEND STRUCTURING --- const finalPayload = { text: textPart?.text || null, audio: audioPart ? { mimeType: audioPart.inlineData.mimeType, data: audioPart.inlineData.data.substring(0, 100) + "... [Truncated for Logs]" // Base64 bada hota hai isliye sirf starting ka part dikhega log me } : null, usage: usage || null }; console.log(chalk.magenta.bold("\nšŸ‘‰ [DEBUG RESPONSE STRUCTURE SENT TO FRONTEND]:")); console.log(JSON.stringify(finalPayload, null, 2), "\n"); // ------------------------------------------------- // Actual client response with complete base64 data res.json({ text: textPart?.text || null, audio: audioPart ? { mimeType: audioPart.inlineData.mimeType, data: audioPart.inlineData.data } : null, usage: usage || null }); } catch (err) { console.error(chalk.red(` -> [Server Fatal Error]:`), err); res.status(500).json({ error: err.message || "Internal Server Error" }); } }); // Port handling for Hugging Face const PORT = process.env.PORT || 7860; app.listen(PORT, "0.0.0.0", () => { console.log(chalk.green.bold(`\nšŸš€ Gemini REST Bridge is live on http://0.0.0.0:${PORT}\n`)); });