fix: loading screen via Go handler instead of file:// HTML
- bee-web.service: remove After=bee-audit so Go starts immediately - Go serves loading page from / when audit JSON not yet present; JS polls /api/ready (503 until file exists, 200 when ready) then redirects to dashboard - bee-openbox-session: wait for /healthz (Go binds fast <2s), open http://localhost/ directly — no file:// cross-origin issues - Remove loading.html static file Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -163,6 +163,7 @@ func NewHandler(opts HandlerOptions) http.Handler {
|
|||||||
|
|
||||||
// ── Infrastructure ──────────────────────────────────────────────────────
|
// ── Infrastructure ──────────────────────────────────────────────────────
|
||||||
mux.HandleFunc("GET /healthz", h.handleHealthz)
|
mux.HandleFunc("GET /healthz", h.handleHealthz)
|
||||||
|
mux.HandleFunc("GET /api/ready", h.handleReady)
|
||||||
|
|
||||||
// ── Existing read-only endpoints (preserved for compatibility) ──────────
|
// ── Existing read-only endpoints (preserved for compatibility) ──────────
|
||||||
mux.HandleFunc("GET /audit.json", h.handleAuditJSON)
|
mux.HandleFunc("GET /audit.json", h.handleAuditJSON)
|
||||||
@@ -654,9 +655,66 @@ func sparseLabels(labels []string, n int) []string {
|
|||||||
|
|
||||||
// ── Page handler ─────────────────────────────────────────────────────────────
|
// ── Page handler ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
func (h *handler) handleReady(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Cache-Control", "no-store")
|
||||||
|
if _, err := os.Stat(h.opts.AuditPath); err != nil {
|
||||||
|
w.WriteHeader(http.StatusServiceUnavailable)
|
||||||
|
_, _ = w.Write([]byte("starting"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
_, _ = w.Write([]byte("ready"))
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadingPageHTML = `<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>EASY-BEE</title>
|
||||||
|
<style>
|
||||||
|
*{margin:0;padding:0;box-sizing:border-box}
|
||||||
|
html,body{height:100%;background:#0f1117;display:flex;align-items:center;justify-content:center;font-family:'Courier New',monospace;color:#e2e8f0}
|
||||||
|
.logo{font-size:13px;line-height:1.4;color:#f6c90e;margin-bottom:48px;white-space:pre}
|
||||||
|
.spinner{width:48px;height:48px;border:4px solid #2d3748;border-top-color:#f6c90e;border-radius:50%;animation:spin .8s linear infinite;margin:0 auto 24px}
|
||||||
|
@keyframes spin{to{transform:rotate(360deg)}}
|
||||||
|
.status{font-size:14px;color:#a0aec0;letter-spacing:.05em}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div style="text-align:center">
|
||||||
|
<div class="logo"> ███████╗ █████╗ ███████╗██╗ ██╗ ██████╗ ███████╗███████╗
|
||||||
|
██╔════╝██╔══██╗██╔════╝╚██╗ ██╔╝ ██╔══██╗██╔════╝██╔════╝
|
||||||
|
█████╗ ███████║███████╗ ╚████╔╝ █████╗██████╔╝█████╗ █████╗
|
||||||
|
██╔══╝ ██╔══██║╚════██║ ╚██╔╝ ╚════╝██╔══██╗██╔══╝ ██╔══╝
|
||||||
|
███████╗██║ ██║███████║ ██║ ██████╔╝███████╗███████╗
|
||||||
|
╚══════╝╚═╝ ╚═╝╚══════╝ ╚═╝ ╚═════╝ ╚══════╝╚══════╝</div>
|
||||||
|
<div class="spinner"></div>
|
||||||
|
<div class="status" id="s">Starting up...</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
function probe(){
|
||||||
|
fetch('/api/ready',{cache:'no-store'})
|
||||||
|
.then(function(r){
|
||||||
|
if(r.ok){window.location.replace('/');}
|
||||||
|
else{setTimeout(probe,1000);}
|
||||||
|
})
|
||||||
|
.catch(function(){setTimeout(probe,1000);});
|
||||||
|
}
|
||||||
|
probe();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>`
|
||||||
|
|
||||||
func (h *handler) handlePage(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) handlePage(w http.ResponseWriter, r *http.Request) {
|
||||||
page := strings.TrimPrefix(r.URL.Path, "/")
|
page := strings.TrimPrefix(r.URL.Path, "/")
|
||||||
if page == "" {
|
if page == "" {
|
||||||
|
// Serve loading page until audit snapshot exists
|
||||||
|
if _, err := os.Stat(h.opts.AuditPath); err != nil {
|
||||||
|
w.Header().Set("Cache-Control", "no-store")
|
||||||
|
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
|
_, _ = w.Write([]byte(loadingPageHTML))
|
||||||
|
return
|
||||||
|
}
|
||||||
page = "dashboard"
|
page = "dashboard"
|
||||||
}
|
}
|
||||||
// Redirect old routes to new names
|
// Redirect old routes to new names
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=Bee: hardware audit web viewer
|
Description=Bee: hardware audit web viewer
|
||||||
After=bee-network.service bee-audit.service
|
After=bee-network.service
|
||||||
Wants=bee-audit.service
|
Wants=bee-audit.service
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
|
|||||||
@@ -8,6 +8,13 @@ xset -dpms
|
|||||||
xset s noblank
|
xset s noblank
|
||||||
|
|
||||||
tint2 &
|
tint2 &
|
||||||
|
# Wait for bee-web to bind (Go starts fast, usually <2s)
|
||||||
|
i=0
|
||||||
|
while [ $i -lt 30 ]; do
|
||||||
|
if curl -sf http://localhost/healthz >/dev/null 2>&1; then break; fi
|
||||||
|
sleep 1
|
||||||
|
i=$((i+1))
|
||||||
|
done
|
||||||
chromium \
|
chromium \
|
||||||
--disable-infobars \
|
--disable-infobars \
|
||||||
--disable-translate \
|
--disable-translate \
|
||||||
@@ -15,6 +22,6 @@ chromium \
|
|||||||
--disable-session-crashed-bubble \
|
--disable-session-crashed-bubble \
|
||||||
--disable-features=TranslateUI \
|
--disable-features=TranslateUI \
|
||||||
--start-fullscreen \
|
--start-fullscreen \
|
||||||
file:///usr/local/share/bee/loading.html &
|
http://localhost/ &
|
||||||
|
|
||||||
exec openbox
|
exec openbox
|
||||||
|
|||||||
@@ -1,82 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>EASY-BEE</title>
|
|
||||||
<style>
|
|
||||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
||||||
html, body {
|
|
||||||
height: 100%;
|
|
||||||
background: #0f1117;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
font-family: 'Courier New', monospace;
|
|
||||||
color: #e2e8f0;
|
|
||||||
}
|
|
||||||
.container {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
.logo {
|
|
||||||
font-size: 13px;
|
|
||||||
line-height: 1.4;
|
|
||||||
color: #f6c90e;
|
|
||||||
margin-bottom: 48px;
|
|
||||||
white-space: pre;
|
|
||||||
}
|
|
||||||
.spinner {
|
|
||||||
width: 48px;
|
|
||||||
height: 48px;
|
|
||||||
border: 4px solid #2d3748;
|
|
||||||
border-top-color: #f6c90e;
|
|
||||||
border-radius: 50%;
|
|
||||||
animation: spin 0.8s linear infinite;
|
|
||||||
margin: 0 auto 24px;
|
|
||||||
}
|
|
||||||
@keyframes spin { to { transform: rotate(360deg); } }
|
|
||||||
.status {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #a0aec0;
|
|
||||||
letter-spacing: 0.05em;
|
|
||||||
}
|
|
||||||
.dots::after {
|
|
||||||
content: '';
|
|
||||||
animation: dots 1.5s steps(4, end) infinite;
|
|
||||||
}
|
|
||||||
@keyframes dots {
|
|
||||||
0% { content: ''; }
|
|
||||||
25% { content: '.'; }
|
|
||||||
50% { content: '..'; }
|
|
||||||
75% { content: '...'; }
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container">
|
|
||||||
<div class="logo"> ███████╗ █████╗ ███████╗██╗ ██╗ ██████╗ ███████╗███████╗
|
|
||||||
██╔════╝██╔══██╗██╔════╝╚██╗ ██╔╝ ██╔══██╗██╔════╝██╔════╝
|
|
||||||
█████╗ ███████║███████╗ ╚████╔╝ █████╗██████╔╝█████╗ █████╗
|
|
||||||
██╔══╝ ██╔══██║╚════██║ ╚██╔╝ ╚════╝██╔══██╗██╔══╝ ██╔══╝
|
|
||||||
███████╗██║ ██║███████║ ██║ ██████╔╝███████╗███████╗
|
|
||||||
╚══════╝╚═╝ ╚═╝╚══════╝ ╚═╝ ╚═════╝ ╚══════╝╚══════╝</div>
|
|
||||||
<div class="spinner"></div>
|
|
||||||
<div class="status">Starting<span class="dots"></span></div>
|
|
||||||
</div>
|
|
||||||
<script>
|
|
||||||
function probe() {
|
|
||||||
fetch('http://localhost/healthz', { cache: 'no-store' })
|
|
||||||
.then(function(r) {
|
|
||||||
if (r.ok) {
|
|
||||||
window.location.replace('http://localhost/');
|
|
||||||
} else {
|
|
||||||
setTimeout(probe, 500);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(function() {
|
|
||||||
setTimeout(probe, 500);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
probe();
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
Reference in New Issue
Block a user