package v1 import ( "crypto/sha256" "encoding/hex" "io" "mime/multipart" "net/http" "os" "github.com/go-chi/chi/v5" "code.icb4dc0.de/prskr/searcherside/core/ports" "code.icb4dc0.de/prskr/searcherside/internal/logging" ) type IndexHandler struct { MaxMemoryBytes int64 Indexer ports.IndexCurator } func (h IndexHandler) IngestIndex(writer http.ResponseWriter, req *http.Request) { logger := logging.GetLogger(req.Context()) if err := req.ParseMultipartForm(h.MaxMemoryBytes); err != nil { logger.WarnContext(req.Context(), "Failed to parse multipart form", logging.Error(err)) http.Error(writer, "Failed to parse multipart form", http.StatusInternalServerError) return } if len(req.MultipartForm.File) != 1 { http.Error(writer, "Only a single file can be uploaded", http.StatusBadRequest) return } var indexFile *multipart.FileHeader for _, files := range req.MultipartForm.File { if len(files) != 1 { http.Error(writer, "Only a single file can be uploaded", http.StatusBadRequest) return } indexFile = files[0] break } indexTempFile, err := os.CreateTemp(os.TempDir(), "searcherside-index-*") if err != nil { logger.ErrorContext(req.Context(), "Failed to create temporary index file", logging.Error(err)) http.Error(writer, "Failed to create temporary index file", http.StatusInternalServerError) return } hash := sha256.New() stream, err := indexFile.Open() if err != nil { logger.ErrorContext(req.Context(), "Failed to open index file", logging.Error(err)) http.Error(writer, "Failed to open index file", http.StatusBadRequest) return } if _, err := io.Copy(io.MultiWriter(hash, indexTempFile), stream); err != nil { logger.ErrorContext(req.Context(), "Failed to copy index file", logging.Error(err)) http.Error(writer, "Failed to copy index file", http.StatusBadRequest) return } if err := stream.Close(); err != nil { logger.ErrorContext(req.Context(), "Failed to close index file", logging.Error(err)) } if err := indexTempFile.Close(); err != nil { logger.ErrorContext(req.Context(), "Failed to close temporary index file", logging.Error(err)) http.Error(writer, "Failed to close temporary index file", http.StatusInternalServerError) return } go func() { indexErr := h.Indexer.Ingest(req.Context(), ports.IngestIndexRequest{ FilePath: indexTempFile.Name(), Hash: hex.EncodeToString(hash.Sum(nil)), Module: chi.URLParam(req, "module"), Instance: chi.URLParam(req, "instance"), }) if indexErr != nil { logger.ErrorContext(req.Context(), "Failed to ingest index", logging.Error(indexErr)) } }() writer.WriteHeader(http.StatusAccepted) }