bigbossmonster's picture
Update backend/server.js
adbbc5e verified
import express from 'express';
import cors from 'cors';
import dotenv from 'dotenv';
import { exec } from 'child_process';
import { promisify } from 'util';
import fs from 'fs';
import path from 'path';
import { AIService } from '@/backend/services/ai';
import { AuthService } from '@/backend/services/authService';
import { CreditService } from '@/backend/services/creditService';
import { AdminService } from '@/backend/services/adminService';
const execPromise = promisify(exec);
dotenv.config();
const app = express();
// CRITICAL FIX: Increased limits to 100mb to allow processing of long audio/video base64 data
app.use(express.json({ limit: '100mb' }));
app.use(express.urlencoded({ limit: '100mb', extended: true }));
app.use(cors());
const apiRouter = express.Router();
apiRouter.post('/download', async (req, res) => {
const { url, quality, platform, sessionId, creditCost } = req.body;
try {
let eligibility = null;
// BYPASS LOGIC: Only check eligibility and deduct credits if NOT tiktok
if (platform !== 'tiktok') {
eligibility = await CreditService.checkEligibility(sessionId, null, 'downloader', false, creditCost);
} else {
console.log(`[DOWNLOADER] TikTok bypass triggered for URL: ${url}`);
}
const timestamp = Date.now();
const fileBaseName = `tm_dl_${timestamp}`;
const outputDir = '/tmp';
const extension = quality === 'audio' ? 'mp3' : 'mp4';
const outputPath = path.join(outputDir, `${fileBaseName}.${extension}`);
let formatStr = "";
if (quality === 'audio') {
formatStr = "-f 'ba' -x --audio-format mp3";
} else {
const qMap = {
'360p': 'bestvideo[height<=360]+bestaudio/best[height<=360]',
'720p': 'bestvideo[height<=720]+bestaudio/best[height<=720]',
'1080p': 'bestvideo[height<=1080]+bestaudio/best[height<=1080]'
};
const format = qMap[quality] || qMap['720p'];
formatStr = `-f "${format}" --merge-output-format mp4`;
}
const tiktokArgs = platform === 'tiktok' ? '--referer "https://www.tiktok.com/"' : '';
const command = `yt-dlp --no-playlist --no-warnings --no-check-certificate --js-runtime node ${formatStr} ${tiktokArgs} -o "${outputPath}" "${url.trim()}"`;
console.log(`[DOWNLOADER] Executing: ${command}`);
try {
await execPromise(command);
} catch (execErr) {
console.error("[DL-EXEC-FAIL]", execErr.stderr || execErr.message);
if (!fs.existsSync(outputPath)) {
throw new Error(`Engine Error: ${execErr.stderr || execErr.message}`);
}
}
if (!fs.existsSync(outputPath)) {
throw new Error("Download engine failed. File not generated.");
}
// Only commit deduction if eligibility was checked (non-tiktok)
if (eligibility) {
await CreditService.commitDeduction(eligibility);
}
res.download(outputPath, `${fileBaseName}.${extension}`, (err) => {
if (err) console.error("Stream Error:", err);
try {
if (fs.existsSync(outputPath)) fs.unlinkSync(outputPath);
} catch (e) { console.error("Cleanup Error:", e); }
});
} catch (error) {
console.error("[DOWNLOAD-ERROR]", error.message);
res.status(500).json({ error: error.message });
}
});
const handleAiRequest = async (req, res, toolKey, taskFn) => {
const sessionId = req.headers['x-session-id'];
const guestId = req.headers['x-guest-id'];
const { isOwnApi, customApiKey, creditCost } = req.body;
try {
const eligibility = await CreditService.checkEligibility(sessionId, guestId, toolKey, isOwnApi, creditCost);
const apiKey = isOwnApi ? (customApiKey || process.env.API_KEY) : process.env.API_KEY;
const result = await taskFn(apiKey, isOwnApi);
await CreditService.commitDeduction(eligibility);
res.json(result);
} catch (error) {
console.error(`[BACKEND-API] Error in ${toolKey}:`, error.message);
res.status(error.message.includes('REACHED') || error.message.includes('INSUFFICIENT') ? 403 : 500).json({
error: error.message
});
}
};
apiRouter.post('/transcribe', (req, res) => {
handleAiRequest(req, res, 'count_transcript', async (key, isOwn) => {
return { text: await AIService.transcribe(req.body.media, req.body.mimeType, key, isOwn) };
});
});
apiRouter.post('/recap', (req, res) => {
handleAiRequest(req, res, 'count_transcript', async (key, isOwn) => {
return { recap: await AIService.recap(req.body.media, req.body.mimeType, req.body.targetLanguage, key, isOwn) };
});
});
apiRouter.post('/book-recap', (req, res) => {
handleAiRequest(req, res, 'count_translate', async (key, isOwn) => {
return { recap: await AIService.bookRecap(req.body.media, req.body.mimeType, req.body.targetLanguage, key, isOwn) };
});
});
apiRouter.post('/comic-translate', (req, res) => {
handleAiRequest(req, res, 'count_translate', async (key, isOwn) => {
return { pdfData: await AIService.comicTranslate(req.body.media, req.body.mimeType, req.body.targetLanguage, key, isOwn) };
});
});
apiRouter.post('/translate', (req, res) => {
handleAiRequest(req, res, 'count_translate', async (key, isOwn) => {
return await AIService.translate(req.body.text, req.body.targetLanguage, req.body.options, key, isOwn);
});
});
apiRouter.post('/srt-translate', (req, res) => {
handleAiRequest(req, res, 'count_srt_translate', async (key, isOwn) => {
return { srt: await AIService.srtTranslate(req.body.srtContent, req.body.sourceLanguage, req.body.targetLanguage, key, isOwn) };
});
});
apiRouter.post('/tts', (req, res) => {
handleAiRequest(req, res, 'count_tts', async (key, isOwn) => {
return { audioData: await AIService.tts(req.body.text, req.body.voiceId, req.body.tone, key, isOwn) };
});
});
apiRouter.post('/subtitle', (req, res) => {
req.setTimeout(1200000);
handleAiRequest(req, res, 'count_subtitle', async (key, isOwn) => {
return await AIService.subtitle(
req.body.media,
req.body.mimeType,
req.body.script,
key,
isOwn,
req.body.sourceLanguage,
req.body.startOffsetMs || 0,
req.body.lastScriptIndex || 0
);
});
});
apiRouter.post('/content-creator', (req, res) => {
handleAiRequest(req, res, 'count_creator_text', async (key, isOwn) => {
const script = await AIService.contentCreator(
req.body.topic, req.body.category, req.body.subTopics,
req.body.contentType, req.body.creatorGender, req.body.targetLanguage, key, isOwn
);
let imageUrl = null;
if (req.body.withImage) {
try { imageUrl = await AIService.generateImage(req.body.topic, key, isOwn); } catch(e) {}
}
return { script, imageUrl };
});
});
apiRouter.post('/secure/save-key', async (req, res) => {
try {
const { sessionId, apiKey } = req.body;
res.json(await AuthService.saveCustomKey(sessionId, apiKey));
} catch (e) { res.status(500).json({ error: e.message }); }
});
apiRouter.post('/secure/update-session', async (req, res) => {
try {
const { userId, newSessionId } = req.body;
res.json(await AuthService.updateActiveSession(userId, newSessionId));
} catch (e) { res.status(500).json({ error: e.message }); }
});
apiRouter.post('/admin/verify', async (req, res) => {
const { adminPassword } = req.body;
if (adminPassword === process.env.ADMIN_PASSWORD) res.json({ success: true });
else res.status(403).json({ error: "Unauthorized" });
});
apiRouter.post('/admin/update-settings', async (req, res) => {
const { adminPassword, settings } = req.body;
if (adminPassword !== process.env.ADMIN_PASSWORD) return res.status(403).json({ error: "Denied" });
try { res.json(await AdminService.updateSettings(settings)); } catch (e) { res.status(500).json({ error: e.message }); }
});
apiRouter.post('/admin/add-user', async (req, res) => {
const { adminPassword, userData, referrerCode } = req.body;
if (adminPassword !== process.env.ADMIN_PASSWORD) return res.status(403).json({ error: "Denied" });
try { res.json(await AdminService.addUser(userData, referrerCode)); } catch (e) { res.status(500).json({ error: e.message }); }
});
apiRouter.post('/admin/reactivate-user', async (req, res) => {
const { adminPassword, nodeKey, userClass, credits } = req.body;
if (adminPassword !== process.env.ADMIN_PASSWORD) return res.status(403).json({ error: "Denied" });
try { res.json(await AdminService.reactivateUser(nodeKey, userClass, credits)); } catch (e) { res.status(500).json({ error: e.message }); }
});
apiRouter.post('/admin/update-user-class', async (req, res) => {
const { adminPassword, nodeKey, userClass, credits } = req.body;
if (adminPassword !== process.env.ADMIN_PASSWORD) return res.status(403).json({ error: "Denied" });
try { res.json(await AdminService.updateUserClass(nodeKey, userClass, credits)); } catch (e) { res.status(500).json({ error: e.message }); }
});
apiRouter.post('/admin/topup-credits', async (req, res) => {
const { adminPassword, sessionId, amount } = req.body;
if (adminPassword !== process.env.ADMIN_PASSWORD) return res.status(403).json({ error: "Denied" });
try { res.json(await AdminService.topUpCredits(sessionId, amount)); } catch (e) { res.status(500).json({ error: e.message }); }
});
apiRouter.post('/admin/delete-user', async (req, res) => {
const { adminPassword, nodeKey } = req.body;
if (adminPassword !== process.env.ADMIN_PASSWORD) return res.status(403).json({ error: "Denied" });
try { res.json(await AdminService.deleteUser(nodeKey)); } catch (e) { res.status(500).json({ error: e.message }); }
});
app.use('/', apiRouter);
const PORT = process.env.PORT || 7860;
app.listen(PORT, () => {
console.log(`🚀 Master Backend Engine running on port ${PORT}`);
});