package app import ( "net/http" "net/url" "strconv" "strings" ) func (a *App) redirect(w http.ResponseWriter, r *http.Request, path string) { http.Redirect(w, r, path, http.StatusSeeOther) } // flash builds a redirect target with a notice or error query param. func flash(path, key, msg string) string { return path + "?" + key + "=" + url.QueryEscape(msg) } func (a *App) handleAdmin(w http.ResponseWriter, r *http.Request) { u, s, _ := current(r) d := pageData{Title: "Overview", User: u, Sub: s} d.Counts.Subs, d.Counts.Users, d.Counts.Catalog, d.Counts.Boms = a.counts(r.Context()) a.render(w, r, http.StatusOK, "admin", d) } // --- catalog -------------------------------------------------------------- func (a *App) handleCatalog(w http.ResponseWriter, r *http.Request) { ctx := r.Context() entries, err := a.listCatalog(ctx) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } d := pageData{ Title: "Catalog", Catalog: entries, Notice: r.URL.Query().Get("notice"), Error: r.URL.Query().Get("err"), } if pn := r.URL.Query().Get("edit"); pn != "" { if e, err := a.getCatalogEntry(ctx, pn); err == nil { d.Edit = &e } } a.render(w, r, http.StatusOK, "catalog", d) } func (a *App) handleCatalogSave(w http.ResponseWriter, r *http.Request) { pn := strings.TrimSpace(r.PostFormValue("part_number")) desc := strings.TrimSpace(r.PostFormValue("description")) price, perr := strconv.ParseFloat(strings.TrimSpace(r.PostFormValue("spread_price")), 64) if pn == "" || perr != nil || price <= 0 { a.redirect(w, r, flash("/admin/catalog", "err", "Part number and a positive spread price are required.")) return } if err := a.upsertCatalog(r.Context(), pn, desc, price); err != nil { a.redirect(w, r, flash("/admin/catalog", "err", err.Error())) return } a.redirect(w, r, flash("/admin/catalog", "notice", "Saved "+pn+".")) } func (a *App) handleCatalogDelete(w http.ResponseWriter, r *http.Request) { pn := strings.TrimSpace(r.PostFormValue("part_number")) if pn != "" { _ = a.deleteCatalog(r.Context(), pn) } a.redirect(w, r, flash("/admin/catalog", "notice", "Deleted "+pn+".")) } func (a *App) handleCatalogSeed(w http.ResponseWriter, r *http.Request) { if err := a.seedDemoCatalog(r.Context()); err != nil { a.redirect(w, r, flash("/admin/catalog", "err", err.Error())) return } a.redirect(w, r, flash("/admin/catalog", "notice", "Seeded the demo catalog.")) } // --- subscribers & users -------------------------------------------------- func (a *App) handleSubscribers(w http.ResponseWriter, r *http.Request) { subs, err := a.listSubscribers(r.Context()) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } a.render(w, r, http.StatusOK, "subscribers", pageData{ Title: "Subscribers", Subs: subs, Notice: r.URL.Query().Get("notice"), Error: r.URL.Query().Get("err"), }) } func (a *App) handleSubscriberCreate(w http.ResponseWriter, r *http.Request) { name := strings.TrimSpace(r.PostFormValue("name")) if name == "" { a.redirect(w, r, flash("/admin/subscribers", "err", "Subscriber name is required.")) return } if _, err := a.createSubscriber(r.Context(), name); err != nil { a.redirect(w, r, flash("/admin/subscribers", "err", err.Error())) return } a.redirect(w, r, flash("/admin/subscribers", "notice", "Created subscriber "+name+".")) } func (a *App) handleUserCreate(w http.ResponseWriter, r *http.Request) { subID, _ := strconv.ParseInt(r.PostFormValue("subscriber_id"), 10, 64) email := strings.TrimSpace(r.PostFormValue("email")) name := strings.TrimSpace(r.PostFormValue("name")) password := r.PostFormValue("password") if subID == 0 || email == "" { a.redirect(w, r, flash("/admin/subscribers", "err", "Subscriber and email are required.")) return } uid, err := a.createUser(r.Context(), subID, email, name, password) if err != nil { a.redirect(w, r, flash("/admin/subscribers", "err", err.Error())) return } if password == "" { if err := a.sendInvite(r.Context(), uid, email); err != nil { a.redirect(w, r, flash("/admin/subscribers", "err", "Created "+email+", but the invite email failed: "+err.Error())) return } a.redirect(w, r, flash("/admin/subscribers", "notice", "Created "+email+" and emailed an invite to set a password.")) return } a.redirect(w, r, flash("/admin/subscribers", "notice", "Created user "+email+".")) }