From a5c4ac60eab3b142afd2a15487934cedcd228f6b Mon Sep 17 00:00:00 2001 From: Tim Lappe Date: Thu, 24 Apr 2025 20:40:14 +0200 Subject: [PATCH] commit --- backend/go.mod | 16 +- backend/go.sum | 23 ++ backend/handlers/file_handlers.go | 57 +++++ backend/main.go | 3 + backend/models/document.go | 5 +- backend/services/file_converter.go | 37 +++ backend/services/sane.go | 2 +- backend/services/scanner_service.go | 10 +- docker-compose.server.yml | 32 +++ frontend/public/index.html | 367 +++++++++++++++++++++++++--- 10 files changed, 509 insertions(+), 43 deletions(-) create mode 100644 backend/go.sum create mode 100644 backend/handlers/file_handlers.go create mode 100644 backend/services/file_converter.go create mode 100644 docker-compose.server.yml diff --git a/backend/go.mod b/backend/go.mod index a9d74e6..298d630 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -1,3 +1,17 @@ module github.com/scanner/backend -go 1.20 \ No newline at end of file +go 1.22 + +toolchain go1.23.7 + +require ( + github.com/hhrutter/lzw v1.0.0 // indirect + github.com/hhrutter/tiff v1.0.1 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect + github.com/pdfcpu/pdfcpu v0.9.1 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/rivo/uniseg v0.4.7 // indirect + golang.org/x/image v0.21.0 // indirect + golang.org/x/text v0.19.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect +) diff --git a/backend/go.sum b/backend/go.sum new file mode 100644 index 0000000..65d9eb4 --- /dev/null +++ b/backend/go.sum @@ -0,0 +1,23 @@ +github.com/hhrutter/lzw v1.0.0 h1:laL89Llp86W3rRs83LvKbwYRx6INE8gDn0XNb1oXtm0= +github.com/hhrutter/lzw v1.0.0/go.mod h1:2HC6DJSn/n6iAZfgM3Pg+cP1KxeWc3ezG8bBqW5+WEo= +github.com/hhrutter/tiff v1.0.1 h1:MIus8caHU5U6823gx7C6jrfoEvfSTGtEFRiM8/LOzC0= +github.com/hhrutter/tiff v1.0.1/go.mod h1:zU/dNgDm0cMIa8y8YwcYBeuEEveI4B0owqHyiPpJPHc= +github.com/jung-kurt/gofpdf/v2 v2.0.0/go.mod h1:59m4YstrsXqnwOx8NOVIk6k0XqfrRPkiunj4eAevyaY= +github.com/jung-kurt/gofpdf/v2 v2.17.3 h1:otZXZby2gXJ7uU6pzprXHq/R57lsHLi0WtH79VabWxY= +github.com/jung-kurt/gofpdf/v2 v2.17.3/go.mod h1:Qx8ZNg4cNsO5i6uLDiBngnm+ii/FjtAqjRNO6drsoYU= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/pdfcpu/pdfcpu v0.9.1 h1:q8/KlBdHjkE7ZJU4ofhKG5Rjf7M6L324CVM6BMDySao= +github.com/pdfcpu/pdfcpu v0.9.1/go.mod h1:fVfOloBzs2+W2VJCCbq60XIxc3yJHAZ0Gahv1oO0gyI= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +golang.org/x/image v0.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s= +golang.org/x/image v0.21.0/go.mod h1:vUbsLavqK/W303ZroQQVKQ+Af3Yl6Uz1Ppu5J/cLz78= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/backend/handlers/file_handlers.go b/backend/handlers/file_handlers.go new file mode 100644 index 0000000..b84650f --- /dev/null +++ b/backend/handlers/file_handlers.go @@ -0,0 +1,57 @@ +package handlers + +import ( + "encoding/json" + "net/http" + + "github.com/scanner/backend/middleware" + "github.com/scanner/backend/models" + "github.com/scanner/backend/services" +) + +type FileHandler struct { + fileConverter *services.FileConverter +} + +func NewFileHandler(fileConverter *services.FileConverter) *FileHandler { + return &FileHandler{fileConverter: fileConverter} +} + +func (h *FileHandler) MergeFiles(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + return + } + + middleware.ApplyCors(w, r) + + var mergeRequest models.MergeRequest + err := json.NewDecoder(r.Body).Decode(&mergeRequest) + if err != nil { + http.Error(w, "Invalid request body", http.StatusBadRequest) + return + } + + if len(mergeRequest.Filenames) < 2 { + http.Error(w, "At least two files are required for merging", http.StatusBadRequest) + return + } + + files := []string{} + for _, filename := range mergeRequest.Filenames { + files = append(files, "/home/backend/var/documents/"+filename) + } + + mergedFilename, err := h.fileConverter.MergeFiles(files) + if err != nil { + http.Error(w, "Failed to merge files: "+err.Error(), http.StatusInternalServerError) + return + } + + response := map[string]string{ + "mergedFile": "/files/" + mergedFilename, + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(response) +} diff --git a/backend/main.go b/backend/main.go index da28454..abc24c7 100644 --- a/backend/main.go +++ b/backend/main.go @@ -12,9 +12,11 @@ import ( func main() { // Initialize services scannerService := services.NewScannerService() + fileConverter := services.NewFileConverter() // Initialize handlers scanHandler := handlers.NewScanHandler(scannerService) + fileHandler := handlers.NewFileHandler(fileConverter) // Set up routes with middleware mux := http.NewServeMux() @@ -24,6 +26,7 @@ func main() { mux.HandleFunc("/scan/abort", scanHandler.AbortScan) mux.HandleFunc("/scan/status", scanHandler.GetScanStatus) mux.HandleFunc("/documents", scanHandler.GetDocuments) + mux.HandleFunc("/documents/merge", fileHandler.MergeFiles) fileServer := http.FileServer(http.Dir("/home/backend/var/documents")) mux.Handle("/files/", http.StripPrefix("/files", fileServer)) diff --git a/backend/models/document.go b/backend/models/document.go index e8aaf14..a7b95fc 100644 --- a/backend/models/document.go +++ b/backend/models/document.go @@ -1,7 +1,10 @@ package models -// Document represents a scanned document type Document struct { Filename string `json:"filename"` URL string `json:"url"` } + +type MergeRequest struct { + Filenames []string `json:"filenames"` +} diff --git a/backend/services/file_converter.go b/backend/services/file_converter.go new file mode 100644 index 0000000..67a56fc --- /dev/null +++ b/backend/services/file_converter.go @@ -0,0 +1,37 @@ +package services + +import ( + "os" + "path/filepath" + "time" + + "github.com/pdfcpu/pdfcpu/pkg/api" +) + +type FileConverter struct { +} + +func NewFileConverter() *FileConverter { + return &FileConverter{} +} + +func (c *FileConverter) MergeFiles(filenames []string) (string, error) { + timestamp := time.Now().Format("20060102-150405") + mergedFilename := "merged-" + timestamp + ".pdf" + mergedFile := "/home/backend/var/documents/" + mergedFilename + + outputDir := filepath.Dir(mergedFile) + if err := os.MkdirAll(outputDir, 0755); err != nil { + return "", err + } + + _ = os.Remove(mergedFile) + + config := api.LoadConfiguration() + + if err := api.MergeCreateFile(filenames, mergedFile, false, config); err != nil { + return "", err + } + + return mergedFilename, nil +} diff --git a/backend/services/sane.go b/backend/services/sane.go index 5cd56a3..6e94864 100644 --- a/backend/services/sane.go +++ b/backend/services/sane.go @@ -42,7 +42,7 @@ func listDevices() ([]string, error) { } func startScan(outputFile string) (string, error) { - result, err := scanImage("-d", "escl:https://printer:443", "--format", "png", "--output-file", outputFile) + result, err := scanImage("-d", "escl:https://printer:443", "--format", "pdf", "--output-file", outputFile) if err != nil { return "", fmt.Errorf("error scanning: %v", err) } diff --git a/backend/services/scanner_service.go b/backend/services/scanner_service.go index 0cd5ece..6464452 100644 --- a/backend/services/scanner_service.go +++ b/backend/services/scanner_service.go @@ -9,12 +9,10 @@ import ( "github.com/scanner/backend/models" ) -// ScannerService handles scanner operations type ScannerService struct { state models.ScannerState } -// NewScannerService creates a new scanner service func NewScannerService() *ScannerService { return &ScannerService{ state: models.ScannerState{ @@ -23,7 +21,6 @@ func NewScannerService() *ScannerService { } } -// GetScanStatus returns the current scan status func (s *ScannerService) IsScanning() bool { s.state.Mu.Lock() state := s.state.IsScanning @@ -32,7 +29,6 @@ func (s *ScannerService) IsScanning() bool { return state } -// StartScan begins a new scanning process func (s *ScannerService) StartScan() (bool, error) { s.state.Mu.Lock() defer s.state.Mu.Unlock() @@ -47,7 +43,6 @@ func (s *ScannerService) StartScan() (bool, error) { return true, nil } -// AbortScan stops an in-progress scan func (s *ScannerService) AbortScan() (bool, error) { s.state.Mu.Lock() defer s.state.Mu.Unlock() @@ -60,7 +55,6 @@ func (s *ScannerService) AbortScan() (bool, error) { return true, nil } -// GetDocuments returns all scanned documents func (s *ScannerService) GetDocuments() []models.Document { const outputDir = "/home/backend/var/documents" @@ -76,7 +70,7 @@ func (s *ScannerService) GetDocuments() []models.Document { } filename := file.Name() - if !strings.HasPrefix(filename, "scan_") || !strings.HasSuffix(filename, ".jpg") { + if !strings.HasSuffix(filename, ".pdf") { continue } @@ -100,7 +94,7 @@ func (s *ScannerService) scan() { outputDir := "/home/backend/var/documents" timestamp := time.Now().Format("20060102150405") - outputFile := fmt.Sprintf("%s/scan_%s.jpg", outputDir, timestamp) + outputFile := fmt.Sprintf("%s/scan_%s.pdf", outputDir, timestamp) _, err := startScan(outputFile) diff --git a/docker-compose.server.yml b/docker-compose.server.yml new file mode 100644 index 0000000..25dce18 --- /dev/null +++ b/docker-compose.server.yml @@ -0,0 +1,32 @@ +services: + backend: + hostname: scanner-backend + build: + context: ./backend + dockerfile: Dockerfile + ports: + - "8089:80" + extra_hosts: + - "printer:192.168.178.36" + networks: + - printer_network + - proxy + cap_add: + - NET_ADMIN + + frontend: + hostname: scanner-frontend + build: + context: ./frontend + dockerfile: Dockerfile + ports: + - "8090:80" + networks: + - printer_network + - proxy + +networks: + printer_network: + driver: bridge + proxy: + external: true \ No newline at end of file diff --git a/frontend/public/index.html b/frontend/public/index.html index bd29bc0..69023fb 100644 --- a/frontend/public/index.html +++ b/frontend/public/index.html @@ -5,76 +5,251 @@ Document Scanner -
-

Document Scanner

- -
Scanner is idle
+
+
Scanner ist bereit
@@ -82,17 +257,33 @@

Documents

+
+ + +

No documents scanned yet.

+ + - \ No newline at end of file + \ No newline at end of file