alist-proxy/oss.js

102 lines
3.4 KiB
JavaScript
Raw Normal View History

2024-09-24 22:55:41 +08:00
const http = require('http');
const https = require('https');
2024-09-27 15:32:37 +08:00
const fs = require('fs').promises;
2024-09-24 22:55:41 +08:00
const path = require('path');
2024-09-27 15:32:37 +08:00
const { URL } = require('url');
2024-09-24 22:55:41 +08:00
const PORT = 3000;
const CACHE_DIR = path.join(__dirname, '.cache');
const CACHE_EXPIRY = 30 * 24 * 60 * 60 * 1000; // 30 days in milliseconds
const CLEAN_INTERVAL = 24 * 60 * 60 * 1000; // 1 day in milliseconds
// Ensure the cache directory exists
2024-09-27 15:32:37 +08:00
fs.mkdir(CACHE_DIR, { recursive: true }).catch(console.error);
2024-09-24 22:55:41 +08:00
// Helper function to get cache file path
const getCacheFilePath = (requestUrl) => {
2024-09-27 15:32:37 +08:00
const urlObj = new URL(requestUrl);
const sanitizedUrl = (urlObj.host + urlObj.pathname).replace(/[^a-z0-9]/gi, '_').toLowerCase();
2024-09-24 22:55:41 +08:00
return path.join(CACHE_DIR, sanitizedUrl);
};
// Function to clean up expired cache files
2024-09-27 15:32:37 +08:00
const cleanUpCache = async () => {
try {
const files = await fs.readdir(CACHE_DIR);
2024-09-24 22:55:41 +08:00
const now = Date.now();
2024-09-27 15:32:37 +08:00
for (const file of files) {
2024-09-24 22:55:41 +08:00
const filePath = path.join(CACHE_DIR, file);
2024-09-27 15:32:37 +08:00
const stats = await fs.stat(filePath);
2024-09-24 22:55:41 +08:00
2024-09-27 15:32:37 +08:00
if (now - stats.mtimeMs > CACHE_EXPIRY) {
await fs.unlink(filePath);
}
}
} catch (err) {
console.error('Error cleaning up cache:', err);
}
2024-09-24 22:55:41 +08:00
};
// Schedule cache clean-up at regular intervals
setInterval(cleanUpCache, CLEAN_INTERVAL);
// Function to handle proxying and caching
2024-09-27 15:32:37 +08:00
const handleRequest = async (req, res) => {
2024-09-24 22:55:41 +08:00
const targetUrl = `https://oss.x-php.com${req.url}`;
const cacheFilePath = getCacheFilePath(targetUrl);
2024-09-27 15:32:37 +08:00
try {
// Check if the cache file exists and is still valid
const cacheStats = await fs.stat(cacheFilePath);
2024-09-24 22:55:41 +08:00
const now = Date.now();
2024-09-27 15:32:37 +08:00
if (now - cacheStats.mtimeMs < CACHE_EXPIRY) {
2024-09-24 22:55:41 +08:00
// Serve from cache
2024-09-27 15:32:37 +08:00
const cachedData = JSON.parse(await fs.readFile(cacheFilePath, 'utf8'));
2024-09-24 22:55:41 +08:00
res.writeHead(cachedData.statusCode, cachedData.headers);
res.end(Buffer.from(cachedData.body, 'base64'));
return;
}
2024-09-27 15:32:37 +08:00
} catch (err) {
// Cache file does not exist or is invalid, proceed to fetch from the target URL
2024-09-24 22:55:41 +08:00
}
// Fetch from the target URL
https.get(targetUrl, (proxyRes) => {
let data = [];
proxyRes.on('data', (chunk) => {
data.push(chunk);
});
2024-09-27 15:32:37 +08:00
proxyRes.on('end', async () => {
2024-09-24 22:55:41 +08:00
const responseData = Buffer.concat(data);
2024-09-27 15:32:37 +08:00
if (proxyRes.statusCode === 200 && proxyRes.headers['content-type'] && proxyRes.headers['content-type'].startsWith('image/')) {
// Save the response to cache if it is an image
const cacheData = {
statusCode: proxyRes.statusCode,
headers: proxyRes.headers,
body: responseData.toString('base64')
};
await fs.writeFile(cacheFilePath, JSON.stringify(cacheData)).catch(console.error);
}
2024-09-24 22:55:41 +08:00
// Serve the response
res.writeHead(proxyRes.statusCode, proxyRes.headers);
res.end(responseData);
});
}).on('error', (err) => {
res.writeHead(500, { 'Content-Type': 'text/plain' });
res.end('Error fetching the resource');
});
};
// Create an HTTP server
const server = http.createServer(handleRequest);
server.listen(PORT, () => {
console.log(`Proxy server running at http://localhost:${PORT}`);
});