Spaces:
Running
Running
require('./prototype.js') | |
require('dotenv').config(); | |
const express = require('express'); | |
const http = require('http'); | |
const axios = require("axios") | |
const app = express(); | |
const { spawn } = require('child_process') | |
const path = require('path') | |
const fs = require('fs') | |
const FormData = require("form-data") | |
const yts = require('yt-search') | |
const { exec } = require("child_process") | |
const util = require("util") | |
const _exec = util.promisify(exec); | |
const PORT = process.env.PORT || 7860; | |
const PASSWORD = process.env.PASSWORD | |
const { decrypt } = require('./lib/crypto.js') | |
function getClientIP(req) { | |
const trustedProxies = ['127.0.0.1', '::1']; | |
const remoteAddress = req.connection.remoteAddress; | |
const xForwardedFor = req.headers['x-forwarded-for']; | |
if (trustedProxies.includes(remoteAddress) && xForwardedFor) { | |
const forwardedIPs = xForwardedFor.split(',').map(ip => ip.trim()); | |
return forwardedIPs[0] | |
} else { | |
return xForwardedFor ? xForwardedFor.split(',')[0].trim() : remoteAddress | |
} | |
} | |
function auth(req, res, next) { | |
const key = req.query.key; | |
if (key !== PASSWORD) return res.status(403).json({ status: 403, message: 'Forbidden' }); | |
next(); | |
} | |
app.use(express.json({limit: '50mb'})); | |
app.use(express.urlencoded({limit: '50mb', extended: true, parameterLimit: 50000})); | |
app.use((req, res, next) => { | |
const startTime = Date.now(); | |
const { method, url } = req; | |
let ip = getClientIP(req) | |
console.log(`\x1b[34m[${new Date().toISOString()}]\x1b[0m Incoming Request: \x1b[33m${method}\x1b[0m \x1b[36m${url}\x1b[0m from \x1b[32m${ip}\x1b[0m`); | |
res.on('finish', () => { | |
const duration = Date.now() - startTime; | |
console.log(`\x1b[34m[${new Date().toISOString()}]\x1b[0m Response for \x1b[33m${method} ${url}\x1b[0m took \x1b[35m${duration}ms\x1b[0m - Status: \x1b[31m${res.statusCode}\x1b[0m`); | |
}); | |
next(); | |
}); | |
const fetchHandler = async (req, res) => { | |
try { | |
const url = req.body.url || req.query.url || req.params.url; | |
const redirect = req.query.redirect; | |
if (!url) return res.status(400).json({ error: 'URL is required!' }); | |
const headers = { ...req.headers }; | |
delete headers['host']; | |
delete headers['connection']; | |
delete headers['content-length']; | |
const method = req.method.toUpperCase(); | |
const isUrlEncoded = headers['content-type']?.includes('urlencoded'); | |
const config = { | |
method, | |
headers, | |
credentials: 'include', | |
}; | |
if (['POST', 'PUT', 'PATCH'].includes(method)) { | |
config.body = isUrlEncoded | |
? new URLSearchParams(req.body) | |
: JSON.stringify(req.body); | |
} | |
if (redirect) config.redirect = redirect; | |
console.log(config); | |
const response = await fetch(url, config); | |
for (const [key, value] of response.headers.entries()) { | |
if (["set-cookie", "authorization", "location"].includes(key.toLowerCase())) { | |
res.setHeader(key, value); | |
} | |
} | |
const contentType = response.headers.get('content-type'); | |
if (contentType?.includes('text/event-stream')) { | |
res.setHeader('Content-Type', contentType); | |
res.setHeader('Cache-Control', 'no-cache'); | |
res.setHeader('Connection', 'keep-alive'); | |
res.status(response.status); | |
if (response.body) { | |
response.body.pipe(res); | |
} else { | |
res.end(); | |
} | |
return; | |
} | |
if (contentType?.includes('application/json')) { | |
const data = await response.json(); | |
return res.status(response.status).json(data); | |
} | |
const data = await response.text(); | |
res.status(response.status).send(data); | |
} catch (error) { | |
console.error('Error during fetch:', error); | |
res.status(500).json({ | |
error: 'Failed to fetch the requested URL', | |
details: error.message, | |
}); | |
} | |
}; | |
const axiosHandler = async (req, res) => { | |
try { | |
const url = req.body.url || req.query.url || req.params.url; | |
const redirect = req.query.redirect; | |
if (!url) return res.status(400).json({ error: 'URL is required!' }); | |
const headers = { ...req.headers }; | |
delete headers['host']; | |
delete headers['connection']; | |
delete headers['content-length']; | |
let data = undefined; | |
const method = req.method.toUpperCase(); | |
const contentType = headers['content-type'] || ''; | |
if (['POST', 'PUT', 'PATCH'].includes(method)) { | |
if (contentType.includes('application/x-www-form-urlencoded')) { | |
const urlEncoded = new URLSearchParams(req.body).toString(); | |
data = urlEncoded; | |
headers['content-type'] = 'application/x-www-form-urlencoded'; | |
} else if (contentType.includes('multipart/form-data')) { | |
const form = new FormData(); | |
for (const key in req.body) { | |
form.append(key, req.body[key]); | |
} | |
data = form; | |
Object.assign(headers, form.getHeaders()); | |
} else { | |
data = req.body; | |
headers['content-type'] = 'application/json'; | |
} | |
} | |
const axiosConfig = { | |
method, | |
url, | |
headers, | |
data, | |
maxRedirects: redirect === 'manual' ? 0 : 5, | |
validateStatus: () => true, | |
withCredentials: true, | |
}; | |
const response = await axios(axiosConfig); | |
const forwardHeaders = ['set-cookie', 'authorization', 'location']; | |
forwardHeaders.forEach((key) => { | |
if (response.headers[key]) { | |
res.setHeader(key, response.headers[key]); | |
} | |
}); | |
const responseType = response.headers['content-type']; | |
if (responseType?.includes('application/json')) { | |
return res.status(response.status).json(response.data); | |
} | |
res.status(response.status).send(response.data); | |
} catch (error) { | |
console.error('Error during axios fetch:', error); | |
res.status(500).json({ | |
error: 'Failed to fetch the requested URL', | |
details: error.message, | |
}); | |
} | |
}; | |
app.get("/api/forward/youtube/get", async (req, res) => { | |
try { | |
const v = decrypt(req.query.v) | |
if (!v) return res.status(400).send("v is required"); | |
const type = req.query.type; | |
if (!type) return res.json("type is required!"); | |
const token = decrypt(req.query.token); | |
if (!token) return res.status(403).end(); | |
let Token = await fetch(token).then(a => a.text()).then(a => a.un("utf16le").un("base64")) | |
if (!["mp3", "mp4"].includes(type)) { | |
return res.json({ | |
status: false, | |
msg: `Harap input type yang tersedia: mp3, mp4`, | |
}); | |
} | |
let [id, t] = v.un("utf16le").un("charCode").un("base64").split("."); | |
if (!id || !t || isNaN(t) || Date.now() - parseInt(t) >= 60000 * 5) { | |
return res.status(403).end(); | |
} | |
const url = `http://www.youtube.com/watch?v=${id}`; | |
const format = "mp4"// === "mp3" ? "bestaudio" : "bestvideo+bestaudio"; | |
res.setHeader("Content-Type", type === "mp3" ? "audio/mpeg" : "video/mp4"); | |
res.setHeader("Content-Disposition", `inline; filename="${id}.${type}"`); | |
const ytdlp = spawn("yt-dlp", [ | |
"-f", format, | |
"-o", "-", | |
"--add-header", `Cookie: ${Token}`, | |
url, | |
]); | |
let errorLog = ""; | |
let stdoutChunks = []; | |
ytdlp.stderr.on("data", (data) => { | |
errorLog += data.toString(); | |
console.error("yt-dlp stderr:", data.toString()); | |
}); | |
ytdlp.stdout.on("data", (chunk) => { | |
stdoutChunks.push(chunk); | |
}); | |
ytdlp.on("error", (err) => { | |
console.error("yt-dlp spawn error:", err); | |
if (!res.headersSent) { | |
res.status(500).json({ error: "yt-dlp failed to start." }); | |
} else { | |
res.end(); | |
} | |
}); | |
ytdlp.on("close", (code) => { | |
if (code !== 0) { | |
const errorLine = errorLog.split("\n").find((line) => line.includes("ERROR:")); | |
if (!res.headersSent) { | |
res.status(500).json({ | |
error: "Download failed", | |
message: errorLine || `yt-dlp exited with code ${code}`, | |
}); | |
} else { | |
res.end(); | |
} | |
} else { | |
const finalBuffer = Buffer.concat(stdoutChunks); | |
res.setHeader("Content-Type", "video/mp4"); | |
res.send(finalBuffer); | |
} | |
}); | |
} catch (err) { | |
console.error(err); | |
if (!res.headersSent) { | |
res.status(500).json({ error: "Internal Server Error" }); | |
} | |
} | |
}); | |
async function handleE(req, res, action) { | |
try { | |
const q = req.method === 'POST' ? req.body.q : req.query.q; | |
let result; | |
if (action === 'eval') { | |
result = await eval(q); | |
} else if (action === 'exec') { | |
const { stdout, stderr } = await _exec(q); | |
result = stdout || stderr; | |
} | |
if (typeof result === 'object') { | |
res.setHeader('Content-Type', 'application/json'); | |
res.json(result); | |
} else { | |
res.setHeader('Content-Type', 'text/plain'); | |
res.send(String(result)); | |
} | |
} catch (error) { | |
res.status(500).json({ | |
status: 500, | |
message: `${action} error`, | |
error: error.message | |
}); | |
} | |
} | |
app.all(['/api/proxy', '/proxy'], axiosHandler); | |
app.use(['/api/fetch', '/fetch'], fetchHandler); | |
app.get('/ev', auth, (req, res) => handleE(req, res, 'eval')); | |
app.post('/ev', auth, (req, res) => handleE(req, res, 'eval')); | |
app.get('/exec', auth, (req, res) => handleE(req, res, 'exec')); | |
app.get('/restart', auth, (req, res) => { | |
req.query.q = `node up ${req.headers.host.split('-')[0]}` | |
handleE(req, res, 'exec') | |
}); | |
app.use((req, res, next) => { | |
res.status(404).end() | |
}); | |
const server = http.createServer({ | |
maxHeaderSize: 64 * 1024 | |
}, app); | |
server.listen(PORT, () => { | |
console.log(`Server running on port ${PORT}`); | |
}); |