diff --git a/client/.DS_Store b/client/.DS_Store index 74cf6a7..d1cf076 100644 Binary files a/client/.DS_Store and b/client/.DS_Store differ diff --git a/client/app.go b/client/app.go index 6128344..5aebbf4 100644 --- a/client/app.go +++ b/client/app.go @@ -140,6 +140,7 @@ type App struct { chatUnread int replyStaffID uint fetchAppsMu sync.Mutex + wsWriteMu sync.Mutex } type LocalStats struct { @@ -608,23 +609,32 @@ func (a *App) GetStats() map[string]any { } } +// safeWriteWS writes a message to the WebSocket connection thread-safely +func (a *App) safeWriteWS(msg any) error { + a.mu.Lock() + conn := a.wsConn + a.mu.Unlock() + + if conn == nil { + return errors.New("websocket connection is nil") + } + + a.wsWriteMu.Lock() + defer a.wsWriteMu.Unlock() + return conn.WriteJSON(msg) +} + // SendWebcamFrame truyền webcam frame từ JS frontend lên máy chủ qua WS func (a *App) SendWebcamFrame(frameBase64 string) { if !a.isMonitoringActive() { return } - a.mu.Lock() - conn := a.wsConn - a.mu.Unlock() - - if conn != nil { - _ = conn.WriteJSON(map[string]any{ - "event": "webcam_stream_frame", - "data": map[string]any{ - "imageBuffer": frameBase64, - }, - }) - } + _ = a.safeWriteWS(map[string]any{ + "event": "webcam_stream_frame", + "data": map[string]any{ + "imageBuffer": frameBase64, + }, + }) } // authStorageScanner chỉ quét localStorage khi người dùng chủ động mở trang đăng nhập @@ -1225,18 +1235,12 @@ func (a *App) startScreenshotStream() { continue } - a.mu.Lock() - conn := a.wsConn - a.mu.Unlock() - - if conn != nil { - _ = conn.WriteJSON(map[string]any{ - "event": "screenshot_stream_frame", - "data": map[string]any{ - "imageBuffer": frame, - }, - }) - } + _ = a.safeWriteWS(map[string]any{ + "event": "screenshot_stream_frame", + "data": map[string]any{ + "imageBuffer": frame, + }, + }) case <-a.streamScStop: return } @@ -1301,22 +1305,14 @@ func (a *App) startWebcamStream() { emptyCount = 0 sentCount++ - a.mu.Lock() - conn := a.wsConn - a.mu.Unlock() - - if conn != nil { - err := conn.WriteJSON(map[string]any{ - "event": "webcam_stream_frame", - "data": map[string]any{ - "imageBuffer": frame, - }, - }) - if sentCount <= 5 { - log.Printf("[WS] Webcam frame #%d sent (len=%d, err=%v)", sentCount, len(frame), err) - } - } else if sentCount <= 5 { - log.Printf("[WS] Webcam frame #%d: no WS connection", sentCount) + err := a.safeWriteWS(map[string]any{ + "event": "webcam_stream_frame", + "data": map[string]any{ + "imageBuffer": frame, + }, + }) + if sentCount <= 5 { + log.Printf("[WS] Webcam frame #%d sent (len=%d, err=%v)", sentCount, len(frame), err) } case <-a.streamCamStop: return diff --git a/client/build/.DS_Store b/client/build/.DS_Store index ce26469..811af54 100644 Binary files a/client/build/.DS_Store and b/client/build/.DS_Store differ