Fix five console-level issues surfaced by live-deploy Playwright audit

1. FlightsMap tiles didn't render: MapCanvas inline height:100% resolved
   to 0 against min-height parents. Hand sizing to consumer CSS so
   .flights-map-start__map height:500px wins.

2. FlightsMap /map/api/tile/{z}/{x}/{y}.jpeg requests fell through to
   Modern.js SSR (HTML body). Dev proxy now forwards /map/* to the
   test env via curl with image headers and binary-safe piping.

3. PopularRequestsPanel duplicate React key (Route-SVO-LED appears
   twice in upstream). Suffix the key with the visible index.

4. OnlineBoardDetailsPage /onlineboard/details 400. Upstream expects
   an ISO datetime (yyyy-MM-DDTHH:mm:ss), matching Angular's
   ApiFormatterService.formatDate. Append T00:00:00.

5. Browser-level SignalR CORS errors on every details page: the
   default SIGNALR_HUB_URL pointed at an unreachable placeholder.
   Default to empty + skip the connection in useLiveFlights when
   blank. Also configureLogging(LogLevel.None) so SignalR stops
   writing its own negotiation failures to console. Live updates
   re-enable by setting SIGNALR_HUB_URL on a deployment.
This commit is contained in:
2026-04-17 21:55:44 +03:00
parent b54746c74c
commit c8d0caa9cf
7 changed files with 68 additions and 7 deletions
+36
View File
@@ -5,6 +5,7 @@
* Port 8080 (browser-facing):
* /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)
* /* → localhost:8081 (Modern.js SSR + HMR)
*/
import express from "express";
@@ -58,6 +59,41 @@ app.get("/api/appSettings", (req, res) => {
});
});
// --- Tile proxy via curl (binary JPEG, pipe stdout straight to response) ---
const TILE_CONTENT_TYPES = {
".jpeg": "image/jpeg",
".jpg": "image/jpeg",
".png": "image/png",
".webp": "image/webp",
};
app.use("/map", (req, res) => {
const targetUrl = `${API_TARGET}${req.originalUrl}`;
const ext = req.path.substring(req.path.lastIndexOf("."));
const contentType = TILE_CONTENT_TYPES[ext] ?? "application/octet-stream";
res.status(200);
res.setHeader("Content-Type", contentType);
res.setHeader("Cache-Control", "public, max-age=86400");
const args = [
"-sS",
"-f", // fail on HTTP 4xx/5xx
"-H", `Accept: image/webp,image/jpeg,image/*,*/*`,
"-H", `User-Agent: ${req.headers["user-agent"] || "Mozilla/5.0"}`,
"-H", `Referer: ${API_TARGET}/`,
targetUrl,
];
const child = spawn("/usr/bin/curl", args, { stdio: ["ignore", "pipe", "pipe"] });
child.stdout.pipe(res);
child.on("exit", (code) => {
if (code !== 0) {
// -f means upstream failed; close cleanly with empty body.
res.end();
}
});
});
// --- API proxy via curl (bypasses WAF TLS fingerprinting) ---
app.use(["/api", "/flights"], (req, res) => {
const targetUrl = `${API_TARGET}${req.originalUrl}`;