feat: add user-scoped WebSocket broadcasting for scan progress
- Add UserID field to Job struct for tracking job ownership - Broadcast scan progress updates to user's WebSocket connections - Send real-time updates during scanning (progress, files scanned, new items, errors) This allows the frontend to display live scan progress without HTTP polling. Scanner now associates scan jobs with requesting user for targeted updates.
This commit is contained in:
@@ -86,8 +86,9 @@ func (h *Handler) ScanLibrary(c echo.Context) error {
|
|||||||
jobID := uuid.New().String()
|
jobID := uuid.New().String()
|
||||||
|
|
||||||
job := &services.Job{
|
job := &services.Job{
|
||||||
ID: jobID,
|
ID: jobID,
|
||||||
Type: services.JobTypeScan,
|
Type: services.JobTypeScan,
|
||||||
|
UserID: userID,
|
||||||
Params: map[string]interface{}{
|
Params: map[string]interface{}{
|
||||||
"library_id": req.LibraryID,
|
"library_id": req.LibraryID,
|
||||||
"folders": folderPaths,
|
"folders": folderPaths,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package services
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bookhoard/internal/database"
|
"bookhoard/internal/database"
|
||||||
|
wsync "bookhoard/internal/sync"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -53,6 +54,7 @@ func (w *Worker) Enqueue(job *Job) {
|
|||||||
type Job struct {
|
type Job struct {
|
||||||
ID string
|
ID string
|
||||||
Type JobType
|
Type JobType
|
||||||
|
UserID string
|
||||||
Params map[string]interface{}
|
Params map[string]interface{}
|
||||||
Status JobStatus
|
Status JobStatus
|
||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
@@ -84,6 +86,7 @@ type JobResult struct {
|
|||||||
type Worker struct {
|
type Worker struct {
|
||||||
jobQueue chan *Job
|
jobQueue chan *Job
|
||||||
results map[string]*JobResult
|
results map[string]*JobResult
|
||||||
|
connManager *wsync.ConnectionManager
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
@@ -91,7 +94,7 @@ type Worker struct {
|
|||||||
shuttingDown atomic.Bool
|
shuttingDown atomic.Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewWorker(numWorkers int) *Worker {
|
func NewWorker(numWorkers int, connManager *wsync.ConnectionManager) *Worker {
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
w := &Worker{
|
w := &Worker{
|
||||||
@@ -106,6 +109,8 @@ func NewWorker(numWorkers int) *Worker {
|
|||||||
go w.worker()
|
go w.worker()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
w.connManager = connManager
|
||||||
|
|
||||||
return w
|
return w
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -249,6 +254,21 @@ func (w *Worker) processScanJob(job *Job) (interface{}, error) {
|
|||||||
result.NewItems = newItems
|
result.NewItems = newItems
|
||||||
result.Errors = errors
|
result.Errors = errors
|
||||||
}
|
}
|
||||||
|
// Broadcast via WebSocket to user
|
||||||
|
if w.connManager != nil && job.UserID != "" {
|
||||||
|
msg := wsync.BroadcastMessage{
|
||||||
|
Type: wsync.MessageTypeScanProgress,
|
||||||
|
Data: map[string]interface{}{
|
||||||
|
"job_id": job.ID,
|
||||||
|
"progress": progress,
|
||||||
|
"files_scanned": filesScanned,
|
||||||
|
"new_items": newItems,
|
||||||
|
"errors": errors,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
w.connManager.BroadcastToUser(job.UserID, msg)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := scanner.SetFolders(folders); err != nil {
|
if err := scanner.SetFolders(folders); err != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user