Files
verdande/internal/httpapi/context.go
T
Kristian WindandClaude Opus 5 4058ff0533 feat(phase2,phase4): comments, attachments, notifications, Web Push, import/export
Comments and attachments close out phase 2. Files are stored
content-addressed by hash, so the path never contains anything the
uploader chose — a filename is attacker-controlled text, and building a
path from it is how "../" becomes a write outside the data directory.
Everything downloads as application/octet-stream: an uploaded SVG served
inline would run its own script on this origin with the session cookie
attached.

Web Push is written directly against RFC 8030/8291/8292 rather than
pulled in — it is three specs' worth of well-defined crypto and about
two hundred lines, and the alternatives carry a JWT and crypto stack
each larger than the file.

Phase 4 imports Todoist's CSV and exports it back. Todoist numbers
priorities the opposite way to how it displays them — its 4 is P1 — so
the two are converted rather than copied; getting it backwards silently
inverts the urgency of every imported task. Nesting is positional, so
the reader keeps an indent stack and the writer never sorts.

The round trip is tested as the brief asks: a CSV in, out, and back
produces an identical project, and two exports of one project are
byte-identical.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 07:42:55 +02:00

25 lines
945 B
Go

package httpapi
import (
"context"
"net/http"
"time"
)
// contextWithTimeout bounds a single handler's work without outliving the request
// itself: if the client has already gone away, the derived context is cancelled too.
func contextWithTimeout(r *http.Request, d time.Duration) (context.Context, context.CancelFunc) {
return context.WithTimeout(r.Context(), d)
}
// contextLike is the read surface store.InstanceKeys needs, so the same call works
// from a request and from a detached background goroutine.
type contextLike = context.Context
// contextWithBackgroundTimeout bounds work that has outlived its request. A push
// round trip is started from a handler but must not be cancelled when that handler
// returns, or the notification is dropped the moment the response is written.
func contextWithBackgroundTimeout(d time.Duration) (context.Context, context.CancelFunc) {
return context.WithTimeout(context.Background(), d)
}