Burn after N reads: reads_limit/reads_used, per-viewer 15min dedupe via paste_views, reads_remaining in API+stats pill, raw counts as read (#49)
This commit is contained in:
@@ -100,7 +100,7 @@ func expiryString(expiresAt int64) string {
|
||||
}
|
||||
}
|
||||
|
||||
func (a *apiServer) renderPaste(w http.ResponseWriter, row *PasteRow, justCreated bool, deletionToken string) {
|
||||
func (a *apiServer) renderPaste(w http.ResponseWriter, row *PasteRow, justCreated bool, deletionToken string, readsRemaining *int) {
|
||||
lines := strings.Count(row.Content, "\n") + 1
|
||||
gutter := ""
|
||||
for i := 1; i <= lines; i++ {
|
||||
@@ -137,6 +137,9 @@ func (a *apiServer) renderPaste(w http.ResponseWriter, row *PasteRow, justCreate
|
||||
"ExpiresAt": row.ExpiresAt.Valid,
|
||||
"ExpiresIn": expIn,
|
||||
"DeletionToken": deletionToken,
|
||||
"ReadsLimit": row.ReadsLimit.Valid,
|
||||
"ReadsLeftN": readsRemaining, // *int: reads remaining after this view
|
||||
"ReadsTotal": int(row.ReadsLimit.Int64),
|
||||
"JustCreated": justCreated,
|
||||
"Host": "this host",
|
||||
}
|
||||
@@ -181,7 +184,7 @@ func (a *apiServer) handlePasteView(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
}
|
||||
a.renderPaste(w, row, false, "")
|
||||
a.renderPaste(w, row, false, "", nil)
|
||||
return
|
||||
}
|
||||
renderPage(w, "unlock.html", map[string]any{"Page": "unlock", "ID": row.ID, "Wrong": true, "CreatedAgo": agoString(row.CreatedAt), "CreatedAtUnix": row.CreatedAt})
|
||||
@@ -195,19 +198,21 @@ func (a *apiServer) handlePasteView(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
a.store.IncrementViews(row.ID)
|
||||
justCreated := r.URL.Query().Get("created") == "1"
|
||||
token := r.URL.Query().Get("token")
|
||||
if justCreated && token != "" {
|
||||
// one-time display of the deletion token via the created banner
|
||||
http.SetCookie(w, &http.Cookie{Name: "tok_" + row.ID, Value: token, Path: "/", MaxAge: 60, HttpOnly: true, SameSite: http.SameSiteLaxMode})
|
||||
}
|
||||
// only pass the token to the template right after creation
|
||||
if justCreated {
|
||||
a.renderPaste(w, row, true, token)
|
||||
// #49: burn-after-N-reads budget (per-viewer, 15-minute dedupe window).
|
||||
// Just-created first render does not count as a read for the creator.
|
||||
if !justCreated {
|
||||
rem, _ := a.store.registerRead(row, currentViewerID(r))
|
||||
a.renderPaste(w, row, false, "", rem)
|
||||
return
|
||||
}
|
||||
a.renderPaste(w, row, false, "")
|
||||
// only pass the token to the template right after creation
|
||||
a.renderPaste(w, row, true, token, nil)
|
||||
}
|
||||
|
||||
var _ = strconv.Itoa
|
||||
|
||||
Reference in New Issue
Block a user