Frontend Application Error — Tüm Sayfalarda
Production'da https://enerji.kepmark.com tüm sayfalarda "Application error: a client-side exception has occurred (see the browser console for more information)" mesajı görünüyorsa bu rehberi takip edin. PR #276 ile kök sebep çözüldü; aynı semptomu görüyorsanız regression checklist'i geçin.
Semptom
- Browser'da tüm sayfalar (login, dashboard, devices, sld...) "Application error: a client-side exception has occurred" gösteriyor.
- Sayfa içeriği tamamen boş veya sadece bu hata mesajı var.
- Login bile yapılamıyor — hata Provider seviyesinde, route render'a varmıyor.
Hızlı Tanı
1. DevTools → Console
Uncaught (in promise) DOMException: Failed to construct 'WebSocket':
An insecure WebSocket connection may not be initiated from a page loaded over HTTPS.
veya:
SecurityError: Failed to construct 'WebSocket':
An insecure WebSocket connection may not be initiated from a page loaded over HTTPS.
2. DevTools → Network
/api/*istekleri hiç görünmüyor (sayfa Provider seviyesinde çöküyor, route'a varamıyor).- Sadece
_next/static/*chunk istekleri var.
3. Sunucu erişimi sağlam
# API endpoint'i çalışıyor mu? (sunucu sağlam, sorun frontend bundle'ında)
curl -i https://enerji.kepmark.com/api/health
# Beklenen: 200 OK
# WebSocket endpoint upgrade ediyor mu?
curl -i -H "Origin: https://enerji.kepmark.com" \
-H "Connection: Upgrade" -H "Upgrade: websocket" \
-H "Sec-WebSocket-Version: 13" \
-H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \
https://enerji.kepmark.com/ws/telemetry?token=test
# Beklenen: 101 Switching Protocols (auth başarısızsa 4001)
Kök Sebep
NEXT_PUBLIC_WS_URL=ws://95.217.179.56:8000/ws build-time bake edilmişti. HTTPS sayfada new WebSocket("ws://...") constructor'ı synchronously SecurityError fırlatır. WebSocketProvider Providers ağacının tepesinde (QueryClientProvider'ın altında) olduğu için throw → tüm React tree unmount → Next.js Error Boundary "Application error" gösterir.
Providers
└── QueryClientProvider
└── ThemeProvider
└── WebSocketProvider ← burada throw
└── AlarmListener ← unmount
└── {children} ← unmount (TÜM SAYFALAR)
Çözüm — PR #276
Üç katmanlı koruma:
1. getWsUrl() host-relative
frontend/src/config/site.ts:42-76 — HTTPS sayfada otomatik wss://${host}/ws üretir; env override sadece protokol uyumluysa kabul. Detay: Frontend Kritik Notlar.
2. WebSocketClient defansif
frontend/src/lib/websocket/client.ts:59-81 — connect öncesi mixed-content guard + new WebSocket() try/catch. Hata durumunda setState("error") early return; React tree throw etmez.
3. WebSocketErrorBoundary + Provider try/catch
frontend/src/components/providers.tsx + frontend/src/lib/websocket/WebSocketErrorBoundary.tsx — provider'a senkron exception ulaşırsa boundary fallback (<>{children}</>) ile sayfa açılmaya devam eder.
4. Bake elimination
frontend/Dockerfile—ARG NEXT_PUBLIC_WS_URL+ENV NEXT_PUBLIC_WS_URLsilindi.docker-compose.yml— frontendbuild.args+environmentblokları temizlendi..github/workflows/{ci,deploy,release}.yml— build-arg + env injection kaldırıldı.
Regression Checklist — Bu hatayı tekrar görüyorsan
1. Browser cache temizle
Eski bundle cache'lenmiş olabilir. Hard refresh:
Chrome/Edge: Ctrl + Shift + R (Mac: Cmd + Shift + R)
Firefox: Ctrl + F5 (Mac: Cmd + Shift + R)
Safari: Cmd + Option + R
DevTools → Network → "Disable cache" işaretle ve sayfayı yenile.
2. Build çıktısında ws:// literal'i ara
# Frontend container'da
docker exec zeus-frontend sh -c "grep -r 'ws://95.217' .next/static/ 2>/dev/null | head -20"
# Beklenen: HİÇBİR sonuç (eğer varsa eski bundle cache'lenmiş)
# Daha geniş arama (literal IP veya http://...:8000 baked olmuş mu?)
docker exec zeus-frontend sh -c "grep -roE 'ws://[^\"\\\"]+' .next/static/ | head -10"
Sonuç varsa: imaj eski sürümden geliyor, fresh build gerekli.
docker compose build --no-cache frontend
docker compose up -d frontend
3. getWsUrl() mantığı host-relative mi?
# Source kontrol — eski env-first mantığı kaldı mı?
grep -n "getWsUrl" frontend/src/config/site.ts
# Beklenen: site.ts:42 + host-relative fallback (production)
Detay: frontend/src/config/site.ts:42-76 (commit hash'ten doğrula).
4. WebSocketClient mixed-content guard var mı?
grep -n "Mixed-content" frontend/src/lib/websocket/client.ts
# Beklenen: client.ts:57-58 yorum + 59-70 guard satırları
5. Reverse proxy /ws upgrade headers doğru mu?
nginx/conf.d/*.conf içinde:
location /ws {
proxy_pass http://backend:8000/ws;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
Eksik Connection: upgrade veya Upgrade: websocket header'ı handshake'i engeller; browser native WebSocket API'si error event fırlatır (uygulama çökertmez).
6. CORS_ORIGINS production'da set mi?
Backend Origin guard aktif (PR #276):
docker exec zeus-backend sh -c 'echo $CORS_ORIGINS'
# Beklenen: https://enerji.kepmark.com (en azından)
CORS_ORIGINS boşsa Origin guard skip olur (dev fallback) — production'da bu hatalı yapılandırma. Detay: WebSocket Origin Guard.
Smoke Test (deploy sonrası)
# 1. Sayfa açılıyor mu? (HTTP 200)
curl -fsS -o /dev/null -w "%{http_code}\n" https://enerji.kepmark.com/login
# Beklenen: 200
# 2. Bundle içinde ws:// literal yok mu?
curl -s https://enerji.kepmark.com/login | grep -oE 'ws://[a-zA-Z0-9.:/_-]+' | head -5
# Beklenen: HİÇBİR sonuç
# 3. WebSocket upgrade çalışıyor mu? (auth başarısız → 4001 close)
wscat -H "Origin: https://enerji.kepmark.com" \
-c "wss://enerji.kepmark.com/ws/telemetry?token=invalid"
# Beklenen: connected → close 4001 (Unauthorized)