package main import ( "encoding/json" "fmt" "io" "net/http" "net/url" "os" "path/filepath" "strings" "sync" "github.com/gorilla/websocket" ) type ProgressInfo struct { Percentage float64 CurrentFile string Paused bool } func handleRoot(w http.ResponseWriter, r *http.Request) { progressMutex.Lock() defer progressMutex.Unlock() jobsInfo := make(map[string]struct { Percentage float64 CurrentFile string Paused bool }) for filename, info := range progress { jobsInfo[filename] = struct { Percentage float64 CurrentFile string Paused bool }{ Percentage: info.Percentage, CurrentFile: info.CurrentFile, Paused: info.Paused, } } err := templates.ExecuteTemplate(w, "index", struct { Jobs map[string]struct { Percentage float64 CurrentFile string Paused bool } }{jobsInfo}) if err != nil { logger.LogError("Handle Root", fmt.Sprintf("Error executing template: %v", err)) http.Error(w, err.Error(), http.StatusInternalServerError) } } func handleUpload(w http.ResponseWriter, r *http.Request) { logger.LogInfo("Handle Upload", "Starting file upload") err := r.ParseMultipartForm(32 << 20) if err != nil { logger.LogError("Handle Upload", fmt.Sprintf("Error parsing multipart form: %v", err)) http.Error(w, err.Error(), http.StatusBadRequest) return } files := r.MultipartForm.File["files"] if len(files) == 0 { logger.LogError("Handle Upload", "No files uploaded") http.Error(w, "No files uploaded", http.StatusBadRequest) return } uploadedFiles := []string{} for _, fileHeader := range files { file, err := fileHeader.Open() if err != nil { logger.LogError("Handle Upload", fmt.Sprintf("Error opening file: %v", err)) http.Error(w, err.Error(), http.StatusInternalServerError) return } defer file.Close() tempFile, err := os.CreateTemp(uploadDir, fileHeader.Filename) if err != nil { logger.LogError("Handle Upload", fmt.Sprintf("Error creating temporary file: %v", err)) http.Error(w, err.Error(), http.StatusInternalServerError) return } defer tempFile.Close() _, err = io.Copy(tempFile, file) if err != nil { logger.LogError("Handle Upload", fmt.Sprintf("Error copying file: %v", err)) http.Error(w, err.Error(), http.StatusInternalServerError) return } uploadedFiles = append(uploadedFiles, filepath.Base(tempFile.Name())) _, err = parseInputFile(tempFile.Name()) if err != nil { logger.LogError("Handle Upload", fmt.Sprintf("Error parsing input file: %v", err)) http.Error(w, err.Error(), http.StatusInternalServerError) return } } validFiles := []string{} for _, file := range uploadedFiles { if file != "" { validFiles = append(validFiles, file) } } if len(validFiles) == 0 { logger.LogError("Handle Upload", "No valid files were uploaded") http.Error(w, "No valid files were uploaded", http.StatusBadRequest) return } logger.LogInfo("Handle Upload", fmt.Sprintf("Redirecting to select with files: %v", validFiles)) http.Redirect(w, r, "/select?files="+url.QueryEscape(strings.Join(validFiles, ",")), http.StatusSeeOther) } func handleSelect(w http.ResponseWriter, r *http.Request) { filesParam := r.URL.Query().Get("files") filenames := strings.Split(filesParam, ",") allItems := make(map[string]map[string][]Item) for _, filename := range filenames { if filename == "" { continue } fullPath := filepath.Join(uploadDir, filename) if _, err := os.Stat(fullPath); os.IsNotExist(err) { logger.LogError("Handle Select", fmt.Sprintf("File does not exist: %s", fullPath)) continue } items, err := parseInputFile(fullPath) if err != nil { logger.LogError("Handle Select", fmt.Sprintf("Error parsing input file: %v", err)) continue } sortItems(items) groupedItems := groupItemsBySeason(items) allItems[filename] = groupedItems } if len(allItems) == 0 { logger.LogError("Handle Select", "No valid files were processed") http.Error(w, "No valid files were processed", http.StatusBadRequest) return } err := templates.ExecuteTemplate(w, "select", struct { Filenames string AllItems map[string]map[string][]Item }{ Filenames: filesParam, AllItems: allItems, }) if err != nil { logger.LogError("Handle Select", fmt.Sprintf("Error executing template: %v", err)) http.Error(w, err.Error(), http.StatusInternalServerError) } } func handleProcess(w http.ResponseWriter, r *http.Request) { logger.LogInfo("Handle Process", "Starting process") if err := r.ParseForm(); err != nil { logger.LogError("Handle Process", fmt.Sprintf("Error parsing form: %v", err)) http.Error(w, err.Error(), http.StatusBadRequest) return } selectedItems := r.Form["items"] if len(selectedItems) == 0 { logger.LogError("Handle Process", "No items selected") http.Error(w, "No items selected", http.StatusBadRequest) return } itemsByFile := make(map[string][]string) for _, item := range selectedItems { parts := strings.SplitN(item, ":", 2) if len(parts) != 2 { logger.LogError("Handle Process", "Invalid item format") continue } filename, itemName := parts[0], parts[1] itemsByFile[filename] = append(itemsByFile[filename], itemName) } for filename, items := range itemsByFile { logger.LogInfo("Handle Process", fmt.Sprintf("Processing file: %s", filename)) fullPath := filepath.Join(uploadDir, filename) allItems, err := parseInputFile(fullPath) if err != nil { logger.LogError("Handle Process", fmt.Sprintf("Error parsing input file: %v", err)) http.Error(w, err.Error(), http.StatusInternalServerError) return } selectedItems := filterSelectedItems(allItems, items) sortItems(selectedItems) go processItems(filename, selectedItems) } http.Redirect(w, r, "/", http.StatusSeeOther) } func handleProgress(w http.ResponseWriter, r *http.Request) { filename := r.URL.Query().Get("filename") if r.Header.Get("Accept") == "application/json" { progressInfo := getProgress(filename) if progressInfo == nil { w.WriteHeader(http.StatusNotFound) json.NewEncoder(w).Encode(map[string]string{"error": "No progress information found"}) return } w.Header().Set("Content-Type", "application/json") err := json.NewEncoder(w).Encode(progressInfo) if err != nil { http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } return } err := templates.ExecuteTemplate(w, "progress", struct{ Filename string }{filename}) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } } func handlePause(w http.ResponseWriter, r *http.Request) { filename := r.URL.Query().Get("filename") if filename == "" { logger.LogError("Pause Handler", "Filename is required") http.Error(w, "Filename is required", http.StatusBadRequest) return } jobsMutex.Lock() jobInfo, exists := jobs[filename] jobsMutex.Unlock() if !exists { logger.LogError("Pause Handler", "Job not found") http.Error(w, "Job not found", http.StatusNotFound) return } jobInfo.Paused = true if jobInfo.Cmd != nil && jobInfo.Cmd.Process != nil { logger.LogJobState(filename, "pausing") jobInfo.Cmd.Process.Kill() } progressMutex.Lock() if progressInfo, ok := progress[filename]; ok { progressInfo.Paused = true } progressMutex.Unlock() fmt.Fprintf(w, "Pause signal sent for %s", filename) } func handleResume(w http.ResponseWriter, r *http.Request) { filename := r.URL.Query().Get("filename") if filename == "" { http.Error(w, "Filename is required", http.StatusBadRequest) return } jobsMutex.Lock() jobInfo, exists := jobs[filename] jobsMutex.Unlock() if !exists { http.Error(w, "Job not found", http.StatusNotFound) return } jobInfo.Paused = false jobInfo.ResumeChan <- struct{}{} progressMutex.Lock() if progressInfo, ok := progress[filename]; ok { progressInfo.Paused = false } progressMutex.Unlock() fmt.Fprintf(w, "Resume signal sent for %s", filename) } func handleAbort(w http.ResponseWriter, r *http.Request) { filename := r.URL.Query().Get("filename") if filename == "" { http.Error(w, "Filename is required", http.StatusBadRequest) return } jobsMutex.Lock() jobInfo, exists := jobs[filename] jobsMutex.Unlock() if !exists { http.Error(w, "Job not found", http.StatusNotFound) return } close(jobInfo.AbortChan) if jobInfo.Cmd != nil && jobInfo.Cmd.Process != nil { jobInfo.Cmd.Process.Kill() } if jobInfo.TempDir != "" { os.RemoveAll(jobInfo.TempDir) } fmt.Fprintf(w, "Abort signal sent for %s", filename) } func handleClearCompleted(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } clearCompletedJobs() w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]bool{"success": true}) } func clearCompletedJobs() { progressMutex.Lock() defer progressMutex.Unlock() for filename, info := range progress { if info.Percentage >= 100 { delete(progress, filename) } } } func updateProgress(filename string, value float64, currentFile string) { progressMutex.Lock() defer progressMutex.Unlock() jobsMutex.Lock() jobInfo, exists := jobs[filename] jobsMutex.Unlock() paused := false if exists { paused = jobInfo.Paused } if existingProgress, ok := progress[filename]; ok { existingProgress.Percentage = value existingProgress.CurrentFile = currentFile existingProgress.Paused = paused } else { progress[filename] = &ProgressInfo{ Percentage: value, CurrentFile: currentFile, Paused: paused, } } } var upgrader = websocket.Upgrader{} var clients = make(map[string]map[*websocket.Conn]bool) var mu sync.Mutex func handleWebSocket(w http.ResponseWriter, r *http.Request) { fmt.Println(config.EnableConsole) if !config.EnableConsole { http.Error(w, "Console output is disabled", http.StatusForbidden) return } filename := r.URL.Query().Get("filename") if filename == "" { http.Error(w, "Filename is required", http.StatusBadRequest) return } conn, err := upgrader.Upgrade(w, r, nil) if err != nil { logger.LogError("WebSocket", fmt.Sprintf("Error while upgrading connection: %v", err)) return } defer conn.Close() logger.LogInfo("WebSocket", fmt.Sprintf("WebSocket connection established for filename: %s", filename)) mu.Lock() if clients[filename] == nil { clients[filename] = make(map[*websocket.Conn]bool) } clients[filename][conn] = true mu.Unlock() for { if _, _, err := conn.NextReader(); err != nil { break } } mu.Lock() delete(clients[filename], conn) mu.Unlock() logger.LogInfo("WebSocket", fmt.Sprintf("WebSocket connection closed for filename: %s", filename)) } func broadcast(filename string, message []byte) { if !config.EnableConsole { return } mu.Lock() defer mu.Unlock() for client := range clients[filename] { if err := client.WriteMessage(websocket.TextMessage, message); err != nil { client.Close() delete(clients[filename], client) logger.LogError("Broadcast", fmt.Sprintf("Error writing message to client: %v", err)) } } }