diff --git a/backend/cmd/server/main.go b/backend/cmd/server/main.go index 974155d..822d3ac 100644 --- a/backend/cmd/server/main.go +++ b/backend/cmd/server/main.go @@ -27,9 +27,12 @@ func main() { queries := db.New(database) fileSvc := service.NewFileService(queries, cfg) - ocrSvc := service.NewOCRService(cfg) + ocrSvc := service.NewOCRService(cfg, fileSvc) urlSvc := service.NewURLService(cfg.HMACSecret, cfg.ServerHost) + ocrSvc.Start() + defer ocrSvc.Stop() + h := handler.New(fileSvc, ocrSvc, urlSvc) r := gin.Default() diff --git a/backend/internal/handler/files.go b/backend/internal/handler/files.go index 5a56046..6ecd203 100644 --- a/backend/internal/handler/files.go +++ b/backend/internal/handler/files.go @@ -13,6 +13,7 @@ import ( type FileHandler struct { files *service.FileService urls *service.URLService + ocr *service.OCRService } func (h *FileHandler) Upload(c *gin.Context) { @@ -35,6 +36,9 @@ func (h *FileHandler) Upload(c *gin.Context) { api.Error(c, http.StatusInternalServerError, "UPLOAD_ERROR", err.Error()) return } + + h.ocr.Enqueue(result.ID, result.Path) + results = append(results, gin.H{ "id": result.ID, "name": result.Name, diff --git a/backend/internal/handler/handler.go b/backend/internal/handler/handler.go index b154863..6bb1f8f 100644 --- a/backend/internal/handler/handler.go +++ b/backend/internal/handler/handler.go @@ -5,14 +5,14 @@ import ( ) type Handler struct { - File *FileHandler - OCR *OCRHandler + File *FileHandler + OCR *OCRHandler Health *HealthHandler } func New(fileSvc *service.FileService, ocrSvc *service.OCRService, urlSvc *service.URLService) *Handler { return &Handler{ - File: &FileHandler{files: fileSvc, urls: urlSvc}, + File: &FileHandler{files: fileSvc, urls: urlSvc, ocr: ocrSvc}, OCR: &OCRHandler{ocr: ocrSvc, files: fileSvc}, Health: &HealthHandler{ocr: ocrSvc}, } diff --git a/backend/internal/model/file.go b/backend/internal/model/file.go index d258407..fcae85d 100644 --- a/backend/internal/model/file.go +++ b/backend/internal/model/file.go @@ -15,4 +15,5 @@ type File struct { type UploadResult struct { ID string `json:"id"` Name string `json:"name"` + Path string `json:"path"` } diff --git a/backend/internal/service/file.go b/backend/internal/service/file.go index 3ac66f2..0010733 100644 --- a/backend/internal/service/file.go +++ b/backend/internal/service/file.go @@ -62,6 +62,7 @@ func (s *FileService) Upload(file *multipart.FileHeader) (*model.UploadResult, e return &model.UploadResult{ ID: dbFile.ID, Name: dbFile.Name, + Path: dst, }, nil } diff --git a/backend/internal/service/ocr.go b/backend/internal/service/ocr.go index 33145e8..98ade16 100644 --- a/backend/internal/service/ocr.go +++ b/backend/internal/service/ocr.go @@ -1,7 +1,7 @@ package service import ( - "fmt" + "log" "os" "strings" @@ -9,22 +9,69 @@ import ( "github.com/vaultdrop/backend/internal/ocr" ) +type OCRJob struct { + FileID string + FilePath string +} + type OCRService struct { - client *ocr.Client + client *ocr.Client + fileSvc *FileService + jobs chan OCRJob } -func NewOCRService(cfg *config.Config) *OCRService { +func NewOCRService(cfg *config.Config, fileSvc *FileService) *OCRService { return &OCRService{ - client: ocr.NewClient(cfg.OCREndpoint), + client: ocr.NewClient(cfg.OCREndpoint), + fileSvc: fileSvc, + jobs: make(chan OCRJob, 100), } } -func (s *OCRService) RecognizeFromFile(filePath string) ([]ocr.TextBlock, error) { - data, err := os.ReadFile(filePath) - if err != nil { - return nil, fmt.Errorf("read file: %w", err) +func (s *OCRService) Start() { + go s.worker() + log.Println("[OCR] Worker started") +} + +func (s *OCRService) Stop() { + close(s.jobs) + log.Println("[OCR] Worker stopped") +} + +func (s *OCRService) Enqueue(fileID, filePath string) { + s.jobs <- OCRJob{FileID: fileID, FilePath: filePath} + log.Printf("[OCR] Enqueued file %s", fileID) +} + +func (s *OCRService) worker() { + for job := range s.jobs { + s.process(job) } - return s.client.Recognize(data) +} + +func (s *OCRService) process(job OCRJob) { + log.Printf("[OCR] Processing file %s", job.FileID) + + data, err := os.ReadFile(job.FilePath) + if err != nil { + log.Printf("[OCR] Failed to read file %s: %v", job.FileID, err) + return + } + + blocks, err := s.client.Recognize(data) + if err != nil { + log.Printf("[OCR] Failed to recognize file %s: %v", job.FileID, err) + return + } + + text := s.FlattenResults(blocks) + + if err := s.fileSvc.UpdateOCRText(job.FileID, text); err != nil { + log.Printf("[OCR] Failed to update ocr_text for file %s: %v", job.FileID, err) + return + } + + log.Printf("[OCR] Completed file %s (%d chars)", job.FileID, len(text)) } func (s *OCRService) RecognizeFromBytes(data []byte) ([]ocr.TextBlock, error) { @@ -42,3 +89,7 @@ func (s *OCRService) FlattenResults(blocks []ocr.TextBlock) string { func (s *OCRService) HealthCheck() error { return s.client.HealthCheck() } + +func (s *OCRService) QueueLength() int { + return len(s.jobs) +}