searcherside/handlers/api/v1/index_handler.go

94 lines
2.6 KiB
Go

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)
}