import express from 'express'; import axios from 'axios'; import crypto from 'crypto'; import FormData from 'form-data'; // Import modul form-data untuk menghandle multipart import Groq from 'groq-sdk'; import bytes from 'bytes'; import { feloAI } from '../lib/feloAI.js'; import { toAnime } from '../lib/toanime.js'; import { convertWebpToPng } from '../lib/converter.js'; const APIrouter = express.Router(); const ISauthenticate = async (req, res, next) => { try { // Contoh sederhana: menggunakan menit UTC sebagai secret const secretResponse = new Date().getUTCMinutes().toString(); const generatedApiKey = generateApiKey(secretResponse); console.log(generatedApiKey); console.log(req.headers); const authHeader = req.headers['authorization']; if (!authHeader || authHeader !== `Bearer ${generatedApiKey}`) { return res.status(403).json({ success: false, message: 'Unauthorized' }); } next(); } catch (error) { console.error('Authentication error:', error); return res.status(500).json({ success: false, message: 'Internal Server Error' }); } }; // Inisialisasi Groq SDK const client = new Groq({ apiKey: process.env.GROQ_API_KEY || "", dangerouslyAllowBrowser: true, }); APIrouter.get('/', (req, res) => { res.send('Hello World'); }); APIrouter.get('/msecret', (req, res) => { const secret = new Date().getUTCMinutes().toString(); res.json({ msec: secret }); }); APIrouter.post('/gpt/completions', ISauthenticate, async (req, res) => { const { messages, model, temperature, max_completion_tokens, top_p, stream, stop, searchMode } = req.body; try { // Set header untuk streaming (Server-Sent Events) res.setHeader('Content-Type', 'text/event-stream'); res.setHeader('Cache-Control', 'no-cache'); res.setHeader('Connection', 'keep-alive'); // Mode tanpa search (normal GPT response stream) if (searchMode === false) { const chatStream = await client.chat.completions.create({ messages, model, temperature, max_completion_tokens, top_p, stream, stop, }); // Kirim setiap chunk stream ke client for await (const chunk of chatStream) { res.write(`data: ${JSON.stringify(chunk)}\n\n`); } res.write('data: [DONE]\n\n'); return res.end(); } // Mode dengan search (tool use) else if (searchMode === true) { // Definisi fungsi searchEngine const searchEngine = async (query, langcode = 'en-US') => { try { console.log("searching..."); console.log("query:", query); console.log("lang:", langcode); const searchResult = await feloAI(query, langcode); // Susun hasil pencarian let result = { answer: "", source: [] }; result.answer = searchResult.answer; for (let i = 0; i < 5; i++) { result.source.push({ title: searchResult.source[i].title, link: searchResult.source[i].link, snippet: searchResult.source[i].snippet }); } return result; } catch (error) { console.error(error); return null; } }; // Definisi tool untuk searchEngine const tools = [ { type: "function", function: { name: "searchEngine", description: "Mencari informasi secara real-time dengan search engine berdasarkan query dan target daerah.", parameters: { type: "object", properties: { query: { type: "string", description: "Query pencarian untuk search engine." }, langcode: { type: "string", description: "Kode atau nama daerah target (misalnya 'id-MM', 'en-US', atau lainnya)." } }, required: ["query", "langcode"] } } } ]; // Panggilan awal ke Groq untuk mendapatkan tool call const response = await client.chat.completions.create({ model: model, messages, stream: false, tools, tool_choice: "auto", max_completion_tokens: 4096 }); const responseMessage = response.choices[0].message; console.log("Response message:", JSON.stringify(responseMessage, null, 2)); // Hapus properti yang tidak didukung if (responseMessage.reasoning) { delete responseMessage.reasoning; } const toolCalls = responseMessage.tool_calls || []; if (toolCalls.length > 0) { const availableFunctions = { searchEngine }; // Tambahkan respons awal ke array pesan messages.push(responseMessage); // Eksekusi setiap tool call for (const toolCall of toolCalls) { const functionName = toolCall.function.name; const functionToCall = availableFunctions[functionName]; const functionArgs = JSON.parse(toolCall.function.arguments); const functionResponse = await functionToCall( functionArgs.query, functionArgs.langcode ); messages.push({ tool_call_id: toolCall.id, role: "tool", name: functionName, content: JSON.stringify(functionResponse) }); } } // Panggilan kedua dengan opsi streaming const secondResponseStream = await client.chat.completions.create({ model: model, messages, stream, }); let finalContent = ""; try { for await (const chunk of secondResponseStream) { // Ambil chunk respons (sesuaikan dengan struktur output model) const contentChunk = (chunk.choices && chunk.choices[0].delta && chunk.choices[0].delta.content) || ""; finalContent += contentChunk; res.write(`data: ${JSON.stringify(chunk)}\n\n`); } } catch (err) { console.error("Error processing stream:", err); } res.write('data: [DONE]\n\n'); res.end(); } } catch (error) { console.error('GPT completion error:', error); res.status(500).json({ message: 'Internal Server Error' }); } }); APIrouter.get('/gpt/modellist', ISauthenticate, async (req, res) => { try { const models = await client.models.list(); res.json(models); } catch (error) { console.error('GPT completion error:', error); res.status(500).json({ message: 'Internal Server Error' }); } }); APIrouter.post('/toanime', async (req, res) => { try { console.log(req.body) const { images } = req.body if (!images) return res.json({ success: false, message: 'Required an images!' }) if (/^(https?|http):\/\//i.test(images)) { const data_img = await axios.request({ method: "GET", url: images, responseType: "arraybuffer" }) const response = await toAnime({ imgBuffer: data_img.data }); //const type_img = await fileTypeFromBuffer(response) //res.setHeader('Content-Type', type_img.mime) res.json({ status: true, data: response }) } else if (images && typeof images == 'string' && isBase64(images)) { const response = await toAnime({ imgBuffer: Buffer.from(images, "base64") }); //const converted = await convertWebpToPng(response); //const type_img = await fileTypeFromBuffer(response) //res.setHeader('Content-Type', type_img.mime) res.json({ status: true, data: response }) } else { res.json({ success: false, message: 'No url or base64 detected!!' }) } } catch (e) { console.log(e) e = String(e) res.json({ error: true, message: e === '[object Object]' ? 'Internal Server Error' : e }) } }) export default APIrouter; function generateApiKey(secret) { // Ambil menit saat ini sebagai nilai integer const currentMinute = Math.floor(Date.now() / 60000).toString(); // Buat HMAC menggunakan algoritma SHA256 dan secret key const hmac = crypto.createHmac('sha256', secret); hmac.update(currentMinute); // Kembalikan hash dalam format hexadecimal return hmac.digest('hex'); }; function formatSize(num) { return bytes(+num || 0, { unitSeparator: ' ' }) } function isBase64(str) { try { return btoa(atob(str)) === str } catch { return false } }