← back to Cronjobs Viewer
5x sweep 2: fix intermittent favicon.ico 404 console error (server route + <link rel=icon>)
f4729fe52ae4c5acade8a69010389b98869497e1 · 2026-08-25 11:28:53 -0700 · steve
Files touched
A 5x/capture-404.jsA 5x/capture-console.jsM public/index.htmlM server.js
Diff
commit f4729fe52ae4c5acade8a69010389b98869497e1
Author: steve <steve@designerwallcoverings.com>
Date: Tue Aug 25 11:28:53 2026 -0700
5x sweep 2: fix intermittent favicon.ico 404 console error (server route + <link rel=icon>)
---
5x/capture-404.js | 13 +++++++++++++
5x/capture-console.js | 19 +++++++++++++++++++
public/index.html | 1 +
server.js | 8 ++++++++
4 files changed, 41 insertions(+)
diff --git a/5x/capture-404.js b/5x/capture-404.js
new file mode 100644
index 0000000..c173e4a
--- /dev/null
+++ b/5x/capture-404.js
@@ -0,0 +1,13 @@
+const pw = require('/Users/macstudio3/.npm-global/lib/node_modules/playwright');
+const CHROME = '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome';
+(async () => {
+ const b = await pw.chromium.launch({ executablePath: CHROME });
+ const ctx = await b.newContext({ httpCredentials:{username:'admin',password:'DW2024!'}});
+ const p = await ctx.newPage();
+ const bad = [];
+ p.on('response', r => { if (r.status() >= 400) bad.push(`${r.status()} ${r.url()}`); });
+ await p.goto('http://127.0.0.1:9772/', { waitUntil:'networkidle', timeout:20000 });
+ await p.waitForTimeout(1500);
+ await b.close();
+ console.log(bad.length ? bad.join('\n') : 'NO 4xx/5xx');
+})();
diff --git a/5x/capture-console.js b/5x/capture-console.js
new file mode 100644
index 0000000..01680de
--- /dev/null
+++ b/5x/capture-console.js
@@ -0,0 +1,19 @@
+const pw = require('/Users/macstudio3/.npm-global/lib/node_modules/playwright');
+const CHROME = '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome';
+(async () => {
+ const b = await pw.chromium.launch({ executablePath: CHROME });
+ const seen = [];
+ for (let i = 0; i < 8; i++) {
+ const ctx = await b.newContext({ httpCredentials: { username:'admin', password:'DW2024!' }});
+ const p = await ctx.newPage();
+ const local = [];
+ p.on('console', m => { if (m.type() === 'error') local.push('CONSOLE: ' + m.text()); });
+ p.on('pageerror', e => local.push('PAGEERROR: ' + e.message));
+ await p.goto('http://127.0.0.1:9772/', { waitUntil:'networkidle', timeout:20000 }).catch(e=>local.push('GOTO: '+e.message));
+ await p.waitForTimeout(1200);
+ if (local.length) seen.push(`load#${i}: ` + local.join(' || '));
+ await ctx.close();
+ }
+ await b.close();
+ console.log(seen.length ? seen.join('\n') : 'NO ERRORS across 8 loads');
+})();
diff --git a/public/index.html b/public/index.html
index 6ecc949..8efe14d 100644
--- a/public/index.html
+++ b/public/index.html
@@ -1,6 +1,7 @@
<!doctype html>
<html lang="en">
<head>
+<link rel="icon" href="/favicon.ico">
<script>
// Credential-safe fetch guard (fleet drop-in) — if this page is opened with
// credentials in the URL (a saved bookmark / Chrome-remembered basic-auth:
diff --git a/server.js b/server.js
index 928203d..79bce7b 100644
--- a/server.js
+++ b/server.js
@@ -171,6 +171,14 @@ app.get('/api/crons', async (req, res) => {
});
app.get('/healthz', (req, res) => res.json({ ok: true }));
+// Favicon: browsers auto-request /favicon.ico with no HTML reference; without this
+// route express.static 404s it → a (cached, intermittent) console error. Serve a tiny
+// inline SVG clock so any client — including a direct bookmark hit — gets a 200.
+app.get('/favicon.ico', (_req, res) => {
+ res.type('image/svg+xml').set('Cache-Control', 'public, max-age=86400').send(
+ "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><text y='13' font-size='13'>🕓</text></svg>"
+ );
+});
app.use(express.static(path.join(__dirname, 'public')));
app.listen(PORT, () => console.log(`cronjobs-viewer on http://127.0.0.1:${PORT} (Basic ${AUTH_USER}/****)`));
← aa15665 creds-safe fetch guard: resolve relative fetch vs credential
·
back to Cronjobs Viewer
·
5x: REPORT + capture harness (2 clean sweeps, favicon 404 fi 62c68e7 →