- Add POST /e/{endpoint}/requests/{id}/replay endpoint
- Replay re-sends captured method, path, headers, query, body to target URL
- Response includes target status code, headers, body (truncated 64KB)
- SSRF guard: denies replay to private/loopback by default
- Opt-in env var HATCH_ALLOW_PRIVATE_REPLAY=true for local dev
- Server-rendered inspect page at GET /e/{endpoint} with replay button UI
- SSE live stream for new requests (EventSource)
- Mock configuration at PUT /e/{endpoint}/mock
- Store: GetRequest, GetMock, SetMock methods added
- Chi router migration from stdlib ServeMux
- 26 passing tests including replay unit tests and E2E capture-to-replay
Co-Authored-By: Paperclip <noreply@paperclip.ing>
36 lines
1.0 KiB
Go
36 lines
1.0 KiB
Go
package handler
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
|
|
"github.com/elfoundation/hatch/internal/store"
|
|
)
|
|
|
|
// HandleMock handles PUT /e/{endpointID}/mock.
|
|
func HandleMock(repo store.Repository) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
endpointID := r.PathValue("endpointID")
|
|
if endpointID == "" {
|
|
writeError(w, http.StatusBadRequest, "missing endpoint ID")
|
|
return
|
|
}
|
|
if _, err := repo.GetEndpoint(r.Context(), endpointID); err != nil {
|
|
repo.CreateEndpoint(r.Context(), endpointID)
|
|
}
|
|
var mock store.MockConfig
|
|
if err := json.NewDecoder(r.Body).Decode(&mock); err != nil {
|
|
writeError(w, http.StatusBadRequest, "invalid JSON body: "+err.Error())
|
|
return
|
|
}
|
|
mock.EndpointID = endpointID
|
|
if err := repo.SetMock(r.Context(), &mock); err != nil {
|
|
writeError(w, http.StatusInternalServerError, "failed to set mock: "+err.Error())
|
|
return
|
|
}
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(http.StatusOK)
|
|
json.NewEncoder(w).Encode(map[string]string{"status": "ok"})
|
|
}
|
|
}
|