package app import ( "io" "net/http" "path/filepath" "strconv" "strings" "spread/internal/spread" ) const maxUpload = 8 << 20 // handleAppAnalyze accepts a customer's uploaded BOM, re-quotes it against the // live DB catalog, persists it under their subscriber, and shows the result. func (a *App) handleAppAnalyze(w http.ResponseWriter, r *http.Request) { u, s, _ := current(r) ctx := r.Context() if err := r.ParseMultipartForm(maxUpload); err != nil { a.redirect(w, r, flash("/app", "err", "Could not read the upload.")) return } f, hdr, err := r.FormFile("bom") if err != nil { a.redirect(w, r, flash("/app", "err", "Choose a BOM file to upload.")) return } defer f.Close() data, _ := io.ReadAll(io.LimitReader(f, maxUpload)) items, err := spread.ParseBOM(hdr.Filename, data) if err != nil { a.redirect(w, r, flash("/app", "err", err.Error())) return } cat, err := a.catalogSource(ctx) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } ref := strings.TrimSpace(strings.TrimSuffix(filepath.Base(hdr.Filename), filepath.Ext(hdr.Filename))) if ref == "" { ref = "BOM" } rep := spread.AnalyzeWith(ref, items, cat) id, err := a.saveBOM(ctx, s.ID, u.ID, rep, hdr.Filename) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } a.redirect(w, r, "/app/bom/"+strconv.FormatInt(id, 10)) } // handleBOM shows one saved BOM. Customers see only their own; the internal // subscriber can view any (admin BOM review). func (a *App) handleBOM(w http.ResponseWriter, r *http.Request) { u, s, _ := current(r) id, _ := strconv.ParseInt(r.PathValue("id"), 10, 64) d, err := a.getBOM(r.Context(), id) if err != nil { http.Error(w, "BOM not found", http.StatusNotFound) return } if !s.IsInternal && d.SubscriberID != s.ID { http.Redirect(w, r, "/app", http.StatusSeeOther) return } back := "/app" if s.IsInternal { back = "/admin/boms" } a.render(w, r, http.StatusOK, "bom", pageData{Title: d.Ref, User: u, Sub: s, BOM: &d, BackURL: back}) } // handleBOMs is the admin BOM-review list across all subscribers. func (a *App) handleBOMs(w http.ResponseWriter, r *http.Request) { boms, err := a.listBOMs(r.Context(), nil) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } a.render(w, r, http.StatusOK, "boms", pageData{Title: "BOMs", BOMs: boms}) }