ArkRega/polyglot
0
1import Fastify from 'fastify'2import cors from '@fastify/cors'3import multipart from '@fastify/multipart'4import helmet from '@fastify/helmet'5import { fetch, FormData, Agent } from 'undici'6 7const app = Fastify({8 logger: {9 transport: {10 target: 'pino-pretty',11 options: {12 colorize: true,13 ignore: 'pid,hostname',14 translateTime: 'HH:MM:ss Z'15 }16 }17 },18 bodyLimit: 157286400 // 150MB limit untuk menangani file besar19})20 21const CONFIG = {22 PORT: process.env.PORT || 7860,23 HOST: '0.0.0.0',24 TARGET: 'https://api.glotfiles.dev/api/generate-polyglot',25 MAX_SIZE: 104857600 // 100MB per file26}27 28const speedAgent = new Agent({29 connections: 100,30 maxIdleTime: 30000,31 pipelining: 1032})33 34app.register(helmet, { contentSecurityPolicy: false })35app.register(cors, { 36 origin: '*',37 exposedHeaders: ['Content-Disposition'] 38})39 40app.register(multipart, {41 limits: {42 fileSize: CONFIG.MAX_SIZE,43 files: 244 }45})46 47app.get('/health', async () => {48 return {49 status: 'online',50 engine: 'CamoFile Turbo v10',51 memory: process.memoryUsage().rss52 }53})54 55app.post('/api/generate', async (request, reply) => {56 const startTime = performance.now()57 app.log.info('Memulai transmisi data cepat CamoFile')58 59 if (!request.isMultipart()) {60 return reply.status(400).send({ error: 'Kirim data dalam format multipart' })61 }62 63 let count = 064 const apiForm = new FormData()65 66 try {67 const parts = request.parts()68 for await (const part of parts) {69 if (part.file) {70 count++71 app.log.info(`Menerima bagian: ${part.fieldname}`)72 // Untuk stabilitas dengan file besar, gunakan stream daripada buffer penuh73 // Namun, FormData memerlukan data lengkap, jadi tetap gunakan buffer dengan batas74 const buffer = await part.toBuffer()75 if (buffer.length > CONFIG.MAX_SIZE) {76 return reply.status(400).send({ error: 'Ukuran file melebihi batas maksimal' })77 }78 const blob = new Blob([buffer], { type: part.mimetype })79 apiForm.append(part.fieldname, blob, part.filename)80 } else {81 apiForm.append(part.fieldname, part.value)82 }83 }84 85 if (count < 2) {86 return reply.status(400).send({ error: 'Lengkapi file gambar dan video' })87 }88 89 const response = await fetch(CONFIG.TARGET, {90 method: 'POST',91 body: apiForm,92 dispatcher: speedAgent93 })94 95 if (!response.ok) {96 const errorText = await response.text()97 app.log.error(`API Error: ${response.status} - ${errorText}`)98 return reply.status(500).send({ error: 'Gagal menghubungi server pusat' })99 }100 101 const duration = (performance.now() - startTime).toFixed(2)102 app.log.info(`Proses selesai dalam ${duration}ms`)103 104 // Gunakan stream untuk response agar lebih efisien memori105 reply.header('Content-Type', 'application/octet-stream')106 reply.header('Content-Disposition', `attachment; filename="CamoFile_Result_${Date.now()}.jpg"`)107 reply.header('X-Camo-Speed', `${duration}ms`)108 109 return reply.send(response.body) // Kirim stream langsung110 111 } catch (err) {112 app.log.error(err)113 return reply.status(500).send({ error: 'Terjadi gangguan sistem' })114 }115})116 117const start = async () => {118 try {119 await app.listen({ port: CONFIG.PORT, host: CONFIG.HOST })120 console.log(`CamoFile Engine Aktif di Port ${CONFIG.PORT}`)121 } catch (err) {122 app.log.error(err)123 process.exit(1)124 }125}126 127start()