Address @qa review on PR #12 (issue #11)

- index.html: replace Image() probe with fetch(no-cors) + AbortController
  timeout. fetch resolves for any network response (incl. 4xx/5xx) so
  resolve -> 'up', reject/timeout -> 'down'. Fixes the bug where 401/404
  services were marked green.
- services.json: add Homepage service under 'dev' (was missing; the
  homepage is one of the public homelab services). Now 21 services.
- services.json: fix ethereum category icon (' Ξ' -> 'Ξ') leading space.
- .gitignore (new): ignore /.playwright-mcp/ and services-page.png.
- git rm --cached the three stray dev artifacts so they leave the repo.
This commit is contained in:
2026-07-28 19:06:57 +00:00
parent 3f6a6ac8d2
commit 19bc3cf991
6 changed files with 16 additions and 231 deletions
+6
View File
@@ -0,0 +1,6 @@
# Playwright MCP scratch files (local browser sessions)
/.playwright-mcp/
# Local screenshots from manual testing
services-page.png
*.screenshot.png
@@ -1,6 +0,0 @@
[ 94ms] [ERROR] Failed to load resource: the server responded with a status of 404 (File not found) @ http://127.0.0.1:8099/favicon.ico:0
[ 218ms] [ERROR] Failed to load resource: the server responded with a status of 401 () @ https://comfyui.ffaerber.duckdns.org/?_probe=1785265042484:0
[ 273ms] [ERROR] Failed to load resource: the server responded with a status of 401 () @ https://grafana.ffaerber.duckdns.org/?_probe=1785265042484:0
[ 273ms] [ERROR] Failed to load resource: the server responded with a status of 401 () @ https://prometheus.ffaerber.duckdns.org/?_probe=1785265042484:0
[ 369ms] [ERROR] Failed to load resource: the server responded with a status of 401 () @ https://staking.ffaerber.duckdns.org/?_probe=1785265042484:0
[ 372ms] [ERROR] Failed to load resource: the server responded with a status of 404 () @ https://gnosis.ffaerber.duckdns.org/?_probe=1785265042484:0
@@ -1,217 +0,0 @@
- generic [active] [ref=f1e1]:
- banner [ref=f1e2]:
- generic [ref=f1e3]:
- heading "ffaerber" [level=1] [ref=f1e4]
- paragraph [ref=f1e5]: Self-hosted homelab — every service running in the house, one click away.
- main [ref=f1e6]:
- generic [ref=f1e7]:
- generic [ref=f1e8]:
- heading "Services" [level=2] [ref=f1e9]
- paragraph [ref=f1e10]: All public-facing services from the homelab, grouped by what they do. A green dot means the service answered just now; grey means it is down or unreachable from your browser.
- generic [ref=f1e11]:
- generic [ref=f1e12]:
- heading "Edge & proxy" [level=3] [ref=f1e13]: 🌐Edge & proxy
- list [ref=f1e14]:
- listitem [ref=f1e15]:
- link "Traefik Reverse proxy and edge router for the whole homelab. status unknown" [ref=f1e16] [cursor=pointer]:
- /url: https://ffaerber.duckdns.org/dashboard/
- generic [ref=f1e17]: 🛡️
- generic [ref=f1e18]:
- generic [ref=f1e19]: Traefik
- generic [ref=f1e20]: Reverse proxy and edge router for the whole homelab.
- img "status unknown" [ref=f1e21]
- generic [ref=f1e22]:
- heading "Observability" [level=3] [ref=f1e23]: 📊Observability
- list [ref=f1e24]:
- listitem [ref=f1e25]:
- link "Grafana Dashboards for metrics, logs and system health. status unknown" [ref=f1e26] [cursor=pointer]:
- /url: https://grafana.ffaerber.duckdns.org
- generic [ref=f1e27]: 📈
- generic [ref=f1e28]:
- generic [ref=f1e29]: Grafana
- generic [ref=f1e30]: Dashboards for metrics, logs and system health.
- img "status unknown" [ref=f1e31]
- listitem [ref=f1e32]:
- link "Prometheus Metrics collection and alerting. status unknown" [ref=f1e33] [cursor=pointer]:
- /url: https://prometheus.ffaerber.duckdns.org
- generic [ref=f1e34]: 🔥
- generic [ref=f1e35]:
- generic [ref=f1e36]: Prometheus
- generic [ref=f1e37]: Metrics collection and alerting.
- img "status unknown" [ref=f1e38]
- generic [ref=f1e39]:
- heading "Media" [level=3] [ref=f1e40]: 🎬Media
- list [ref=f1e41]:
- listitem [ref=f1e42]:
- link "Jellyfin Self-hosted movies, shows and music. status unknown" [ref=f1e43] [cursor=pointer]:
- /url: https://jellyfin.ffaerber.duckdns.org
- generic [ref=f1e44]: 🎞️
- generic [ref=f1e45]:
- generic [ref=f1e46]: Jellyfin
- generic [ref=f1e47]: Self-hosted movies, shows and music.
- img "status unknown" [ref=f1e48]
- listitem [ref=f1e49]:
- link "Immich Self-hosted photo and video library. status unknown" [ref=f1e50] [cursor=pointer]:
- /url: https://immich.ffaerber.duckdns.org
- generic [ref=f1e51]: 📸
- generic [ref=f1e52]:
- generic [ref=f1e53]: Immich
- generic [ref=f1e54]: Self-hosted photo and video library.
- img "status unknown" [ref=f1e55]
- listitem [ref=f1e56]:
- link "ComfyUI Node-based image generation UI on the GPU. status unknown" [ref=f1e57] [cursor=pointer]:
- /url: https://comfyui.ffaerber.duckdns.org
- generic [ref=f1e58]: 🎨
- generic [ref=f1e59]:
- generic [ref=f1e60]: ComfyUI
- generic [ref=f1e61]: Node-based image generation UI on the GPU.
- img "status unknown" [ref=f1e62]
- generic [ref=f1e63]:
- heading "Downloads & files" [level=3] [ref=f1e64]: 💾Downloads & files
- list [ref=f1e65]:
- listitem [ref=f1e66]:
- link "qBittorrent Torrent downloads over VPN. status unknown" [ref=f1e67] [cursor=pointer]:
- /url: https://bittorrent.ffaerber.duckdns.org
- generic [ref=f1e68]: 🧲
- generic [ref=f1e69]:
- generic [ref=f1e70]: qBittorrent
- generic [ref=f1e71]: Torrent downloads over VPN.
- img "status unknown" [ref=f1e72]
- listitem [ref=f1e73]:
- link "Filebrowser Web file browser for the homelab storage. status unknown" [ref=f1e74] [cursor=pointer]:
- /url: https://files.ffaerber.duckdns.org
- generic [ref=f1e75]: 📁
- generic [ref=f1e76]:
- generic [ref=f1e77]: Filebrowser
- generic [ref=f1e78]: Web file browser for the homelab storage.
- img "status unknown" [ref=f1e79]
- generic [ref=f1e80]:
- heading "Ethereum" [level=3] [ref=f1e81]: ΞEthereum
- list [ref=f1e82]:
- listitem [ref=f1e83]:
- link "Mainnet Geth Ethereum mainnet execution client (RPC). status unknown" [ref=f1e84] [cursor=pointer]:
- /url: https://mainnet.ffaerber.duckdns.org
- generic [ref=f1e85]: ⛓️
- generic [ref=f1e86]:
- generic [ref=f1e87]: Mainnet Geth
- generic [ref=f1e88]: Ethereum mainnet execution client (RPC).
- img "status unknown" [ref=f1e89]
- listitem [ref=f1e90]:
- link "Mainnet Beacon Lighthouse consensus / beacon node. status unknown" [ref=f1e91] [cursor=pointer]:
- /url: https://beacon.ffaerber.duckdns.org
- generic [ref=f1e92]: 🗼
- generic [ref=f1e93]:
- generic [ref=f1e94]: Mainnet Beacon
- generic [ref=f1e95]: Lighthouse consensus / beacon node.
- img "status unknown" [ref=f1e96]
- listitem [ref=f1e97]:
- link "Staking Tracker Daily validator rewards and tax reports. status unknown" [ref=f1e98] [cursor=pointer]:
- /url: https://staking.ffaerber.duckdns.org
- generic [ref=f1e99]: 💰
- generic [ref=f1e100]:
- generic [ref=f1e101]: Staking Tracker
- generic [ref=f1e102]: Daily validator rewards and tax reports.
- img "status unknown" [ref=f1e103]
- listitem [ref=f1e104]:
- link "Gnosis Nethermind Gnosis Chain execution client (RPC). status unknown" [ref=f1e105] [cursor=pointer]:
- /url: https://gnosis.ffaerber.duckdns.org
- generic [ref=f1e106]: ⛓️
- generic [ref=f1e107]:
- generic [ref=f1e108]: Gnosis Nethermind
- generic [ref=f1e109]: Gnosis Chain execution client (RPC).
- img "status unknown" [ref=f1e110]
- listitem [ref=f1e111]:
- link "Bee Ethereum Swarm node — decentralized storage API. status unknown" [ref=f1e112] [cursor=pointer]:
- /url: https://bee.ffaerber.duckdns.org
- generic [ref=f1e113]: 🐝
- generic [ref=f1e114]:
- generic [ref=f1e115]: Bee
- generic [ref=f1e116]: Ethereum Swarm node — decentralized storage API.
- img "status unknown" [ref=f1e117]
- listitem [ref=f1e118]:
- link "Bee Dashboard Web UI for the Swarm Bee node. status unknown" [ref=f1e119] [cursor=pointer]:
- /url: https://bee-dashboard.ffaerber.duckdns.org
- generic [ref=f1e120]: 🎛️
- generic [ref=f1e121]:
- generic [ref=f1e122]: Bee Dashboard
- generic [ref=f1e123]: Web UI for the Swarm Bee node.
- img "status unknown" [ref=f1e124]
- listitem [ref=f1e125]:
- link "t4t Provider Truth-for-Truth data provider node. status unknown" [ref=f1e126] [cursor=pointer]:
- /url: https://t4t-provider.ffaerber.duckdns.org
- generic [ref=f1e127]: 🔍
- generic [ref=f1e128]:
- generic [ref=f1e129]: t4t Provider
- generic [ref=f1e130]: Truth-for-Truth data provider node.
- img "status unknown" [ref=f1e131]
- generic [ref=f1e132]:
- heading "AI & speech" [level=3] [ref=f1e133]: 🤖AI & speech
- list [ref=f1e134]:
- listitem [ref=f1e135]:
- link "Whisper Self-hosted speech-to-text on the GPU. status unknown" [ref=f1e136] [cursor=pointer]:
- /url: https://whisper.ffaerber.duckdns.org
- generic [ref=f1e137]: 👂
- generic [ref=f1e138]:
- generic [ref=f1e139]: Whisper
- generic [ref=f1e140]: Self-hosted speech-to-text on the GPU.
- img "status unknown" [ref=f1e141]
- listitem [ref=f1e142]:
- link "Kokoro Self-hosted text-to-speech, 49 voices. status unknown" [ref=f1e143] [cursor=pointer]:
- /url: https://kokoro.ffaerber.duckdns.org
- generic [ref=f1e144]: 🗣️
- generic [ref=f1e145]:
- generic [ref=f1e146]: Kokoro
- generic [ref=f1e147]: Self-hosted text-to-speech, 49 voices.
- img "status unknown" [ref=f1e148]
- listitem [ref=f1e149]:
- link "Ollama Local LLM serving on the GPU. status unknown" [ref=f1e150] [cursor=pointer]:
- /url: https://ollama.ffaerber.duckdns.org
- generic [ref=f1e151]: 🦙
- generic [ref=f1e152]:
- generic [ref=f1e153]: Ollama
- generic [ref=f1e154]: Local LLM serving on the GPU.
- img "status unknown" [ref=f1e155]
- listitem [ref=f1e156]:
- link "Open WebUI Chat UI for Ollama models. status unknown" [ref=f1e157] [cursor=pointer]:
- /url: https://chat.ffaerber.duckdns.org
- generic [ref=f1e158]: 💬
- generic [ref=f1e159]:
- generic [ref=f1e160]: Open WebUI
- generic [ref=f1e161]: Chat UI for Ollama models.
- img "status unknown" [ref=f1e162]
- generic [ref=f1e163]:
- heading "Dev platform" [level=3] [ref=f1e164]: 🛠️Dev platform
- list [ref=f1e165]:
- listitem [ref=f1e166]:
- link "Gitea Self-hosted Git and CI for all ffaerber repos. status unknown" [ref=f1e167] [cursor=pointer]:
- /url: https://git.ffaerber.duckdns.org
- generic [ref=f1e168]: 🔧
- generic [ref=f1e169]:
- generic [ref=f1e170]: Gitea
- generic [ref=f1e171]: Self-hosted Git and CI for all ffaerber repos.
- img "status unknown" [ref=f1e172]
- generic [ref=f1e173]:
- generic [ref=f1e174]:
- heading "Projects" [level=2] [ref=f1e175]
- paragraph [ref=f1e176]: The code behind the services lives across a few GitHub orgs.
- list [ref=f1e177]:
- listitem [ref=f1e178]:
- link "github.com/ffaerber ↗" [ref=f1e179] [cursor=pointer]:
- /url: https://github.com/ffaerber
- text: — dapps, Ethereum infra, GPU speech services
- listitem [ref=f1e180]:
- link "github.com/cipherdolls ↗" [ref=f1e181] [cursor=pointer]:
- /url: https://github.com/cipherdolls
- text: — AI companion platform
- contentinfo [ref=f1e182]:
- generic [ref=f1e183]:
- navigation [ref=f1e184]:
- link "github.com/ffaerber ↗" [ref=f1e185] [cursor=pointer]:
- /url: https://github.com/ffaerber
- link "github.com/cipherdolls ↗" [ref=f1e186] [cursor=pointer]:
- /url: https://github.com/cipherdolls
- paragraph [ref=f1e187]:
- text: Served from a container built in
- link "ffaerber/homepage" [ref=f1e188] [cursor=pointer]:
- /url: https://git.ffaerber.duckdns.org/ffaerber/homepage
- text: .
+8 -7
View File
@@ -81,13 +81,14 @@
done = true;
setStatusDot(dot, state);
}
var img = new Image();
img.onload = function () { settle("up"); };
img.onerror = function () { settle("up"); };
img.onloadstart = function () {};
var timeout = setTimeout(function () { settle("down"); }, 6000);
img.src = url + "?_probe=" + Date.now();
window.addEventListener("beforeunload", function () { clearTimeout(timeout); });
var controller = window.AbortController ? new AbortController() : null;
var timeout = setTimeout(function () {
if (controller) controller.abort();
settle("down");
}, 6000);
fetch(url, { mode: "no-cors", cache: "no-store", redirect: "follow", signal: controller ? controller.signal : undefined })
.then(function () { clearTimeout(timeout); settle("up"); })
.catch(function () { clearTimeout(timeout); settle("down"); });
}
fetch(src)
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 212 KiB

+1
View File
@@ -66,6 +66,7 @@
"label": "Dev platform",
"icon": "🛠️",
"services": [
{ "name": "Homepage", "url": "https://homepage.ffaerber.duckdns.org", "desc": "This page — the homelab services index you are looking at.", "icon": "🏠" },
{ "name": "Gitea", "url": "https://git.ffaerber.duckdns.org", "desc": "Self-hosted Git and CI for all ffaerber repos.", "icon": "🔧" }
]
}