Remove env endpoint and mark live APIs no-store

This commit is contained in:
scamiv
2026-03-22 20:43:01 +01:00
parent e003ffc468
commit 0635daa742
8 changed files with 50 additions and 45 deletions
-19
View File
@@ -195,25 +195,6 @@ server {
add_header Cache-Control "public, max-age=86400"; # 24 hours
}
# /api/env endpoint - Cache for 1 hour
location = /api/env {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
# Cache configuration
proxy_cache API_CACHE;
proxy_cache_valid 200 1h; # Cache successful responses for 1 hour
proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;
proxy_cache_lock on;
add_header X-Cache-Status $upstream_cache_status;
# Standard proxy headers
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# /api/health endpoint - No caching, always hit the backend
location = /api/health {
proxy_pass http://127.0.0.1:3000;
+3 -12
View File
@@ -40,20 +40,11 @@ export async function getServerConfigFromClient(): Promise<ServerConfig> {
}
const bootstrapGameEnv = window.BOOTSTRAP_CONFIG?.gameEnv;
if (bootstrapGameEnv) {
cachedSC = getServerConfig(bootstrapGameEnv);
return cachedSC;
if (!bootstrapGameEnv) {
throw new Error("Missing bootstrap server config");
}
const response = await fetch("/api/env");
if (!response.ok) {
throw new Error(
`Failed to fetch server config: ${response.status} ${response.statusText}`,
);
}
const config = await response.json();
cachedSC = getServerConfig(config.game_env);
cachedSC = getServerConfig(bootstrapGameEnv);
return cachedSC;
}
export function getServerConfigFromServer(): ServerConfig {
+2
View File
@@ -14,6 +14,7 @@ import {
ExternalGameInfo,
ExternalGameInfoSchema,
} from "./GamePreviewBuilder";
import { setNoStoreHeaders } from "./NoStoreHeaders";
import { renderHtmlContent, setHtmlNoCacheHeaders } from "./RenderHtml";
const requestOrigin = (req: Request, config: ServerConfig): string => {
@@ -151,6 +152,7 @@ export function registerGamePreviewRoute(opts: {
}
// Fallback to JSON if HTML file not found
setNoStoreHeaders(res);
res.setHeader("Content-Type", "application/json");
return res.send(JSON.stringify(lobby ?? publicInfo, replacer));
} catch (error) {
+6 -8
View File
@@ -10,6 +10,7 @@ import { getServerConfigFromServer } from "../core/configuration/ConfigLoader";
import { logger } from "./Logger";
import { MapPlaylist } from "./MapPlaylist";
import { MasterLobbyService } from "./MasterLobbyService";
import { setNoStoreHeaders } from "./NoStoreHeaders";
import { renderHtml } from "./RenderHtml";
import { applyStaticAssetCacheControl } from "./StaticAssetCache";
@@ -61,6 +62,11 @@ app.use(
}),
);
app.use("/api", (_req, res, next) => {
setNoStoreHeaders(res);
next();
});
// Start the master process
export async function startMaster() {
if (!cluster.isPrimary) {
@@ -133,14 +139,6 @@ export async function startMaster() {
});
}
app.get("/api/env", async (req, res) => {
const envConfig = {
game_env: process.env.GAME_ENV,
};
if (!envConfig.game_env) return res.sendStatus(500);
res.json(envConfig);
});
app.get("/api/health", (_req, res) => {
const ready = lobbyService?.isHealthy() ?? false;
if (ready) {
+10
View File
@@ -0,0 +1,10 @@
import type { Response } from "express";
export function setNoStoreHeaders(res: Response): void {
res.setHeader(
"Cache-Control",
"no-store, no-cache, must-revalidate, proxy-revalidate",
);
res.setHeader("Pragma", "no-cache");
res.setHeader("Expires", "0");
}
+2 -6
View File
@@ -2,6 +2,7 @@ import ejs from "ejs";
import type { Response } from "express";
import fs from "fs/promises";
import { buildAssetUrl } from "../core/AssetUrls";
import { setNoStoreHeaders } from "./NoStoreHeaders";
import { getRuntimeAssetManifest } from "./RuntimeAssetManifest";
export async function renderHtmlContent(htmlPath: string): Promise<string> {
@@ -25,12 +26,7 @@ export async function renderHtmlContent(htmlPath: string): Promise<string> {
}
export function setHtmlNoCacheHeaders(res: Response): void {
res.setHeader(
"Cache-Control",
"no-store, no-cache, must-revalidate, proxy-revalidate",
);
res.setHeader("Pragma", "no-cache");
res.setHeader("Expires", "0");
setNoStoreHeaders(res);
res.setHeader("ETag", "");
res.setHeader("Content-Type", "text/html");
}
+6
View File
@@ -27,6 +27,7 @@ import { logger } from "./Logger";
import { GameEnv } from "../core/configuration/Config";
import { MapPlaylist } from "./MapPlaylist";
import { setNoStoreHeaders } from "./NoStoreHeaders";
import { startPolling } from "./PollingLoop";
import { PrivilegeRefresher } from "./PrivilegeRefresher";
import { applyStaticAssetCacheControl } from "./StaticAssetCache";
@@ -139,6 +140,11 @@ export async function startWorker() {
}),
);
app.use("/api", (_req, res, next) => {
setNoStoreHeaders(res);
next();
});
app.post("/api/create_game/:id", async (req, res) => {
const id = req.params.id;
+21
View File
@@ -0,0 +1,21 @@
import { describe, expect, test } from "vitest";
import { setNoStoreHeaders } from "../../src/server/NoStoreHeaders";
describe("NoStoreHeaders", () => {
test("sets explicit no-store headers", () => {
const headers = new Map<string, string>();
const response = {
setHeader(name: string, value: string) {
headers.set(name, value);
},
} as any;
setNoStoreHeaders(response);
expect(headers.get("Cache-Control")).toBe(
"no-store, no-cache, must-revalidate, proxy-revalidate",
);
expect(headers.get("Pragma")).toBe("no-cache");
expect(headers.get("Expires")).toBe("0");
});
});