Skip to content

Commit 4b9fef0

Browse files
authored
Merge pull request #17 from nickprice101/codex/fix-device-list-caching
Prevent stale device list caching
2 parents 42323f7 + 2d28b09 commit 4b9fef0

2 files changed

Lines changed: 42 additions & 4 deletions

File tree

server.js

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ const execFileAsync = promisify(execFile);
2626
const __dirname = path.dirname(fileURLToPath(import.meta.url));
2727
const app = express();
2828
const PORT = Number(process.env.PORT) || 3001;
29+
app.disable("etag");
2930

3031
// ── Rate limiting ─────────────────────────────────────────────────────────────
3132
// Limits each IP to 120 requests per minute to prevent abuse.
@@ -39,7 +40,33 @@ app.use(limiter);
3940

4041
// ── Static files (production build) ─────────────────────────────────────────
4142
const distPath = path.join(__dirname, "dist");
42-
app.use(express.static(distPath));
43+
app.use("/api", (_req, res, next) => {
44+
res.set({
45+
"Cache-Control": "no-store, no-cache, must-revalidate, proxy-revalidate",
46+
Pragma: "no-cache",
47+
Expires: "0",
48+
"Surrogate-Control": "no-store",
49+
});
50+
next();
51+
});
52+
53+
app.use(
54+
express.static(distPath, {
55+
etag: false,
56+
setHeaders: (res, filePath) => {
57+
if (filePath.endsWith(".html")) {
58+
res.setHeader("Cache-Control", "no-store, no-cache, must-revalidate, proxy-revalidate");
59+
res.setHeader("Pragma", "no-cache");
60+
res.setHeader("Expires", "0");
61+
return;
62+
}
63+
64+
if (/\.[A-Za-z0-9_-]+\.(js|css)$/.test(path.basename(filePath))) {
65+
res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
66+
}
67+
},
68+
})
69+
);
4370

4471
// ── Helpers ──────────────────────────────────────────────────────────────────
4572

@@ -671,6 +698,11 @@ app.get("/api/router/devices", async (req, res) => {
671698

672699
// ── SPA fallback ─────────────────────────────────────────────────────────────
673700
app.get("/{*path}", (_req, res) => {
701+
res.set({
702+
"Cache-Control": "no-store, no-cache, must-revalidate, proxy-revalidate",
703+
Pragma: "no-cache",
704+
Expires: "0",
705+
});
674706
res.sendFile(path.join(distPath, "index.html"));
675707
});
676708

src/App.jsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ import { Database, Globe, History, Plus, Router, Wallet, AlertTriangle, Calendar
3131
* The UI falls back gracefully when the endpoints are not reachable.
3232
*/
3333

34-
const STORAGE_KEY = "mobile-data-dashboard-v1";
34+
const STORAGE_KEY = "mobile-data-dashboard-v2";
35+
const FETCH_CACHE_BUSTER = "slate7-device-list-v2";
3536
const DEFAULT_POLLING_MINUTES = 15;
3637
const COUNTRY_OPTIONS = [
3738
"Netherlands",
@@ -89,6 +90,11 @@ function fmtGB(v) {
8990
return `${(n * 1024).toFixed(0)} MB`;
9091
}
9192

93+
function withCacheBust(url) {
94+
const value = `${FETCH_CACHE_BUSTER}-${Date.now()}`;
95+
return url.includes("?") ? `${url}&_=${encodeURIComponent(value)}` : `${url}?_=${encodeURIComponent(value)}`;
96+
}
97+
9298

9399
function addDays(isoDate, days) {
94100
const d = new Date(isoDate);
@@ -424,7 +430,7 @@ export default function MobileDataDashboard() {
424430
setSyncError("");
425431
try {
426432
const response = await fetch(
427-
`/api/router/usage?iface=${encodeURIComponent(state.settings.interfaceName || "wwan0")}`,
433+
withCacheBust(`/api/router/usage?iface=${encodeURIComponent(state.settings.interfaceName || "wwan0")}`),
428434
{ cache: "no-store" }
429435
);
430436
if (!response.ok) throw new Error(`HTTP ${response.status}`);
@@ -467,7 +473,7 @@ export default function MobileDataDashboard() {
467473
setDeviceDetectMessage("");
468474

469475
try {
470-
const response = await fetch("/api/router/devices", { cache: "no-store" });
476+
const response = await fetch(withCacheBust("/api/router/devices"), { cache: "no-store" });
471477
if (!response.ok) throw new Error("Router device endpoint not available");
472478

473479
const payload = await response.json();

0 commit comments

Comments
 (0)