From 82bff3babf50ee7957915b0ce9ae6617470aadcd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=92=8B=E5=B0=8F=E9=99=8C?= <xiaomo.drip@gmail.com>
Date: Sat, 12 Oct 2024 17:11:47 +0800
Subject: [PATCH] 1111

---
 index.js | 72 ++++++++++++++++++++++++++++++++++----------------------
 1 file changed, 44 insertions(+), 28 deletions(-)

diff --git a/index.js b/index.js
index 309ebe5..63bd512 100644
--- a/index.js
+++ b/index.js
@@ -3,12 +3,13 @@ const https = require('https');
 const url = require('url');
 const querystring = require('querystring');
 const fs = require('fs');
-const path = require('path');
+const pathModule = require('path');
 const crypto = require('crypto');
 
 const requestTimeout = 10000; // 10 seconds
-const cacheDir = path.join(__dirname, '.cache');
+const cacheDir = pathModule.join(__dirname, '.cache');
 const args = process.argv.slice(2);
+const pathIndex = {};
 
 let port = 9001;
 let apiEndpoint = 'https://oss.x-php.com/alist/link';
@@ -38,10 +39,6 @@ const server = http.createServer(async (req, res) => {
     const parsedUrl = url.parse(req.url, true);
     const reqPath = parsedUrl.pathname;
     const sign = parsedUrl.query.sign || '';
-    const reqName = parsedUrl.pathname.split('/').pop();
-    const cacheMetaFile = path.join(cacheDir, `${reqName.replace(/\//g, '_')}.meta`);
-    const cacheContentFile = path.join(cacheDir, `${reqName.replace(/\//g, '_')}.content`);
-    const tempCacheContentFile = path.join(cacheDir, `${reqName.replace(/\//g, '_')}_${crypto.randomUUID()}.temp`);
 
     if (!sign || reqPath === '/') {
         res.writeHead(400, { 'Content-Type': 'text/plain' });
@@ -49,7 +46,18 @@ const server = http.createServer(async (req, res) => {
         return;
     }
 
-    if (isCacheValid(cacheMetaFile, cacheContentFile)) {
+    const uniqidhex = crypto.createHash('md5').update(reqPath + sign).digest('hex');
+
+    let cacheMetaFile = '';
+    let cacheContentFile = '';
+    let tempCacheContentFile = '';
+
+    if (pathIndex[uniqidhex]) {
+        cacheMetaFile = pathModule.join(cacheDir, `${pathIndex[uniqidhex]}.meta`);
+        cacheContentFile = pathModule.join(cacheDir, `${pathIndex[uniqidhex]}.content`);
+    }
+
+    if (pathIndex[uniqidhex] && isCacheValid(cacheMetaFile, cacheContentFile)) {
         serveFromCache(cacheMetaFile, cacheContentFile, res);
     } else {
         try {
@@ -58,6 +66,12 @@ const server = http.createServer(async (req, res) => {
                 const { url: realUrl, cloudtype, expiration, path, headers, uniqid } = apiData.data;
                 const data = { realUrl, cloudtype, expiration: expiration * 1000, path, headers, uniqid };
 
+                pathIndex[uniqidhex] = data.uniqid;
+
+                cacheMetaFile = pathModule.join(cacheDir, `${data.uniqid}.meta`);
+                cacheContentFile = pathModule.join(cacheDir, `${data.uniqid}.content`);
+                tempCacheContentFile = pathModule.join(cacheDir, `${data.uniqid}_${crypto.randomBytes(16).toString('hex')}.temp`);
+
                 if (expiration > 0) {
                     fs.writeFileSync(cacheMetaFile, JSON.stringify(data));
                 }
@@ -119,9 +133,9 @@ const fetchApiData = (reqPath, sign) => {
 const fetchAndServe = (data, tempCacheContentFile, cacheContentFile, res) => {
     https.get(data.realUrl, { timeout: requestTimeout * 10 }, (realRes) => {
         const cacheStream = fs.createWriteStream(tempCacheContentFile, { flags: 'w' });
-        const isVideo = data.path.includes('.mp4');
-        data.headers['content-length'] = realRes.headers['content-length'];
 
+        let isVideo = data.path && typeof data.path === 'string' && data.path.includes('.mp4');
+        data.headers['content-length'] = realRes.headers['content-length'];
         res.writeHead(realRes.statusCode, {
             ...data.headers,
             'Cloud-Type': data.cloudtype,
@@ -151,30 +165,18 @@ const fetchAndServe = (data, tempCacheContentFile, cacheContentFile, res) => {
         });
 
         realRes.on('error', (e) => {
-            if (!res.headersSent) {
-                res.writeHead(502, { 'Content-Type': 'text/plain' });
-                res.end(`Bad Gateway: ${data.realUrl}`);
-            }
-            if (fs.existsSync(tempCacheContentFile)) {
-                fs.unlinkSync(tempCacheContentFile);
-            }
+            handleResponseError(res, tempCacheContentFile, data.realUrl);
         });
     }).on('error', (e) => {
-        if (!res.headersSent) {
-            res.writeHead(502, { 'Content-Type': 'text/plain' });
-            res.end(`Bad Gateway: ${data.realUrl}`);
-        }
-        if (fs.existsSync(tempCacheContentFile)) {
-            fs.unlinkSync(tempCacheContentFile);
-        }
+        handleResponseError(res, tempCacheContentFile, data.realUrl);
     });
 };
 
 const serveFromCache = (cacheMetaFile, cacheContentFile, res) => {
     const cacheData = JSON.parse(fs.readFileSync(cacheMetaFile, 'utf8'));
     const readStream = fs.createReadStream(cacheContentFile);
-    const isVideo = cacheData.path.includes('.mp4');
 
+    let isVideo = cacheData.path && typeof cacheData.path === 'string' && cacheData.path.includes('.mp4');
     cacheData.headers['content-length'] = fs.statSync(cacheContentFile).size;
 
     readStream.on('open', () => {
@@ -195,13 +197,27 @@ const serveFromCache = (cacheMetaFile, cacheContentFile, res) => {
     });
 
     readStream.on('error', (err) => {
-        if (!res.headersSent) {
-            res.writeHead(500, { 'Content-Type': 'text/plain' });
-            res.end('Internal Server Error: Unable to read cache content file');
-        }
+        handleCacheReadError(res);
     });
 };
 
+const handleResponseError = (res, tempCacheContentFile, realUrl) => {
+    if (!res.headersSent) {
+        res.writeHead(502, { 'Content-Type': 'text/plain' });
+        res.end(`Bad Gateway: ${realUrl}`);
+    }
+    if (fs.existsSync(tempCacheContentFile)) {
+        fs.unlinkSync(tempCacheContentFile);
+    }
+};
+
+const handleCacheReadError = (res) => {
+    if (!res.headersSent) {
+        res.writeHead(500, { 'Content-Type': 'text/plain' });
+        res.end('Internal Server Error: Unable to read cache content file');
+    }
+};
+
 server.listen(port, () => {
     console.log(`Proxy server is running on http://localhost:${port}`);
 });