From 5b89d0b04ab2e6cfedb8c065ed2f88ee2f0ac67c Mon Sep 17 00:00:00 2001 From: Steve White Date: Wed, 29 Jan 2025 15:54:31 +0000 Subject: [PATCH] Add server.go with HTTP API functionality --- server.go | 193 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100644 server.go diff --git a/server.go b/server.go new file mode 100644 index 0000000..bf73150 --- /dev/null +++ b/server.go @@ -0,0 +1,193 @@ +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, + }) +} \ No newline at end of file