package main import ( "encoding/json" "fmt" "log" "net/http" "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" ) type Server struct { router *chi.Mux port string } func NewServer(port string) *Server { s := &Server{ router: chi.NewRouter(), port: port, } s.setupRoutes() return s } func (s *Server) setupRoutes() { s.router.Use(middleware.Logger) s.router.Use(middleware.Recoverer) s.router.Post("/api/papers/search", s.handleSearch) s.router.Post("/api/papers/process", s.handleProcess) s.router.Post("/api/papers/search-process", s.handleSearchAndProcess) } func (s *Server) Run() error { addr := fmt.Sprintf(":%s", s.port) log.Printf("Starting server on %s", addr) return http.ListenAndServe(addr, s.router) } func (s *Server) handleSearch(w http.ResponseWriter, r *http.Request) { var req struct { StartDate string `json:"start_date"` EndDate string `json:"end_date"` Query string `json:"query"` MaxResults int `json:"max_results"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid request body", http.StatusBadRequest) return } // Reuse existing validation if !isValidDate(req.StartDate) || !isValidDate(req.EndDate) { http.Error(w, "Invalid date format", http.StatusBadRequest) return } papers, err := arxiva.FetchPapers(req.StartDate, req.EndDate, req.Query, req.MaxResults) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } json.NewEncoder(w).Encode(papers) } func (s *Server) handleProcess(w http.ResponseWriter, r *http.Request) { var req struct { InputFile string `json:"input_file"` CriteriaFile string `json:"criteria_file"` ApiKey string `json:"api_key"` Model string `json:"model"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid request body", http.StatusBadRequest) return } // Create processor configuration config := paperprocessor.Config{ APIEndpoint: *apiEndpoint, // This would need to be passed to Server struct APIKey: req.ApiKey, Model: req.Model, RequestDelay: 2 * time.Second, } // Process the papers outputJSON := req.InputFile + "-processed.json" if err := paperprocessor.ProcessFile( req.InputFile, outputJSON, req.CriteriaFile, config, ); err != nil { http.Error(w, fmt.Sprintf("Processing failed: %v", err), http.StatusInternalServerError) return } // Format to markdown outputMD := req.InputFile + "-processed.md" if err := paperformatter.FormatPapers(outputJSON, outputMD); err != nil { http.Error(w, fmt.Sprintf("Formatting failed: %v", err), http.StatusInternalServerError) return } // Return the paths to the generated files json.NewEncoder(w).Encode(struct { JSONOutput string `json:"json_output"` MDOutput string `json:"md_output"` }{ JSONOutput: outputJSON, MDOutput: outputMD, }) } func (s *Server) handleSearchAndProcess(w http.ResponseWriter, r *http.Request) { var req struct { StartDate string `json:"start_date"` EndDate string `json:"end_date"` Query string `json:"query"` MaxResults int `json:"max_results"` CriteriaFile string `json:"criteria_file"` ApiKey string `json:"api_key"` Model string `json:"model"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid request body", http.StatusBadRequest) return } // Validate dates if !isValidDate(req.StartDate) || !isValidDate(req.EndDate) { http.Error(w, "Invalid date format", http.StatusBadRequest) return } // Fetch papers papers, err := arxiva.FetchPapers(req.StartDate, req.EndDate, req.Query, req.MaxResults) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } // Save papers to temporary JSON file baseFilename := fmt.Sprintf("%s-%s-%s", req.StartDate, req.EndDate, sanitizeFilename(req.Query)) inputJSON := baseFilename + ".json" if err := arxiva.SaveToFile(papers, req.StartDate, req.EndDate, req.Query); err != nil { http.Error(w, fmt.Sprintf("Failed to save papers: %v", err), http.StatusInternalServerError) return } // Create processor configuration config := paperprocessor.Config{ APIEndpoint: *apiEndpoint, // This would need to be passed to Server struct APIKey: req.ApiKey, Model: req.Model, RequestDelay: 2 * time.Second, } // Process the papers outputJSON := baseFilename + "-processed.json" if err := paperprocessor.ProcessFile( inputJSON, outputJSON, req.CriteriaFile, config, ); err != nil { http.Error(w, fmt.Sprintf("Processing failed: %v", err), http.StatusInternalServerError) return } // Format to markdown outputMD := baseFilename + "-processed.md" if err := paperformatter.FormatPapers(outputJSON, outputMD); err != nil { http.Error(w, fmt.Sprintf("Formatting failed: %v", err), http.StatusInternalServerError) return } // Return the paths to the generated files json.NewEncoder(w).Encode(struct { JSONOutput string `json:"json_output"` MDOutput string `json:"md_output"` }{ JSONOutput: outputJSON, MDOutput: outputMD, }) }