Files
OwnCord/Server/api/metrics_handler.go
T
jevb 9f381f54e9 feat: voice/video polish — refactor, AudioWorklet VAD, bug fixes, UX improvements
Research-driven voice/video polish pass based on Discord/TeamSpeak comparison.

Refactor:
- Split livekitSession.ts (1,509 lines) into 4 modules: audioPipeline.ts,
  audioElements.ts, deviceManager.ts + facade in livekitSession.ts
- Facade pattern preserves all existing exports (zero breaking changes)

AudioWorklet VAD:
- Migrated VAD from setTimeout polling to AudioWorklet (vad-worklet.js)
- Runs on audio thread, works when app is backgrounded
- Graceful fallback to setTimeout if AudioWorklet unavailable

Bug fixes:
- Token TTL extended from 4h to 24h (eliminates fragile long sessions)
- Ghost voice state: retry with exponential backoff (3 attempts, 100-400ms)
- Client token refresh adjusted to 23h (1h before expiry)

UX improvements:
- Speaker indicator: pulsing green glow animation (speak-pulse keyframes)
- Permission recovery: "Grant Microphone" button in VoiceWidget for
  listen-only mode with listenOnly state in voiceStore
- Device hot-swap: devicechange listener with 500ms debounce, auto-fallback
  to default device, toast notification
- Camera/screenshare stop: toast feedback on disable
- Connection quality: auto-expand stats pane on poor/bad quality (3s debounce)
- Bandwidth display: human-readable Mbps in stats pane (formatBitrate)

Observability:
- Voice session metrics: voice_sessions counter on /api/v1/metrics endpoint

Tests:
- 55 new unit tests for audioPipeline + audioElements modules
- 22 new Go tests for HTTPS proxy (WebSocket upgrade, origin validation,
  path blocking)
- 11 new voice E2E tests (lifecycle, widget, speaker indicators)
- Pre-refactor snapshot tests for livekitSession public API

Docs:
- DESIGN.md: full design system documentation (tokens, typography, colors,
  spacing, motion, voice-specific tokens)
- VOICE-COMPARISON-MATRIX.md: 25-behavior comparison across Discord,
  TeamSpeak, Guilded
- voice-video-polish.md: CEO plan with scope decisions
2026-03-29 00:51:54 +01:00

50 lines
1.6 KiB
Go

package api
import (
"net/http"
"runtime"
"time"
)
// ServerMetrics holds runtime metrics for the /api/v1/metrics endpoint.
type ServerMetrics struct {
Uptime string `json:"uptime"`
UptimeSeconds float64 `json:"uptime_seconds"`
GoRoutines int `json:"goroutines"`
HeapAllocMB float64 `json:"heap_alloc_mb"`
HeapSysMB float64 `json:"heap_sys_mb"`
NumGC uint32 `json:"num_gc"`
ConnectedUsers int `json:"connected_users"`
VoiceSessions int `json:"voice_sessions"`
LiveKitHealthy *bool `json:"livekit_healthy,omitempty"`
}
// handleMetrics returns an HTTP handler that reports runtime server metrics.
// getConnectedUsers is a callback to retrieve the current WebSocket client count.
// livekitHealthCheck is optional — if non-nil, it probes the LiveKit companion process.
func handleMetrics(getConnectedUsers func() int, getVoiceSessions func() int, livekitHealthCheck func() (bool, error)) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var m runtime.MemStats
runtime.ReadMemStats(&m)
uptime := time.Since(serverStartTime)
metrics := ServerMetrics{
Uptime: uptime.Truncate(time.Second).String(),
UptimeSeconds: uptime.Seconds(),
GoRoutines: runtime.NumGoroutine(),
HeapAllocMB: float64(m.HeapAlloc) / 1024 / 1024,
HeapSysMB: float64(m.HeapSys) / 1024 / 1024,
NumGC: m.NumGC,
ConnectedUsers: getConnectedUsers(),
VoiceSessions: getVoiceSessions(),
}
if livekitHealthCheck != nil {
healthy, _ := livekitHealthCheck()
metrics.LiveKitHealthy = &healthy
}
writeJSON(w, http.StatusOK, metrics)
}
}