#1: server-side regex highlighter (highlight.go) for go/python/js/json/bash/sql; token span classes styled in app.css; per-line so gutter stays aligned. #2: in-memory token-bucket rate limiter (ratelimit.go) on POST /api/pastes, /api/guess-language and unlock POST; 429 + Retry-After + X-RateLimit headers. #26: new-page JS POSTs the password to /{id} with ?next= after creation; the unlock handler honors same-origin ?next= redirect so the creator lands on the unlocked paste. POST /{id} route added. Tests: ratelimit_test.go (burst/429, refill, unlock limit, highlight, auto- unlock e2e); existing tests updated for per-test limiter isolation.
This commit is contained in:
@@ -371,6 +371,7 @@ func (a *apiServer) routes() http.Handler {
|
||||
r.Get("/unlock/{id}", a.handlePasteView)
|
||||
r.Post("/unlock/{id}", a.handlePasteView)
|
||||
r.Get("/{id}", a.handlePasteView)
|
||||
r.Post("/{id}", a.handlePasteView)
|
||||
|
||||
r.NotFound(func(w http.ResponseWriter, r *http.Request) {
|
||||
writeErr(w, 404, "not found")
|
||||
@@ -379,6 +380,11 @@ func (a *apiServer) routes() http.Handler {
|
||||
}
|
||||
|
||||
func (a *apiServer) handleCreatePaste(w http.ResponseWriter, r *http.Request) {
|
||||
setRateLimitHeaders(w, 1, 5)
|
||||
if !rateLimitCreate(r) {
|
||||
writeRateLimited(w, 1)
|
||||
return
|
||||
}
|
||||
var p Paste
|
||||
if err := json.NewDecoder(r.Body).Decode(&p); err != nil {
|
||||
writeErr(w, 400, "invalid json body")
|
||||
@@ -405,6 +411,7 @@ func (a *apiServer) handleCreatePaste(w http.ResponseWriter, r *http.Request) {
|
||||
"api_url": "/api/pastes/" + created.ID,
|
||||
"expires_at": created.ExpiresAt,
|
||||
"created_at": created.CreatedAt,
|
||||
"rate_limit": map[string]int{"create_per_sec": 1, "burst": 5},
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user