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}`); });