Fix Aeroflot shell navigation links
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
const SAME_ORIGIN_AEROFLOT_ENDPOINTS =
|
||||
/https:\/\/(?:www\.)?aeroflot\.ru(?=\/(?:ws2|cms2|personal|offers|feedback|pkl|frontend\/static|media)(?:[/?#]|$))/g;
|
||||
|
||||
export function rewriteAeroflotShellUrls(value) {
|
||||
return value
|
||||
.replaceAll("https://gw.aeroflot.ru", "/gw")
|
||||
.replace(SAME_ORIGIN_AEROFLOT_ENDPOINTS, "");
|
||||
}
|
||||
+85
-11
@@ -16,6 +16,7 @@ import { execFile, spawn } from "node:child_process";
|
||||
import { resolve } from "node:path";
|
||||
import { existsSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { rewriteAeroflotShellUrls } from "./aeroflot-url-rewrite.mjs";
|
||||
|
||||
const PUBLIC_PORT = 8080;
|
||||
const MODERNJS_PORT = 8081;
|
||||
@@ -25,6 +26,7 @@ const AEROFLOT_STATIC_TARGET = process.env.AEROFLOT_STATIC_TARGET || "https://ww
|
||||
const SYSTEM_PROXY = process.env.https_proxy || process.env.HTTPS_PROXY || "";
|
||||
const DEBUG_PROXY_BODY = process.env.DEBUG_PROXY_BODY === "1";
|
||||
const LOCAL_PUBLIC_ORIGIN = `http://localhost:${PUBLIC_PORT}`;
|
||||
const shellStaticAssetCache = new Map();
|
||||
|
||||
// Shared cookie jar so the Ngenix WAF cookie challenge (`ngenix_valid` +
|
||||
// 307-to-self) only runs once per dev-server lifetime, not per request.
|
||||
@@ -233,23 +235,86 @@ app.use(
|
||||
pathRewrite: (path) => `/frontend/static${path}`,
|
||||
selfHandleResponse: true,
|
||||
on: {
|
||||
proxyRes: responseInterceptor(async (buffer, proxyRes) => {
|
||||
const contentType = proxyRes.headers["content-type"] ?? "";
|
||||
proxyRes: responseInterceptor(async (buffer, proxyRes, req, res) => {
|
||||
const cacheKey = req.originalUrl ?? req.url ?? "";
|
||||
const contentType = inferShellStaticContentType(
|
||||
cacheKey,
|
||||
proxyRes.headers["content-type"],
|
||||
);
|
||||
if (!/(css|html|javascript|json|text)/i.test(String(contentType))) {
|
||||
if (proxyRes.statusCode && proxyRes.statusCode < 500) {
|
||||
shellStaticAssetCache.set(cacheKey, { body: buffer, contentType });
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
return buffer
|
||||
.toString("utf8")
|
||||
.replaceAll("https://gw.aeroflot.ru", "/gw")
|
||||
.replaceAll("https://www.aeroflot.ru", "")
|
||||
.replaceAll("https://aeroflot.ru", "");
|
||||
if (proxyRes.statusCode && proxyRes.statusCode >= 500) {
|
||||
const cached = shellStaticAssetCache.get(cacheKey);
|
||||
if (cached) {
|
||||
res.statusCode = 200;
|
||||
res.statusMessage = "OK";
|
||||
res.setHeader("content-type", cached.contentType);
|
||||
return cached.body;
|
||||
}
|
||||
|
||||
const retried = await retryAeroflotStaticAsset(cacheKey);
|
||||
if (retried) {
|
||||
const body = rewriteAeroflotShellUrls(retried.body);
|
||||
shellStaticAssetCache.set(cacheKey, { body, contentType });
|
||||
res.statusCode = 200;
|
||||
res.statusMessage = "OK";
|
||||
res.setHeader("content-type", contentType);
|
||||
return body;
|
||||
}
|
||||
}
|
||||
|
||||
const body = rewriteAeroflotShellUrls(buffer.toString("utf8"));
|
||||
if (!proxyRes.statusCode || proxyRes.statusCode < 500) {
|
||||
shellStaticAssetCache.set(cacheKey, { body, contentType });
|
||||
}
|
||||
return body;
|
||||
}),
|
||||
},
|
||||
...(SYSTEM_PROXY ? { agent: new HttpsProxyAgent(SYSTEM_PROXY) } : {}),
|
||||
}),
|
||||
);
|
||||
|
||||
function inferShellStaticContentType(requestPath, upstreamContentType) {
|
||||
const contentType = String(upstreamContentType ?? "");
|
||||
if (/(css|html|javascript|json|text)/i.test(contentType)) return contentType;
|
||||
const pathname = requestPath.split("?")[0] ?? "";
|
||||
if (pathname.endsWith(".js") || pathname.endsWith(".mjs")) return "application/javascript";
|
||||
if (pathname.endsWith(".css")) return "text/css";
|
||||
if (pathname.endsWith(".json")) return "application/json";
|
||||
if (pathname.endsWith(".html")) return "text/html";
|
||||
return contentType;
|
||||
}
|
||||
|
||||
function retryAeroflotStaticAsset(originalUrl) {
|
||||
const targetUrl = `${AEROFLOT_STATIC_TARGET}${originalUrl}`;
|
||||
const buildArgs = (noproxy) => [
|
||||
"-sS",
|
||||
"-f",
|
||||
"-L",
|
||||
...(noproxy ? ["--noproxy", "*"] : []),
|
||||
"-H", "Accept: */*",
|
||||
"-H", "User-Agent: Mozilla/5.0",
|
||||
targetUrl,
|
||||
];
|
||||
|
||||
return new Promise((resolveRetry) => {
|
||||
runCurlWithoutStatus(buildArgs(true), (direct) => {
|
||||
if (!direct.err && direct.stdout) {
|
||||
resolveRetry({ body: direct.stdout });
|
||||
return;
|
||||
}
|
||||
runCurlWithoutStatus(buildArgs(false), (proxy) => {
|
||||
resolveRetry(!proxy.err && proxy.stdout ? { body: proxy.stdout } : null);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
app.use("/personal/services/internal/v.0.0.1/json/get_member_info", (_req, res) => {
|
||||
res.status(200).json({ data: null });
|
||||
});
|
||||
@@ -337,6 +402,18 @@ function runCurl(args, cb) {
|
||||
});
|
||||
}
|
||||
|
||||
function runCurlWithoutStatus(args, cb) {
|
||||
execFile("/usr/bin/curl", args, { maxBuffer: 10 * 1024 * 1024, timeout: 30000 }, (err, stdout, stderr) => {
|
||||
if (err) {
|
||||
console.warn(`static curl retry failed: ${err.message}`);
|
||||
}
|
||||
if (stderr) {
|
||||
console.warn(`static curl retry stderr: ${stderr.substring(0, 300)}`);
|
||||
}
|
||||
cb({ err, stdout: stdout ?? "" });
|
||||
});
|
||||
}
|
||||
|
||||
function isSuccessfulUpstream({ err, stdout }) {
|
||||
if (err) return false;
|
||||
const lastNewline = stdout.lastIndexOf("\n");
|
||||
@@ -395,10 +472,7 @@ function respondWithAeroflotFrontendResult(result, res) {
|
||||
|
||||
const lastNewline = result.stdout.lastIndexOf("\n");
|
||||
const rawBody = lastNewline >= 0 ? result.stdout.substring(0, lastNewline) : result.stdout;
|
||||
const body = rawBody
|
||||
.replaceAll("https://gw.aeroflot.ru", "/gw")
|
||||
.replaceAll("https://www.aeroflot.ru", "")
|
||||
.replaceAll("https://aeroflot.ru", "");
|
||||
const body = rewriteAeroflotShellUrls(rawBody);
|
||||
const statusStr = lastNewline >= 0 ? result.stdout.substring(lastNewline + 1).trim() : "200";
|
||||
const status = parseInt(statusStr) || 200;
|
||||
const isJson = body.trimStart().startsWith("{") || body.trimStart().startsWith("[");
|
||||
|
||||
@@ -14,6 +14,7 @@ import http from "node:http";
|
||||
import https from "node:https";
|
||||
import { spawn } from "node:child_process";
|
||||
import { resolve } from "node:path";
|
||||
import { rewriteAeroflotShellUrls } from "./aeroflot-url-rewrite.mjs";
|
||||
|
||||
const PUBLIC_PORT = Number(process.env.PORT || 8080);
|
||||
const MODERNJS_PORT = Number(process.env.MODERN_SERVER_PORT || 8081);
|
||||
@@ -49,10 +50,7 @@ const app = express();
|
||||
const shellResponseCache = new Map();
|
||||
|
||||
function rewriteAeroflotUrls(value) {
|
||||
return value
|
||||
.replaceAll("https://gw.aeroflot.ru", "/gw")
|
||||
.replaceAll("https://www.aeroflot.ru", "")
|
||||
.replaceAll("https://aeroflot.ru", "");
|
||||
return rewriteAeroflotShellUrls(value);
|
||||
}
|
||||
|
||||
function requestOrigin(req) {
|
||||
|
||||
Reference in New Issue
Block a user