Spaces:
Sleeping
Sleeping
| const express = require('express'); | |
| const bodyParser = require('body-parser'); | |
| const axios = require('axios'); | |
| const { execSync } = require('child_process'); | |
| const path = require('path'); | |
| const fs = require('fs'); | |
| const app = express(); | |
| app.use(bodyParser.json({ limit: '100mb' })); | |
| // File Dummy lokal sebagai cadangan jika dummy_url tidak dikirim | |
| const DUMMY_ZIP = 'dummy_lite.zepeto.zip'; | |
| if (!fs.existsSync('uploads')) fs.mkdirSync('uploads', { recursive: true }); | |
| function getTS() { return new Date().toLocaleTimeString(); } | |
| // Fungsi sakti untuk mencari file/folder di kedalaman mana pun | |
| function findPath(base, targetName, isFolder = false) { | |
| if (!fs.existsSync(base)) return null; | |
| const items = fs.readdirSync(base); | |
| for (const item of items) { | |
| const fullPath = path.join(base, item); | |
| const stat = fs.lstatSync(fullPath); | |
| if (isFolder && stat.isDirectory() && item.toLowerCase() === targetName.toLowerCase()) return fullPath; | |
| if (!isFolder && stat.isFile() && item.toLowerCase().endsWith(targetName.toLowerCase())) return fullPath; | |
| if (stat.isDirectory()) { | |
| const found = findPath(fullPath, targetName, isFolder); | |
| if (found) return found; | |
| } | |
| } | |
| return null; | |
| } | |
| app.post('/convert', async (req, res) => { | |
| const targetFilename = req.body.filename || 'result.zepeto'; | |
| // KUNCI UTAMA: dummy_url wajib dari draft yang sama agar KTP GUID cocok! | |
| const hpUrl = req.body.file_url; | |
| const dummyUrl = req.body.dummy_url; | |
| console.log(`\n[${getTS()}] 🔥 REQUEST MASUK: Eksekusi ICU Clone (Native Packing) -> ${targetFilename}`); | |
| const timeId = Date.now(); | |
| const tempHP = path.join(__dirname, 'uploads', `hp_${timeId}.zepeto`); | |
| const tempDummy = path.join(__dirname, 'uploads', `dummy_${timeId}.zepeto`); | |
| const extHP = path.join(__dirname, 'uploads', `ext_hp_${timeId}`); | |
| const extDummy = path.join(__dirname, 'uploads', `ext_dummy_${timeId}`); | |
| const stagePath = path.join(__dirname, 'uploads', `stage_${timeId}`); | |
| const finalOut = path.join(__dirname, 'uploads', `final_${timeId}.zepeto`); | |
| try { | |
| console.log(`[${getTS()}] [1] Mendownload Baju Raksasa...`); | |
| const resHP = await axios({ method: 'GET', url: hpUrl, responseType: 'stream' }); | |
| const writerHP = fs.createWriteStream(tempHP); | |
| resHP.data.pipe(writerHP); | |
| await new Promise(resolve => writerHP.on('finish', resolve)); | |
| console.log(`[${getTS()}] [2] Menyiapkan File Dummy...`); | |
| if (dummyUrl) { | |
| const resDummy = await axios({ method: 'GET', url: dummyUrl, responseType: 'stream' }); | |
| const writerDummy = fs.createWriteStream(tempDummy); | |
| resDummy.data.pipe(writerDummy); | |
| await new Promise(resolve => writerDummy.on('finish', resolve)); | |
| } else { | |
| // Gunakan dummy lokal jika tidak dikirim dari CURL | |
| fs.copyFileSync(DUMMY_ZIP, tempDummy); | |
| } | |
| console.log(`[${getTS()}] [3] Mengekstrak File...`); | |
| fs.mkdirSync(extHP, { recursive: true }); | |
| fs.mkdirSync(extDummy, { recursive: true }); | |
| fs.mkdirSync(stagePath, { recursive: true }); // Ruang Operasi Bersih | |
| execSync(`unzip -q -o "${tempHP}" -d "${extHP}"`); | |
| execSync(`unzip -q -o "${tempDummy}" -d "${extDummy}"`); | |
| console.log(`[${getTS()}] [4] Eksekusi Transplantasi Murni...`); | |
| // 1. AMBIL KTP & BLUEPRINT DARI DUMMY | |
| const dummyMetaPath = findPath(extDummy, '.prefab.meta'); | |
| const dummyPrefabPath = findPath(extDummy, '.prefab'); | |
| if (!dummyMetaPath || !dummyPrefabPath) throw new Error("File .prefab/.meta Dummy hilang!"); | |
| fs.copyFileSync(dummyPrefabPath, path.join(stagePath, 'untitled.prefab')); | |
| fs.copyFileSync(dummyMetaPath, path.join(stagePath, 'untitled.prefab.meta')); | |
| console.log(`[${getTS()}] -> Kertas Blueprint & KTP Dummy terpasang bersih.`); | |
| // 2. AMBIL BRANKAS BINER DARI RAKSASA | |
| const hpBundleDir = findPath(extHP, 'assetbundles', true); | |
| if (!hpBundleDir) throw new Error("Folder assetbundles Raksasa tidak ditemukan!"); | |
| const stageBundles = path.join(stagePath, 'assetbundles'); | |
| fs.mkdirSync(stageBundles, { recursive: true }); | |
| let fileCount = 0; | |
| fs.readdirSync(hpBundleDir).forEach(p => { | |
| const src = path.join(hpBundleDir, p); | |
| if (fs.lstatSync(src).isFile()) { | |
| fs.copyFileSync(src, path.join(stageBundles, p)); | |
| fileCount++; | |
| } | |
| }); | |
| console.log(`[${getTS()}] -> ${fileCount} File Biner Raksasa berhasil diselundupkan utuh.`); | |
| // 3. PEMBUNGKUSAN NATIVE LINUX (SOLUSI MUTLAK) | |
| console.log(`[${getTS()}] [5] Membungkus dengan Algoritma ZIP Native (Level 0 / Store)...`); | |
| // Memastikan sistem Linux memiliki package zip terinstal | |
| try { | |
| execSync('zip -v', { stdio: 'ignore' }); | |
| } catch (e) { | |
| console.log(`[${getTS()}] -> Menginstal library zip native...`); | |
| execSync('apt-get update && apt-get install -y zip', { stdio: 'ignore' }); | |
| } | |
| // Mengeksekusi perintah packing asli sistem operasi! | |
| // -r = Rekursif ke dalam folder | |
| // -0 = STORE ONLY (Dilarang keras melakukan kompresi DEFLATE agar Zepeto tidak error) | |
| execSync(`cd "${stagePath}" && zip -r -0 "${finalOut}" ./*`); | |
| console.log(`[${getTS()}] ✅ SELESAI! Packing ZIP 100% Identik dengan Web ICU.`); | |
| res.download(finalOut, targetFilename, () => { | |
| try { | |
| fs.rmSync(extHP, { recursive: true, force: true }); | |
| fs.rmSync(extDummy, { recursive: true, force: true }); | |
| fs.rmSync(stagePath, { recursive: true, force: true }); | |
| fs.unlinkSync(tempHP); | |
| fs.unlinkSync(tempDummy); | |
| fs.unlinkSync(finalOut); | |
| } catch(e) {} | |
| }); | |
| } catch (err) { | |
| console.error(`[FATAL ERROR]`, err); | |
| res.status(500).json({ error: err.message }); | |
| } | |
| }); | |
| const PORT = 7860; | |
| app.listen(PORT, () => console.log(`[${getTS()}] 🚀 Server Native Packing (ICU Clone) Aktif di Port ${PORT}`)); |