Conrt / server.js
Reaperxxxx's picture
Update server.js
a3ce5c5 verified
const express = require("express");
const axios = require("axios");
const fs = require("fs");
const path = require("path");
const https = require("https");
const crypto = require("crypto");
const app = express();
const PORT = 7860;
const DOWNLOAD_DIR = path.join(__dirname, "downloads");
// Ensure the downloads directory exists
if (!fs.existsSync(DOWNLOAD_DIR)) {
fs.mkdirSync(DOWNLOAD_DIR, { recursive: true });
}
// Axios instance to ignore SSL verification
const axiosInstance = axios.create({
httpsAgent: new https.Agent({ rejectUnauthorized: false }),
timeout: 300000, // 5 minutes timeout
});
// Function to generate a unique request ID
const generateRequestId = () => crypto.randomBytes(6).toString("hex");
// Function to validate URLs
const isValidUrl = (url) => {
try {
new URL(url);
return true;
} catch (error) {
return false;
}
};
// Function to delete files after a delay (default: 10 minutes)
const scheduleFileDeletion = (filePath, delay = 600000) => {
setTimeout(() => {
fs.unlink(filePath, (err) => {
if (!err) {
console.log(`βœ… Deleted file: ${filePath}`);
}
});
}, delay);
};
// **Download Function with Integrity Check**
const downloadFile = async (fileUrl, filePath, requestId) => {
let retries = 3; // Retry up to 3 times
while (retries > 0) {
try {
console.log(`⬇️ [${requestId}] Downloading: ${fileUrl}`);
const response = await axiosInstance({
url: fileUrl,
method: "GET",
responseType: "stream",
headers: { "User-Agent": "Mozilla/5.0", "Accept": "*/*" }
});
const totalSize = response.headers["content-length"]; // Get file size
console.log(`πŸ“ [${requestId}] Expected size: ${totalSize} bytes`);
const writer = fs.createWriteStream(filePath);
response.data.pipe(writer);
await new Promise((resolve, reject) => {
writer.on("finish", resolve);
writer.on("error", reject);
});
// Verify file size
const fileSize = fs.statSync(filePath).size;
if (totalSize && fileSize < totalSize * 0.9) { // Allow minor variance
console.error(`⚠️ [${requestId}] Download incomplete: ${fileSize} bytes (expected ${totalSize} bytes)`);
fs.unlinkSync(filePath);
throw new Error("Incomplete download, retrying...");
}
console.log(`βœ… [${requestId}] Download complete: ${filePath}`);
return true;
} catch (error) {
console.error(`❌ [${requestId}] Download failed: ${error.message}`);
retries--;
if (retries === 0) {
console.error(`β›” [${requestId}] All retries failed, skipping.`);
}
}
}
return false;
};
// **API Route - Responds Immediately, Processes in Background**
app.get("/download", async (req, res) => {
const fileUrl = req.query.url;
// Validate URL before processing
if (!fileUrl || !isValidUrl(fileUrl)) {
return res.status(400).json({ error: "❌ Invalid or missing URL parameter" });
}
// Generate a unique request ID
const requestId = generateRequestId();
console.log(`πŸ“₯ [${requestId}] Received request for: ${fileUrl}`);
// Extract file extension from URL
const fileExt = path.extname(new URL(fileUrl).pathname) || ".dat"; // Default to ".dat" if no extension
const filename = `${requestId}${fileExt}`;
const filePath = path.join(DOWNLOAD_DIR, filename);
const hostUrl = `${req.protocol}://${req.get("host")}`;
const servedUrl = `${hostUrl}/files/${filename}`;
// **Respond to the client immediately**
res.json({ message: "Processing in background", fileUrl: servedUrl });
// **Background Processing (Download)**
(async () => {
const success = await downloadFile(fileUrl, filePath, requestId);
if (success) {
scheduleFileDeletion(filePath);
}
})();
});
// Serve files from downloads directory
app.use("/files", express.static(DOWNLOAD_DIR));
// Start the server with an extended timeout
const server = app.listen(PORT, () => {
console.log(`πŸš€ Server running on port ${PORT}`);
});
// Increase Express server timeout to 10 minutes
server.timeout = 600000;