Ana içeriğe geç

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/DockerfileARG NEXT_PUBLIC_WS_URL + ENV NEXT_PUBLIC_WS_URL silindi.
  • docker-compose.yml — frontend build.args + environment blokları 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)

İlgili Dokümanlar