Spaces:
Sleeping
Sleeping
Upload server.js
Browse files
server.js
CHANGED
|
@@ -53,7 +53,7 @@ async function fetchText(url, timeoutMs, clientUA) {
|
|
| 53 |
redirect: 'follow',
|
| 54 |
signal: controller.signal,
|
| 55 |
headers: {
|
| 56 |
-
'user-agent': clientUA ?
|
| 57 |
}
|
| 58 |
})
|
| 59 |
|
|
@@ -81,7 +81,7 @@ async function fetchText(url, timeoutMs, clientUA) {
|
|
| 81 |
}
|
| 82 |
}
|
| 83 |
|
| 84 |
-
function jsWrapper(serverBase, baseUrl, transpiledCode, needRegenerator) {
|
| 85 |
const safeServer = JSON.stringify(String(serverBase || ''))
|
| 86 |
const safeBaseUrl = JSON.stringify(String(baseUrl || ''))
|
| 87 |
|
|
@@ -123,7 +123,7 @@ function jsWrapper(serverBase, baseUrl, transpiledCode, needRegenerator) {
|
|
| 123 |
'function origin(u){try{var a=document.createElement("a");a.href=u;return (a.protocol? a.protocol:"") + "//" + (a.host||"");}catch(e){return "";}}' +
|
| 124 |
'var allow=window.__es5ProxyAllow||{};window.__es5ProxyAllow=allow;' +
|
| 125 |
'allow[origin(__es5_base)]=1;' +
|
| 126 |
-
'function proxyJs(u){var a=abs(u);var o=origin(a);if(__es5_active)allow[o]=1;allow[o]=1;if(allow[o]&&__es5_server){try{var testUrl=new URL(a);if(testUrl.protocol==="http:"||testUrl.protocol==="https:")return __es5_server + "/p?url=" + encodeURIComponent(a).replace(/%3A/gi, ":").replace(/%2F/gi, "/");}catch(e){}}return a;}' +
|
| 127 |
'function proxyCss(u){var a=abs(u);var o=origin(a);if(__es5_active)allow[o]=1;if(allow[o]&&__es5_server)return __es5_server + "/s?url=" + encodeURIComponent(a).replace(/%3A/gi, ":").replace(/%2F/gi, "/");return a;}' +
|
| 128 |
'function proxyRaw(u){var a=abs(u);if(!/^https?:\\/\\//i.test(a))return u;if(__es5_server&&a.indexOf(__es5_server)===-1){return __es5_server+"/r?url="+encodeURIComponent(a).replace(/%3A/gi, ":").replace(/%2F/gi, "/");}return a;}' +
|
| 129 |
'function mapArr(items, fn){if(!items||!items.length)return items;for(var i=0;i<items.length;i++){items[i]=fn(items[i]);}return items;}' +
|
|
@@ -139,7 +139,7 @@ function jsWrapper(serverBase, baseUrl, transpiledCode, needRegenerator) {
|
|
| 139 |
'var origInsertBefore=Element.prototype.insertBefore;' +
|
| 140 |
'function patchNode(node){' +
|
| 141 |
'if(node&&node.tagName==="SCRIPT"&&node.src){' +
|
| 142 |
-
'var a=abs(node.src);var o2=origin(a);if(__es5_active)allow[o2]=1;allow[o2]=1;if(allow[o2]&&__es5_server&&node.src.indexOf(__es5_server)===-1){try{var testUrl=new URL(a);if(testUrl.protocol==="http:"||testUrl.protocol==="https:"){node.src=__es5_server+"/p?url="+encodeURIComponent(a).replace(/%3A/gi, ":").replace(/%2F/gi, "/");}}catch(e){}}' +
|
| 143 |
'}' +
|
| 144 |
'}' +
|
| 145 |
'Element.prototype.appendChild=function(node){' +
|
|
@@ -152,10 +152,8 @@ function jsWrapper(serverBase, baseUrl, transpiledCode, needRegenerator) {
|
|
| 152 |
'};' +
|
| 153 |
'}' +
|
| 154 |
'}catch(e){}' +
|
| 155 |
-
'try{if(window.__es5CssVarsApply)window.__es5CssVarsApply();}catch(e){}' +
|
| 156 |
-
|
| 157 |
-
// который выполняется ниже в этой же функции (transpiledCode вставлен прямо сюда).
|
| 158 |
-
// Остальная Лампа продолжает использовать настоящие window.fetch / window.XMLHttpRequest.
|
| 159 |
'var _origFetch=window.fetch;' +
|
| 160 |
'var fetch=function(input,init){' +
|
| 161 |
'try{' +
|
|
@@ -174,9 +172,6 @@ function jsWrapper(serverBase, baseUrl, transpiledCode, needRegenerator) {
|
|
| 174 |
'};' +
|
| 175 |
'return xhr;' +
|
| 176 |
'};' +
|
| 177 |
-
// Локальный шим Lampa.Reguest — только для кода этого плагина.
|
| 178 |
-
// network.silent(url,...) / network["native"](url,...) — основной способ,
|
| 179 |
-
// которым плагины (в т.ч. этот) реально ходят за данными, минуя fetch/XHR напрямую.
|
| 180 |
'try{' +
|
| 181 |
'if(Lampa&&Lampa.Reguest){' +
|
| 182 |
'var _RealReguest=Lampa.Reguest;' +
|
|
@@ -200,7 +195,8 @@ function jsWrapper(serverBase, baseUrl, transpiledCode, needRegenerator) {
|
|
| 200 |
'Lampa=Object.create(Lampa);' +
|
| 201 |
'Lampa.Reguest=ReguestShim;' +
|
| 202 |
'}' +
|
| 203 |
-
'}catch(e){}'
|
|
|
|
| 204 |
'try{' +
|
| 205 |
'var __mock_script = document.createElement("script");' +
|
| 206 |
'__mock_script.src = __es5_base;' +
|
|
@@ -217,13 +213,15 @@ function jsWrapper(serverBase, baseUrl, transpiledCode, needRegenerator) {
|
|
| 217 |
'try { if(__orig_cs_desc) Object.defineProperty(document, "currentScript", __orig_cs_desc); else delete document.currentScript; } catch(e_cs2) {}' +
|
| 218 |
'}' +
|
| 219 |
'__es5_active=false;' +
|
| 220 |
-
'try{if(window.__es5CssVarsApply)window.__es5CssVarsApply();}catch(e){}' +
|
| 221 |
'})();'
|
| 222 |
|
| 223 |
const parts = []
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
|
|
|
|
|
|
|
| 227 |
parts.push(patchLoaders)
|
| 228 |
return parts.join('\n')
|
| 229 |
}
|
|
@@ -316,32 +314,41 @@ app.get('/p', async (req, res) => {
|
|
| 316 |
const source = normalizeUrl(rawUrl)
|
| 317 |
if (!source) return res.status(400).type('text/plain').send('Bad url')
|
| 318 |
|
|
|
|
|
|
|
|
|
|
| 319 |
try {
|
| 320 |
const { text } = await fetchText(source, 25_000, req.headers['user-agent'])
|
| 321 |
-
|
| 322 |
-
|
| 323 |
-
|
| 324 |
-
|
| 325 |
-
|
| 326 |
-
|
| 327 |
-
|
| 328 |
-
|
| 329 |
-
|
| 330 |
-
|
| 331 |
-
|
| 332 |
-
|
| 333 |
-
|
| 334 |
-
|
| 335 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 336 |
]
|
| 337 |
-
|
| 338 |
-
|
| 339 |
-
|
| 340 |
-
|
| 341 |
-
|
|
|
|
| 342 |
|
| 343 |
const serverBase = getSelfBase(req)
|
| 344 |
-
const out = jsWrapper(serverBase, source, code, needRegenerator)
|
| 345 |
|
| 346 |
res.setHeader('content-type', 'application/javascript; charset=utf-8')
|
| 347 |
res.setHeader('cache-control', 'public, max-age=600')
|
|
@@ -361,7 +368,8 @@ const RAW_METHODS = ['GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS']
|
|
| 361 |
const REQ_HEADERS_SKIP = new Set([
|
| 362 |
'host', 'connection', 'content-length', 'accept-encoding',
|
| 363 |
'x-forwarded-for', 'x-forwarded-proto', 'x-forwarded-host',
|
| 364 |
-
'cf-connecting-ip', 'cf-ray', 'cf-visitor', 'x-real-ip'
|
|
|
|
| 365 |
])
|
| 366 |
const RES_HEADERS_PASS = [
|
| 367 |
'content-type', 'content-length', 'content-range', 'accept-ranges',
|
|
@@ -393,8 +401,8 @@ app.all('/r', async (req, res) => {
|
|
| 393 |
headers[h] = req.headers[h]
|
| 394 |
}
|
| 395 |
headers['user-agent'] = req.headers['user-agent']
|
| 396 |
-
? req.headers['user-agent']
|
| 397 |
-
: 'Mozilla/5.0 (
|
| 398 |
|
| 399 |
let body
|
| 400 |
if (method !== 'GET' && method !== 'HEAD') {
|
|
@@ -414,6 +422,12 @@ app.all('/r', async (req, res) => {
|
|
| 414 |
|
| 415 |
res.status(upstream.status)
|
| 416 |
for (const h of RES_HEADERS_PASS) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 417 |
const v = upstream.headers.get(h)
|
| 418 |
if (v) res.setHeader(h, v)
|
| 419 |
}
|
|
|
|
| 53 |
redirect: 'follow',
|
| 54 |
signal: controller.signal,
|
| 55 |
headers: {
|
| 56 |
+
'user-agent': clientUA ? clientUA : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36'
|
| 57 |
}
|
| 58 |
})
|
| 59 |
|
|
|
|
| 81 |
}
|
| 82 |
}
|
| 83 |
|
| 84 |
+
function jsWrapper(serverBase, baseUrl, transpiledCode, needRegenerator, useVpn, useEs5) {
|
| 85 |
const safeServer = JSON.stringify(String(serverBase || ''))
|
| 86 |
const safeBaseUrl = JSON.stringify(String(baseUrl || ''))
|
| 87 |
|
|
|
|
| 123 |
'function origin(u){try{var a=document.createElement("a");a.href=u;return (a.protocol? a.protocol:"") + "//" + (a.host||"");}catch(e){return "";}}' +
|
| 124 |
'var allow=window.__es5ProxyAllow||{};window.__es5ProxyAllow=allow;' +
|
| 125 |
'allow[origin(__es5_base)]=1;' +
|
| 126 |
+
'function proxyJs(u){var a=abs(u);var o=origin(a);if(__es5_active)allow[o]=1;allow[o]=1;if(allow[o]&&__es5_server){try{var testUrl=new URL(a);if(testUrl.protocol==="http:"||testUrl.protocol==="https:")return __es5_server + "/p?url=" + encodeURIComponent(a).replace(/%3A/gi, ":").replace(/%2F/gi, "/") + "&es5=' + (useEs5?'1':'0') + '&vpn=' + (useVpn?'1':'0') + '";}catch(e){}}return a;}' +
|
| 127 |
'function proxyCss(u){var a=abs(u);var o=origin(a);if(__es5_active)allow[o]=1;if(allow[o]&&__es5_server)return __es5_server + "/s?url=" + encodeURIComponent(a).replace(/%3A/gi, ":").replace(/%2F/gi, "/");return a;}' +
|
| 128 |
'function proxyRaw(u){var a=abs(u);if(!/^https?:\\/\\//i.test(a))return u;if(__es5_server&&a.indexOf(__es5_server)===-1){return __es5_server+"/r?url="+encodeURIComponent(a).replace(/%3A/gi, ":").replace(/%2F/gi, "/");}return a;}' +
|
| 129 |
'function mapArr(items, fn){if(!items||!items.length)return items;for(var i=0;i<items.length;i++){items[i]=fn(items[i]);}return items;}' +
|
|
|
|
| 139 |
'var origInsertBefore=Element.prototype.insertBefore;' +
|
| 140 |
'function patchNode(node){' +
|
| 141 |
'if(node&&node.tagName==="SCRIPT"&&node.src){' +
|
| 142 |
+
'var a=abs(node.src);var o2=origin(a);if(__es5_active)allow[o2]=1;allow[o2]=1;if(allow[o2]&&__es5_server&&node.src.indexOf(__es5_server)===-1){try{var testUrl=new URL(a);if(testUrl.protocol==="http:"||testUrl.protocol==="https:"){node.src=__es5_server+"/p?url="+encodeURIComponent(a).replace(/%3A/gi, ":").replace(/%2F/gi, "/")+"&es5=' + (useEs5?'1':'0') + '&vpn=' + (useVpn?'1':'0') + '";}}catch(e){}}' +
|
| 143 |
'}' +
|
| 144 |
'}' +
|
| 145 |
'Element.prototype.appendChild=function(node){' +
|
|
|
|
| 152 |
'};' +
|
| 153 |
'}' +
|
| 154 |
'}catch(e){}' +
|
| 155 |
+
(useEs5 ? 'try{if(window.__es5CssVarsApply)window.__es5CssVarsApply();}catch(e){}' : '') +
|
| 156 |
+
(useVpn ? (
|
|
|
|
|
|
|
| 157 |
'var _origFetch=window.fetch;' +
|
| 158 |
'var fetch=function(input,init){' +
|
| 159 |
'try{' +
|
|
|
|
| 172 |
'};' +
|
| 173 |
'return xhr;' +
|
| 174 |
'};' +
|
|
|
|
|
|
|
|
|
|
| 175 |
'try{' +
|
| 176 |
'if(Lampa&&Lampa.Reguest){' +
|
| 177 |
'var _RealReguest=Lampa.Reguest;' +
|
|
|
|
| 195 |
'Lampa=Object.create(Lampa);' +
|
| 196 |
'Lampa.Reguest=ReguestShim;' +
|
| 197 |
'}' +
|
| 198 |
+
'}catch(e){}'
|
| 199 |
+
) : '') +
|
| 200 |
'try{' +
|
| 201 |
'var __mock_script = document.createElement("script");' +
|
| 202 |
'__mock_script.src = __es5_base;' +
|
|
|
|
| 213 |
'try { if(__orig_cs_desc) Object.defineProperty(document, "currentScript", __orig_cs_desc); else delete document.currentScript; } catch(e_cs2) {}' +
|
| 214 |
'}' +
|
| 215 |
'__es5_active=false;' +
|
| 216 |
+
(useEs5 ? 'try{if(window.__es5CssVarsApply)window.__es5CssVarsApply();}catch(e){}' : '') +
|
| 217 |
'})();'
|
| 218 |
|
| 219 |
const parts = []
|
| 220 |
+
if (useEs5) {
|
| 221 |
+
parts.push(fetchPolyfill)
|
| 222 |
+
if (needRegenerator) parts.push(regeneratorRuntimeCode)
|
| 223 |
+
parts.push(cssVarsPonyfill)
|
| 224 |
+
}
|
| 225 |
parts.push(patchLoaders)
|
| 226 |
return parts.join('\n')
|
| 227 |
}
|
|
|
|
| 314 |
const source = normalizeUrl(rawUrl)
|
| 315 |
if (!source) return res.status(400).type('text/plain').send('Bad url')
|
| 316 |
|
| 317 |
+
const useEs5 = req.query.es5 !== '0';
|
| 318 |
+
const useVpn = req.query.vpn !== '0';
|
| 319 |
+
|
| 320 |
try {
|
| 321 |
const { text } = await fetchText(source, 25_000, req.headers['user-agent'])
|
| 322 |
+
let code = text;
|
| 323 |
+
let needRegenerator = false;
|
| 324 |
+
|
| 325 |
+
if (useEs5) {
|
| 326 |
+
const result = await babel.transformAsync(text, {
|
| 327 |
+
babelrc: false,
|
| 328 |
+
configFile: false,
|
| 329 |
+
sourceType: 'script',
|
| 330 |
+
compact: true,
|
| 331 |
+
comments: false,
|
| 332 |
+
presets: [
|
| 333 |
+
[
|
| 334 |
+
require('@babel/preset-env'),
|
| 335 |
+
{
|
| 336 |
+
targets: { chrome: '38' },
|
| 337 |
+
bugfixes: true,
|
| 338 |
+
loose: true,
|
| 339 |
+
modules: false
|
| 340 |
+
}
|
| 341 |
+
]
|
| 342 |
]
|
| 343 |
+
})
|
| 344 |
+
if (result && result.code) {
|
| 345 |
+
code = result.code;
|
| 346 |
+
needRegenerator = code.indexOf('regeneratorRuntime') !== -1;
|
| 347 |
+
}
|
| 348 |
+
}
|
| 349 |
|
| 350 |
const serverBase = getSelfBase(req)
|
| 351 |
+
const out = jsWrapper(serverBase, source, code, needRegenerator, useVpn, useEs5)
|
| 352 |
|
| 353 |
res.setHeader('content-type', 'application/javascript; charset=utf-8')
|
| 354 |
res.setHeader('cache-control', 'public, max-age=600')
|
|
|
|
| 368 |
const REQ_HEADERS_SKIP = new Set([
|
| 369 |
'host', 'connection', 'content-length', 'accept-encoding',
|
| 370 |
'x-forwarded-for', 'x-forwarded-proto', 'x-forwarded-host',
|
| 371 |
+
'cf-connecting-ip', 'cf-ray', 'cf-visitor', 'x-real-ip',
|
| 372 |
+
'origin', 'referer'
|
| 373 |
])
|
| 374 |
const RES_HEADERS_PASS = [
|
| 375 |
'content-type', 'content-length', 'content-range', 'accept-ranges',
|
|
|
|
| 401 |
headers[h] = req.headers[h]
|
| 402 |
}
|
| 403 |
headers['user-agent'] = req.headers['user-agent']
|
| 404 |
+
? req.headers['user-agent']
|
| 405 |
+
: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36'
|
| 406 |
|
| 407 |
let body
|
| 408 |
if (method !== 'GET' && method !== 'HEAD') {
|
|
|
|
| 422 |
|
| 423 |
res.status(upstream.status)
|
| 424 |
for (const h of RES_HEADERS_PASS) {
|
| 425 |
+
if (h === 'content-length') {
|
| 426 |
+
const ct = upstream.headers.get('content-type') || '';
|
| 427 |
+
if (!ct.includes('video/') && !ct.includes('audio/') && !upstream.headers.has('content-range')) {
|
| 428 |
+
continue; // fetch decompresses gzip/br, so original content-length is invalid for text/json
|
| 429 |
+
}
|
| 430 |
+
}
|
| 431 |
const v = upstream.headers.get(h)
|
| 432 |
if (v) res.setHeader(h, v)
|
| 433 |
}
|