Files
MindSift/internal/api/bodylimit.go
T
sandrodachandClaude Opus 5.5 7801cd13b9
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 1m49s
feat(voice): self-hosted speech recognition via Whisper (STT_*)
- STT_URL switches voice input to an OpenAI-compatible Whisper server
  (e.g. speaches); without it the browser's recognition stays. Configured
  by environment only, with limits for upload size, parallel jobs, hourly
  budget per user and timeout. The demo always uses the browser.
- POST /voice/transcribe forwards recordings and returns the text; nothing
  is stored. On startup MindSift waits for the server and asks it to
  download the model if missing.
- Admin card shows the active mode and server state, with a test recording.
- docker-compose: optional "whisper" service (speaches 0.8.3 CPU, profile
  "whisper", models in ./whisper).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 22:12:57 +01:00

45 lines
1.4 KiB
Go

package api
import (
"net/http"
"strings"
"time"
"github.com/sandrodach/mindsift/internal/storage"
)
// Request bodies are capped for every endpoint (FLAW-36), so a session or API key cannot tie up memory
// with huge uploads. Note sync and imports need more; their handlers apply the same limits again.
const defaultMaxBody = 2 << 20
func (s *Server) bodyLimitFor(path string) int64 {
switch {
case path == "/api/v1/voice/transcribe":
return int64(s.cfg.Speech.MaxUploadMB)<<20 + 1<<20
case strings.HasPrefix(path, "/api/v1/import/"):
return storage.MaxImportUploadBytes + 1<<20
case strings.HasPrefix(path, "/api/v1/docs/") && strings.HasSuffix(path, "/sync"):
return syncMaxBody
}
return defaultMaxBody
}
func (s *Server) bodyLimit(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Body != nil && r.Body != http.NoBody {
r.Body = http.MaxBytesReader(w, r.Body, s.bodyLimitFor(r.URL.Path))
}
next.ServeHTTP(w, r)
})
}
// The server gives every request 15 s to send its body and 30 s for the answer. Whole archives on a slow
// line need longer: downloads of backups and exports, uploads of imports (FLAW-38).
const longTransferTime = 30 * time.Minute
func allowLongTransfer(w http.ResponseWriter) {
rc := http.NewResponseController(w)
_ = rc.SetWriteDeadline(time.Now().Add(longTransferTime))
_ = rc.SetReadDeadline(time.Now().Add(longTransferTime))
}