Files
warpbox-dev/backend/libs/handlers/upload_group.go
Daniel Legt adb1a12dfd feat(upload): support batching via header and update ShareX config
Introduce support for grouping multiple sequential file uploads into a single box using the `X-Warpbox-Batch` header. This is particularly useful for ShareX multi-file selections, which are sent as separate back-to-back requests.

Additionally, this change:
- Updates the ShareX configuration template to opt-in to batching by default.
- Switches ShareX configuration placeholders to the modern `{json:...}` format.
- Adds `thumbnailUrl` to the upload response schema and documents its usage.
2026-05-31 22:27:43 +03:00

50 lines
1.4 KiB
Go

package handlers
import (
"sync"
"time"
)
// uploadGroupWindow is how long after a batched upload a follow-up upload with
// the same X-Warpbox-Batch value (and same account/IP) is folded into the same
// box. ShareX sends a multi-file selection as separate back-to-back requests;
// the batch header lets it land them in one box.
const uploadGroupWindow = 20 * time.Second
// uploadBatchHeader is the opt-in request header. Without it, uploads behave
// exactly as before (one box per request). With it, requests sharing the same
// value (per account/IP) within uploadGroupWindow are grouped into one box.
const uploadBatchHeader = "X-Warpbox-Batch"
// uploadGrouper tracks the most recent box per batch key so opt-in batched
// uploads land in a single box. Each key has its own lock, which also serialises
// that key's concurrent uploads so they append to the same box instead of racing
// to create several.
type uploadGrouper struct {
mu sync.Mutex
entries map[string]*uploadGroupEntry
}
type uploadGroupEntry struct {
mu sync.Mutex
boxID string
manageURL string
deleteURL string
at time.Time
}
func newUploadGrouper() *uploadGrouper {
return &uploadGrouper{entries: make(map[string]*uploadGroupEntry)}
}
func (g *uploadGrouper) entryFor(key string) *uploadGroupEntry {
g.mu.Lock()
defer g.mu.Unlock()
entry, ok := g.entries[key]
if !ok {
entry = &uploadGroupEntry{}
g.entries[key] = entry
}
return entry
}