package main import ( "encoding/csv" "fmt" "net/http" "strconv" "strings" ) // handleGenerateTokens creates volunteer_token values for all attendees that don't have one. func (app *App) handleGenerateTokens(w http.ResponseWriter, r *http.Request) { count, err := app.generateTokensForAll() if err != nil { writeError(w, err.Error(), http.StatusInternalServerError) return } writeJSON(w, map[string]any{"generated": count}) } // handleExportTokenLinks streams a CSV download with token signup links, // compatible with MailChimp / Zeffy bulk-send workflows. func (app *App) handleExportTokenLinks(w http.ResponseWriter, r *http.Request) { attendees, err := app.listAttendees("", "", "") if err != nil { writeError(w, err.Error(), http.StatusInternalServerError) return } baseURL := app.baseURL if baseURL == "" { app.db.QueryRow(`SELECT value FROM config WHERE key = 'base_url'`).Scan(&baseURL) } baseURL = strings.TrimRight(baseURL, "/") w.Header().Set("Content-Type", "text/csv") w.Header().Set("Content-Disposition", `attachment; filename="volunteer-tokens.csv"`) wr := csv.NewWriter(w) wr.Write([]string{"Email Address", "First Name", "Token", "Signup Link"}) for _, a := range attendees { if a.VolunteerToken == nil { continue } firstName := a.Name if parts := strings.Fields(a.Name); len(parts) > 0 { firstName = parts[0] } link := fmt.Sprintf("%s/#/v/%s", baseURL, *a.VolunteerToken) wr.Write([]string{a.Email, firstName, *a.VolunteerToken, link}) } wr.Flush() } // handleEmailToken sends a token email to a single attendee. func (app *App) handleEmailToken(w http.ResponseWriter, r *http.Request) { id, err := strconv.Atoi(r.PathValue("id")) if err != nil { writeError(w, "invalid id", http.StatusBadRequest) return } a, err := app.getAttendee(id) if err != nil || a == nil { writeError(w, "not found", http.StatusNotFound) return } if err := app.sendTokenEmail(*a); err != nil { writeError(w, err.Error(), http.StatusInternalServerError) return } writeJSON(w, map[string]any{"ok": true}) } // handleEmailAllTokens bulk-sends token emails to all attendees that have both a token and email. func (app *App) handleEmailAllTokens(w http.ResponseWriter, r *http.Request) { attendees, err := app.listAttendees("", "", "") if err != nil { writeError(w, err.Error(), http.StatusInternalServerError) return } var sent, skipped int var errors []string for _, a := range attendees { if a.Email == "" || a.VolunteerToken == nil { skipped++ continue } if err := app.sendTokenEmail(a); err != nil { errors = append(errors, fmt.Sprintf("%s: %v", a.Name, err)) skipped++ } else { sent++ } } if errors == nil { errors = []string{} } writeJSON(w, map[string]any{"sent": sent, "skipped": skipped, "errors": errors}) }