File size: 4,481 Bytes
ece4c92
fb8af9a
ece4c92
 
fb8af9a
704cb8b
fb8af9a
ece4c92
 
 
 
 
 
1a7be53
ece4c92
 
06c1688
fb8af9a
de32a43
704cb8b
fb8af9a
 
5b152aa
 
1a7be53
5b152aa
 
 
 
 
 
 
 
1a7be53
ece4c92
5b152aa
 
 
 
 
 
 
 
 
 
 
704cb8b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a3ce5c5
ece4c92
 
16a21a6
5b152aa
 
 
ece4c92
 
5b152aa
 
3c65dd1
5b152aa
16a21a6
 
 
 
ece4c92
3c65dd1
16a21a6
fb8af9a
a3ce5c5
 
6a31a3b
a3ce5c5
 
 
 
 
 
 
ece4c92
 
06c1688
ece4c92
 
de32a43
 
c6e5396
de32a43
 
16a21a6
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
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;