package main import ( "crypto/rand" "crypto/subtle" "encoding/base64" "net/http" "github.com/go-chi/chi/v5" ) // genDeletionToken returns a 32-char url-safe random token func genDeletionToken() string { b := make([]byte, 24) rand.Read(b) return base64.RawURLEncoding.EncodeToString(b) } // maybeBurn marks a paste soft-deleted if burn_after_read is set. // Returns true if this read consumed the paste. func (s *Store) maybeBurn(row *PasteRow) bool { if !row.BurnAfterRead { return false } s.SoftDelete(row.ID) return true } func deletionTokenEqual(stored, given string) bool { return subtle.ConstantTimeCompare([]byte(stored), []byte(given)) == 1 } // handleRedeemDeletion lets a holder of the deletion token hard-delete immediately. // DELETE /api/pastes/{id}/redeem?token=... func (a *apiServer) handleRedeemDeletion(w http.ResponseWriter, r *http.Request) { id := chi.URLParam(r, "id") token := r.URL.Query().Get("token") if token == "" { writeErr(w, 400, "token required") return } row, err := a.store.GetPaste(id) if err != nil || row == nil { writeErr(w, 404, "paste not found") return } if row.DeletionToken.String == "" || !deletionTokenEqual(row.DeletionToken.String, token) { writeErr(w, 403, "invalid token") return } // hard delete: pastes table row goes away entirely a.store.db.Exec(`DELETE FROM pastes WHERE id = ?`, row.ID) writeJSON(w, 200, map[string]string{"status": "deleted"}) }