Proxy dev SignalR hub locally
This commit is contained in:
@@ -37,7 +37,8 @@ PNPM := pnpm
|
||||
PID_FILE := .dev.pid
|
||||
LOG_FILE := .dev.log
|
||||
API_TARGET ?= https://flights.test.aeroflot.ru
|
||||
SIGNALR_HUB_URL ?= https://platform.test.aeroflot.ru/tracker/hub
|
||||
TRACKER_TARGET ?= https://platform.test.aeroflot.ru
|
||||
SIGNALR_HUB_URL ?= http://localhost:8080/tracker/hub
|
||||
|
||||
# Development
|
||||
dev:
|
||||
@@ -48,10 +49,11 @@ dev:
|
||||
|
||||
dev-full:
|
||||
@echo "Starting dev server with API proxy in background..."
|
||||
@API_TARGET="$(API_TARGET)" SIGNALR_HUB_URL="$(SIGNALR_HUB_URL)" nohup $(PNPM) dev:full > $(LOG_FILE) 2>&1 & echo $$! > $(PID_FILE)
|
||||
@API_TARGET="$(API_TARGET)" TRACKER_TARGET="$(TRACKER_TARGET)" SIGNALR_HUB_URL="$(SIGNALR_HUB_URL)" nohup $(PNPM) dev:full > $(LOG_FILE) 2>&1 & echo $$! > $(PID_FILE)
|
||||
@echo "Dev server started (PID: $$(cat $(PID_FILE)))"
|
||||
@echo " App & API: http://localhost:8080"
|
||||
@echo " API target: $(API_TARGET)"
|
||||
@echo " Tracker target: $(TRACKER_TARGET)"
|
||||
@echo " SignalR hub: $(SIGNALR_HUB_URL)"
|
||||
@echo "View logs: make logs"
|
||||
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ const isRemote = buildTarget === "remote";
|
||||
// '{z}/{x}/{y}', so we serialize the whole payload to base64 and decode
|
||||
// it in the browser at runtime. Base64 output is A-Z/a-z/0-9/+/=/ — no
|
||||
// braces for the template engine to grab.
|
||||
const PUBLIC_ENV_KEYS = ["MAP_TILE_URL", "API_BASE_URL"] as const;
|
||||
const PUBLIC_ENV_KEYS = ["MAP_TILE_URL", "API_BASE_URL", "SIGNALR_HUB_URL"] as const;
|
||||
const PUBLIC_ENV: Record<string, string> = {};
|
||||
for (const k of PUBLIC_ENV_KEYS) {
|
||||
const v = process.env[k];
|
||||
|
||||
+27
-1
@@ -6,10 +6,12 @@
|
||||
* /api/* → curl → https://flights.test.aeroflot.ru (bypasses WAF via curl TLS)
|
||||
* /flights/* → curl → https://flights.test.aeroflot.ru
|
||||
* /map/* → curl → https://flights.test.aeroflot.ru (binary JPEG tiles)
|
||||
* /tracker/* → https://platform.test.aeroflot.ru (SignalR, same-origin)
|
||||
* /* → localhost:8081 (Modern.js SSR + HMR)
|
||||
*/
|
||||
import express from "express";
|
||||
import { createProxyMiddleware } from "http-proxy-middleware";
|
||||
import { HttpsProxyAgent } from "https-proxy-agent";
|
||||
import { execFile, spawn } from "node:child_process";
|
||||
import { resolve } from "node:path";
|
||||
import { existsSync } from "node:fs";
|
||||
@@ -18,6 +20,8 @@ import { tmpdir } from "node:os";
|
||||
const PUBLIC_PORT = 8080;
|
||||
const MODERNJS_PORT = 8081;
|
||||
const API_TARGET = process.env.API_TARGET || "https://flights.test.aeroflot.ru";
|
||||
const TRACKER_TARGET = process.env.TRACKER_TARGET || "https://platform.test.aeroflot.ru";
|
||||
const SYSTEM_PROXY = process.env.https_proxy || process.env.HTTPS_PROXY || "";
|
||||
const DEBUG_PROXY_BODY = process.env.DEBUG_PROXY_BODY === "1";
|
||||
|
||||
// Shared cookie jar so the Ngenix WAF cookie challenge (`ngenix_valid` +
|
||||
@@ -138,6 +142,21 @@ app.use(["/api", "/flights"], (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// --- SignalR TrackerHub proxy ---
|
||||
// Browser-direct localhost → platform.test.aeroflot.ru fails CORS. Keep the
|
||||
// hub same-origin in development and let proxy-helper / gost route the
|
||||
// upstream request through the TIM tunnel when HTTPS_PROXY is set.
|
||||
const trackerProxy = createProxyMiddleware({
|
||||
target: TRACKER_TARGET,
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
ws: true,
|
||||
logLevel: "warn",
|
||||
pathRewrite: (path) => `/tracker${path}`,
|
||||
...(SYSTEM_PROXY ? { agent: new HttpsProxyAgent(SYSTEM_PROXY) } : {}),
|
||||
});
|
||||
app.use("/tracker", trackerProxy);
|
||||
|
||||
function execCurlWithFallback(buildArgs, extraArgs, res) {
|
||||
runCurl([...extraArgs, ...buildArgs(true)], (direct) => {
|
||||
if (isSuccessfulUpstream(direct)) {
|
||||
@@ -252,12 +271,19 @@ app.use(modernProxy);
|
||||
const server = app.listen(PUBLIC_PORT, () => {
|
||||
console.log(`\n ✓ Dev server: http://localhost:${PUBLIC_PORT}`);
|
||||
console.log(` /api/* → curl → ${API_TARGET}`);
|
||||
console.log(` /tracker/* → proxy → ${TRACKER_TARGET}`);
|
||||
console.log(` /* → Modern.js :${MODERNJS_PORT}\n`);
|
||||
});
|
||||
|
||||
// Forward WebSocket upgrades to Modern.js HMR server explicitly,
|
||||
// preventing reconnection spam from http-proxy-middleware's built-in ws handling.
|
||||
server.on("upgrade", modernProxy.upgrade);
|
||||
server.on("upgrade", (req, socket, head) => {
|
||||
if (req.url?.startsWith("/tracker")) {
|
||||
trackerProxy.upgrade(req, socket, head);
|
||||
return;
|
||||
}
|
||||
modernProxy.upgrade(req, socket, head);
|
||||
});
|
||||
|
||||
process.on("SIGINT", () => { modernProcess.kill(); process.exit(); });
|
||||
process.on("SIGTERM", () => { modernProcess.kill(); process.exit(); });
|
||||
|
||||
@@ -228,9 +228,9 @@ async function defaultBuildConnection(
|
||||
url: string,
|
||||
delays: number[],
|
||||
): Promise<HubConnectionLike> {
|
||||
const { HubConnectionBuilder, HttpTransportType, LogLevel } = await import("@microsoft/signalr");
|
||||
const { HubConnectionBuilder, LogLevel } = await import("@microsoft/signalr");
|
||||
return new HubConnectionBuilder()
|
||||
.withUrl(url, { transport: HttpTransportType.WebSockets })
|
||||
.withUrl(url)
|
||||
.withAutomaticReconnect(delays)
|
||||
// Suppress SignalR's internal console logging. The hub is optional
|
||||
// (failures degrade to polling) and we already handle status via
|
||||
|
||||
Reference in New Issue
Block a user