proxy-node
unknown
javascript
a year ago
3.3 kB
1
Indexable
Never
const express = require('express'); const axios = require('axios'); const cors = require('cors'); const fs = require('fs-extra'); const path = require('path'); const crypto = require('crypto'); const minimist = require('minimist'); const chalk = require('chalk'); const app = express(); const argv = minimist(process.argv.slice(2)); if (!argv.targetUrl || !argv.port || !argv.mode) { console.log(chalk.red('Usage: node index.js --targetUrl <target_url> --port <port> --mode <mode>')); console.log('\n' + chalk.green('Modes:')); console.log(' ' + chalk.blue('capture-only') + ': Only caches requests to the file system but does not read from them.'); console.log(' ' + chalk.blue('proxy-only') + ': Only hits the target URL and does not use caching.'); console.log(' ' + chalk.blue('mix') + ': Uses both caching and proxying.'); process.exit(1); } const TARGET_URL = argv.targetUrl; const PORT = parseInt(argv.port, 10); const MODE = argv.mode.toLowerCase(); if (!['capture-only', 'proxy-only', 'mix'].includes(MODE)) { console.log(chalk.red('Invalid mode. Choose one of the following: capture-only, proxy-only, mix.')); process.exit(1); } app.use(cors()); // Enable CORS for all routes app.use(express.json()); // Parse JSON request bodies const CACHE_DIR = './cache'; if (MODE !== 'proxy-only') { // Create cache directory if it doesn't exist fs.ensureDirSync(CACHE_DIR); } // Generate cache key based on the original URL function generateCacheKey(req) { return req.originalUrl.replace(/\//g, '__'); } // Route for proxying and caching requests app.all('*', async (req, res) => { const { method, headers, body } = req; const url = TARGET_URL + req.originalUrl; const cacheKey = generateCacheKey(req); const cacheFilePath = path.join(CACHE_DIR, cacheKey + '.json'); // If mode is 'mix' and cache file exists, return cached response if (MODE === 'mix' && await fs.pathExists(cacheFilePath)) { const cachedResponse = await fs.readJson(cacheFilePath); return res.status(cachedResponse.status).set(cachedResponse.headers).send(cachedResponse.data); } // Remove host and connection headers to avoid conflicts delete headers.host; delete headers.connection; try { const axiosResponse = await axios({ method, url, headers, data: body, }); // Cache the response if mode is 'capture-only' or 'mix' if (MODE !== 'proxy-only') { await fs.writeJson(cacheFilePath, { status: axiosResponse.status, headers: axiosResponse.headers, data: axiosResponse.data, }); } // Forward status, headers, and data from Axios response res.status(axiosResponse.status).set(axiosResponse.headers).send(axiosResponse.data); } catch (error) { if (error.response) { // Forward error status, headers, and data from Axios error response res.status(error.response.status).set(error.response.headers).send(error.response.data); } else { // If the request failed for any other reason, return a 500 status code res.status(500).send('An error occurred while proxying the request.'); } } }); app.listen(PORT, () => { console.log(chalk.yellow(CORS proxy server with mode "${MODE}" listening on port ${PORT})); });