-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
14 changed files
with
308 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
/node_modules | ||
package-lock.json | ||
.env | ||
.old |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"watch": ["*.js", "*.json", ".env"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
{ | ||
"name": "multipurpose-proxy-server", | ||
"version": "1.0.0", | ||
"description": "", | ||
"main": "./src/index.js", | ||
"scripts": { | ||
"dev": "npx nodemon ./src/index.js", | ||
"start": "npm run dev" | ||
}, | ||
"license": "ISC", | ||
"type": "module", | ||
"dependencies": { | ||
"dotenv": "16.3.1", | ||
"express": "4.18.2", | ||
"http-proxy-middleware": "2.0.6", | ||
"nodemon": "3.0.1" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import express from 'express'; | ||
import { config } from '../utils/config.js'; | ||
import { logger } from '../middlewares/logger.js'; | ||
import { proxy } from '../middlewares/proxy.js'; | ||
|
||
export function runVideoAdsBlockingProxy() { | ||
const { IP, PORT1, HOST1, PATH1, PORT2, HOST2 } = config(); | ||
|
||
const app1 = express(); | ||
app1.use(express.json()); | ||
|
||
app1.use('/*', logger('<APP1-PROXY>'), proxy({ | ||
target: HOST1, | ||
destination: IP, | ||
pathRewrite: { '^/site': PATH1 }, | ||
amendIncomingHtml: ` | ||
<script> | ||
function trimVideoIframe() { | ||
const collection = document.getElementsByTagName('iframe') || []; | ||
for (const item of collection) { | ||
if (item.src?.includes('${HOST2}'.replace(/^.+\\.([A-Za-z0-9]+\\.[A-Za-z]+\\/?)$/, '$1'))) { | ||
const newSrc = item.src.replace(/https:\\/\\/[^/]+\\//g, 'http://${IP}:${PORT2}/'); | ||
document.body.innerHTML = '<iframe id="video_iframe" width="560" height="400" src="' + newSrc + '" frameborder="0" allowfullscreen=""></iframe>'; | ||
break; | ||
} | ||
} | ||
document.body.setAttribute('style', 'background: #000 !important; min-height: 100vh !important; display: flex !important; align-items: center !important; justify-content: center !important;'); | ||
} | ||
document.addEventListener('DOMContentLoaded', () => { | ||
setTimeout(trimVideoIframe, 1000); | ||
}); | ||
</script> | ||
`, | ||
})); | ||
|
||
app1.listen(PORT1, () => { | ||
console.log(`APP1 RUNNING: http://${IP}:${PORT1}`); | ||
}); | ||
|
||
const app2 = express(); | ||
app2.use(express.json()); | ||
|
||
app2.use('/*', logger('<APP2-PROXY>'), proxy({ | ||
target: HOST2, | ||
destination: IP, | ||
followRedirects: false, | ||
amendIncomingHtml: ` | ||
<script> | ||
function fastForwardVideoAds() { | ||
const collection = document.getElementsByTagName('video') || []; | ||
for (const video of collection) { | ||
if (!video) { continue; } | ||
const isVideoShort = video.duration < 60; | ||
const isVideoPlaying = !!(video.currentTime > 0 && !video.paused && !video.ended && video.readyState > 2); | ||
if (isVideoShort && isVideoPlaying) { | ||
video.currentTime = video.currentTime + video.duration; | ||
} | ||
} | ||
} | ||
document.addEventListener('DOMContentLoaded', () => { | ||
setInterval(fastForwardVideoAds, 200); | ||
}); | ||
</script> | ||
`, | ||
})); | ||
|
||
app2.listen(PORT2, () => { | ||
console.log(`APP2 RUNNING: http://${IP}:${PORT2}`); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import { runVideoAdsBlockingProxy } from './apps/video-ads-blocking-proxy.js'; | ||
|
||
runVideoAdsBlockingProxy(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export function delay(time) { | ||
return (_req, _res, next) => setTimeout(next, time); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
export function logger(preMessage = '', postMessage = '') { | ||
return (req, res, next) => { | ||
const fullUrl = req.protocol + '://' + req.get('host') + req.originalUrl; | ||
console.log(res.statusCode, preMessage, req.method, fullUrl, postMessage); | ||
next(); | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
import { createProxyMiddleware, responseInterceptor } from 'http-proxy-middleware'; | ||
import { parseJson, stringifyJson } from '../utils/json.js'; | ||
import { mergeDeep } from '../utils/objects.js'; | ||
import { parseCookies } from '../utils/cookies.js'; | ||
|
||
export function proxy(config = {}) { | ||
const { | ||
target, | ||
destination, | ||
followRedirects, | ||
amendOutgoingCookies, | ||
amendOutgoingJson, | ||
amendIncomingJson, | ||
amendIncomingHtml, | ||
...restConfig | ||
} = config; | ||
|
||
return createProxyMiddleware({ | ||
target: target, | ||
secure: true, | ||
changeOrigin: true, | ||
cookieDomainRewrite: destination, | ||
followRedirects, | ||
onProxyReq: (proxyReq, req) => { | ||
const contentType = proxyReq.getHeader('Content-Type') || ''; | ||
|
||
if (amendOutgoingCookies) { | ||
let newCookiesString = ''; | ||
if (typeof amendOutgoingCookies === 'function') { | ||
newCookiesString = amendOutgoingCookies(req.headers.cookie); | ||
} else if (amendOutgoingCookies) { | ||
newCookiesString = stringifyJson( | ||
(parseCookies(req.headers.cookie) || {}), | ||
(parseCookies(amendOutgoingCookies) || {}), | ||
); | ||
} | ||
proxyReq.setHeader('cookie', newCookiesString); | ||
} | ||
|
||
if (contentType.includes('application/json')) { | ||
let dataObject = req.body || {}; | ||
if (amendOutgoingJson) { | ||
if (typeof amendOutgoingJson === 'function') { | ||
dataObject = amendOutgoingJson(dataObject); | ||
} else if (amendOutgoingJson) { | ||
dataObject = mergeDeep(dataObject, amendOutgoingJson); | ||
} | ||
} | ||
const dataString = stringifyJson(dataObject); | ||
proxyReq.setHeader('Content-Length', Buffer.byteLength(dataString)); | ||
proxyReq.write(dataString); | ||
} | ||
}, | ||
selfHandleResponse: true, | ||
onProxyRes: responseInterceptor(async (responseBuffer, proxyRes, req, res) => { | ||
const contentType = res.getHeader('Content-Type') || ''; | ||
|
||
if (followRedirects === false) { | ||
res.removeHeader('location'); | ||
} | ||
|
||
if (contentType.includes('application/json') && amendIncomingJson) { | ||
let jsonObject = parseJson(responseBuffer.toString('utf8')); | ||
if (typeof amendIncomingJson === 'function') { | ||
jsonObject = amendIncomingJson(jsonObject); | ||
} else if (typeof amendIncomingJson === 'object') { | ||
jsonObject = mergeDeep(jsonObject, amendIncomingJson); | ||
} | ||
return stringifyJson(jsonObject); | ||
} | ||
|
||
if (contentType.includes('text/html') && amendIncomingHtml) { | ||
let htmlString = responseBuffer.toString('utf8'); | ||
if (typeof amendIncomingHtml === 'function') { | ||
htmlString = amendIncomingHtml(htmlString); | ||
} else if (amendIncomingHtml) { | ||
htmlString += amendIncomingHtml; | ||
} | ||
return htmlString; | ||
} | ||
|
||
return responseBuffer; | ||
}), | ||
...restConfig, | ||
}); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import dotenv from 'dotenv'; | ||
import { getIp } from './ip.js'; | ||
|
||
dotenv.config(); | ||
|
||
export function config() { | ||
process.env.IP = getIp(); | ||
return process.env; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
export function parseCookies(cookiesInput) { | ||
if (typeof cookiesInput === 'string') { | ||
const cookiesObject = {}; | ||
const cookiesList = cookiesInput.split(';'); | ||
for (const cookiesPair of cookiesList) { | ||
const [cookieKey, cookieValue] = cookiesPair.split('='); | ||
if (cookieKey.trim()) { | ||
cookiesObject[cookieKey.trim()] = (cookieValue || '').trim(); | ||
} | ||
} | ||
return cookiesObject; | ||
} if (typeof cookiesInput === 'object') { | ||
return cookiesInput; | ||
} | ||
return null; | ||
} | ||
|
||
export function stringifyCookies(cookiesInput) { | ||
if (typeof cookiesInput === 'object') { | ||
return Object.entries(cookiesInput).map(([key, value]) => `${key}=${value}`).join('; '); | ||
} if (typeof cookiesInput === 'string') { | ||
return cookiesInput; | ||
} | ||
return ''; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import { networkInterfaces } from 'os'; | ||
|
||
export function getIp() { | ||
return [].concat(...Object.values(networkInterfaces())) | ||
.find((item) => !item.internal && item.family === 'IPv4')?.address; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
export function parseJson(jsonString) { | ||
if (typeof jsonString !== 'string') { | ||
return null; | ||
} | ||
try { | ||
return JSON.parse(jsonString.replace(/([{,][ ]*)(['"])?([a-z0-9A-Z_]+)(['"])?:/g, '$1"$3": ')); | ||
} catch(error) { | ||
console.error(error, jsonString); | ||
return null; | ||
} | ||
} | ||
|
||
export function stringifyJson(jsonObject) { | ||
try { | ||
return JSON.stringify(jsonObject); | ||
} catch(error) { | ||
console.error(error, value); | ||
return ''; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
export function isPlainObject(item) { | ||
if (typeof item !== 'object' || item === null) { | ||
return false; | ||
} | ||
const prototype = Object.getPrototypeOf(item); | ||
return (prototype === null || prototype === Object.prototype || Object.getPrototypeOf(prototype) === null); | ||
} | ||
|
||
export function isArray(item) { | ||
return Array.isArray(item); | ||
} | ||
|
||
export function mergeDeep(target, ...sources) { | ||
if (!sources.length) { | ||
return target; | ||
} | ||
const source = sources.shift(); | ||
if (isPlainObject(target) && isPlainObject(source)) { | ||
for (const key in source) { | ||
if (isPlainObject(source[key])) { | ||
if (!isPlainObject(target[key])) { | ||
target[key] = {}; | ||
} | ||
mergeDeep(target[key], source[key]); | ||
} else if (isArray(source[key])) { | ||
if (!isArray(target[key])) { | ||
target[key] = []; | ||
} | ||
mergeDeep(target[key], source[key]); | ||
} else { | ||
target[key] = source[key]; | ||
} | ||
} | ||
} else if (isArray(target) && isArray(source)) { | ||
for (let key = 0; key < source.length; ++key) { | ||
if (isPlainObject(source[key])) { | ||
if (!isPlainObject(target[key])) { | ||
target[key] = {}; | ||
} | ||
mergeDeep(target[key], source[key]); | ||
} else if (isArray(source[key])) { | ||
if (!isArray(target[key])) { | ||
target[key] = []; | ||
} | ||
mergeDeep(target[key], source[key]); | ||
} else { | ||
target[key] = source[key]; | ||
} | ||
} | ||
} | ||
return mergeDeep(target, ...sources); | ||
} |