asli / server.js
webzepetoku's picture
Update server.js
8997d08 verified
Raw
History Blame Contribute Delete
6.28 kB
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}`));