const http = require('http'); const https = require('https'); const url = require('url'); const querystring = require('querystring'); const apiEndpoint = process.env.url || 'https://oss.x-php.com/alist/link'; const requestTimeout = 10000; // 10 seconds const cache = {}; // Get port from environment variable or default to 9001 const PORT = process.env.PORT || 9001; const server = http.createServer((req, res) => { if (req.url === '/favicon.ico') { res.writeHead(204); res.end(); return; } const parsedUrl = url.parse(req.url, true); const path = parsedUrl.pathname; const sign = parsedUrl.query.sign || ''; // Check if the data is in cache and not expired const cacheEntry = cache[path]; if (cacheEntry && cacheEntry.expiration > Date.now()) { // 清理所有过期的缓存 Object.keys(cache).forEach(key => { if (cache[key].expiration < Date.now()) { delete cache[key]; } }); serveFromCache(cacheEntry, res); return; } else { delete cache[path]; // Remove expired cache entry if exists } // Construct the POST data const postData = querystring.stringify({ path, sign }); // Request the real URL from the API const apiReq = https.request(apiEndpoint, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json', 'Content-Length': Buffer.byteLength(postData), 'sign': sign }, timeout: requestTimeout }, (apiRes) => { let data = ''; apiRes.on('data', chunk => data += chunk); apiRes.on('end', () => { try { const apiData = JSON.parse(data); if (apiData.code === 200 && apiData.data && apiData.data.url) { const { url: realUrl, cloudtype, expiration } = apiData.data; // Cache the response if expiration is greater than 0 if (expiration > 0) { cache[path] = { realUrl, cloudtype, expiration: Date.now() + expiration * 1000 }; } fetchAndServe(realUrl, cloudtype, res); } else { res.writeHead(502, { 'Content-Type': 'text/plain' }); res.end(apiData.message || 'Bad Gateway'); } } catch (error) { res.writeHead(502, { 'Content-Type': 'text/plain' }); res.end('Bad Gateway: Failed to decode JSON'); } }); }); apiReq.on('error', (e) => { if (e.code === 'ETIMEDOUT') { res.writeHead(504, { 'Content-Type': 'text/plain' }); res.end('Gateway Timeout'); } else { res.writeHead(500, { 'Content-Type': 'text/plain' }); res.end('Internal Server Error'); } }); apiReq.write(postData); apiReq.end(); }); function fetchAndServe(realUrl, cloudtype, res) { const realReq = https.get(realUrl, { timeout: requestTimeout * 10 }, (realRes) => { res.writeHead(realRes.statusCode, { ...realRes.headers, 'cloudtype': cloudtype }); realRes.pipe(res); }); realReq.on('error', (e) => { res.writeHead(502, { 'Content-Type': 'text/plain' }); res.end(`Bad Gateway: ${realUrl}`); }); } function serveFromCache(cacheEntry, res) { fetchAndServe(cacheEntry.realUrl, cacheEntry.cloudtype, res); } server.listen(PORT, () => { console.log(`Proxy server is running on http://localhost:${PORT}`); }); // Graceful shutdown process.on('SIGINT', () => { console.log('Received SIGINT. Shutting down gracefully...'); server.close(() => { console.log('Server closed.'); process.exit(0); }); // Force shutdown after 10 seconds if not closed setTimeout(() => { console.error('Forcing shutdown...'); process.exit(1); }, 10000); });