Re-vendor the shared files from sneak/prompts at dd4027b (closes #52)
check / check (push) Canceled after 0s
check / check (push) Canceled after 0s
Takes .golangci.yml, .dockerignore, .gitignore, .editorconfig, the workflow and REPO_POLICIES.md from sneak/prompts dd4027b, keeping this repository's own entries after the shared content. Lint (golangci-lint v2.14.0) and test become Dockerfile phases that script/lint and script/test build alone and uncached; the image build depends on both, and script/cibuild bootstraps, runs script/check, then builds the image. The agent guidance file moves to AGENTS.md. The Go code is fixed for what the new configuration finds; reads that iterate rows now also report an iteration error. Model: opus-5-5
This commit is contained in:
@@ -14,6 +14,7 @@ import (
|
||||
// responseWriter wraps http.ResponseWriter to capture the response
|
||||
type responseWriter struct {
|
||||
http.ResponseWriter
|
||||
|
||||
body *bytes.Buffer
|
||||
statusCode int
|
||||
written bool
|
||||
@@ -86,8 +87,10 @@ func JSONResponseMiddleware(next http.Handler) http.Handler {
|
||||
}
|
||||
|
||||
// Parse the original response
|
||||
var originalResponse map[string]interface{}
|
||||
if err := json.Unmarshal(rw.body.Bytes(), &originalResponse); err != nil {
|
||||
var originalResponse map[string]any
|
||||
|
||||
err := json.Unmarshal(rw.body.Bytes(), &originalResponse)
|
||||
if err != nil {
|
||||
// If we can't parse it, just write original
|
||||
w.WriteHeader(rw.statusCode)
|
||||
_, _ = w.Write(rw.body.Bytes())
|
||||
@@ -96,7 +99,7 @@ func JSONResponseMiddleware(next http.Handler) http.Handler {
|
||||
}
|
||||
|
||||
// Add @meta field
|
||||
originalResponse["@meta"] = map[string]interface{}{
|
||||
originalResponse["@meta"] = map[string]any{
|
||||
"time_zone": "UTC",
|
||||
"api_version": 1,
|
||||
"execution_time": fmt.Sprintf("%d ms", execTime.Milliseconds()),
|
||||
@@ -112,6 +115,7 @@ func JSONResponseMiddleware(next http.Handler) http.Handler {
|
||||
// timeoutWriter wraps ResponseWriter to prevent concurrent writes after timeout
|
||||
type timeoutWriter struct {
|
||||
http.ResponseWriter
|
||||
|
||||
mu sync.Mutex
|
||||
written bool
|
||||
header http.Header // cached header to prevent concurrent access
|
||||
@@ -158,6 +162,7 @@ func (tw *timeoutWriter) Header() http.Header {
|
||||
func (tw *timeoutWriter) markWritten() {
|
||||
tw.mu.Lock()
|
||||
defer tw.mu.Unlock()
|
||||
|
||||
tw.written = true
|
||||
}
|
||||
|
||||
@@ -168,7 +173,10 @@ func (tw *timeoutWriter) markWritten() {
|
||||
// The timeout parameter specifies the maximum duration allowed for request processing.
|
||||
// The returned middleware handles panics from the wrapped handler by re-panicking
|
||||
// after cleanup, and prevents concurrent writes to the response after timeout occurs.
|
||||
func TimeoutMiddleware(timeout time.Duration, logger *slog.Logger) func(http.Handler) http.Handler {
|
||||
func TimeoutMiddleware(
|
||||
timeout time.Duration,
|
||||
logger *slog.Logger,
|
||||
) func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
startTime := time.Now()
|
||||
@@ -182,7 +190,7 @@ func TimeoutMiddleware(timeout time.Duration, logger *slog.Logger) func(http.Han
|
||||
}
|
||||
|
||||
done := make(chan struct{})
|
||||
panicChan := make(chan interface{}, 1)
|
||||
panicChan := make(chan any, 1)
|
||||
|
||||
go func() {
|
||||
defer func() {
|
||||
@@ -202,6 +210,7 @@ func TimeoutMiddleware(timeout time.Duration, logger *slog.Logger) func(http.Han
|
||||
return
|
||||
case <-ctx.Done():
|
||||
tw.markWritten() // Prevent the handler from writing after timeout
|
||||
|
||||
execTime := time.Since(startTime)
|
||||
|
||||
// Log the timeout as a warning
|
||||
@@ -219,13 +228,13 @@ func TimeoutMiddleware(timeout time.Duration, logger *slog.Logger) func(http.Han
|
||||
w.WriteHeader(http.StatusRequestTimeout)
|
||||
tw.mu.Unlock()
|
||||
|
||||
response := map[string]interface{}{
|
||||
"status": "error",
|
||||
"error": map[string]interface{}{
|
||||
"msg": "Request timeout",
|
||||
"code": http.StatusRequestTimeout,
|
||||
response := map[string]any{
|
||||
jsonKeyStatus: jsonStatusError,
|
||||
jsonKeyError: map[string]any{
|
||||
jsonKeyMsg: "Request timeout",
|
||||
jsonKeyCode: http.StatusRequestTimeout,
|
||||
},
|
||||
"@meta": map[string]interface{}{
|
||||
"@meta": map[string]any{
|
||||
"time_zone": "UTC",
|
||||
"api_version": 1,
|
||||
"execution_time": fmt.Sprintf("%d ms", execTime.Milliseconds()),
|
||||
@@ -273,8 +282,10 @@ func JSONValidationMiddleware(next http.Handler) http.Handler {
|
||||
|
||||
// For JSON responses, validate the JSON
|
||||
if rw.body.Len() > 0 {
|
||||
var testParse interface{}
|
||||
if err := json.Unmarshal(rw.body.Bytes(), &testParse); err == nil {
|
||||
var testParse any
|
||||
|
||||
err := json.Unmarshal(rw.body.Bytes(), &testParse)
|
||||
if err == nil {
|
||||
// Valid JSON, write it out
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(rw.statusCode)
|
||||
@@ -293,6 +304,7 @@ func JSONValidationMiddleware(next http.Handler) http.Handler {
|
||||
if statusCode == http.StatusOK {
|
||||
statusCode = http.StatusInternalServerError
|
||||
}
|
||||
|
||||
w.WriteHeader(statusCode)
|
||||
|
||||
errorMsg := "Internal server error"
|
||||
@@ -300,11 +312,11 @@ func JSONValidationMiddleware(next http.Handler) http.Handler {
|
||||
errorMsg = "Request timeout"
|
||||
}
|
||||
|
||||
response := map[string]interface{}{
|
||||
"status": "error",
|
||||
"error": map[string]interface{}{
|
||||
"msg": errorMsg,
|
||||
"code": statusCode,
|
||||
response := map[string]any{
|
||||
jsonKeyStatus: jsonStatusError,
|
||||
jsonKeyError: map[string]any{
|
||||
jsonKeyMsg: errorMsg,
|
||||
jsonKeyCode: statusCode,
|
||||
},
|
||||
}
|
||||
_ = json.NewEncoder(w).Encode(response)
|
||||
@@ -314,6 +326,7 @@ func JSONValidationMiddleware(next http.Handler) http.Handler {
|
||||
// statusWriter wraps http.ResponseWriter to capture the status code
|
||||
type statusWriter struct {
|
||||
http.ResponseWriter
|
||||
|
||||
statusCode int
|
||||
written bool
|
||||
}
|
||||
@@ -323,6 +336,7 @@ func (sw *statusWriter) WriteHeader(statusCode int) {
|
||||
sw.statusCode = statusCode
|
||||
sw.written = true
|
||||
}
|
||||
|
||||
sw.ResponseWriter.WriteHeader(statusCode)
|
||||
}
|
||||
|
||||
@@ -362,11 +376,12 @@ func RequestLoggerMiddleware(logger *slog.Logger) func(http.Handler) http.Handle
|
||||
// Slow query threshold (1 second)
|
||||
const slowQueryThreshold = 1 * time.Second
|
||||
|
||||
if sw.statusCode >= http.StatusInternalServerError {
|
||||
switch {
|
||||
case sw.statusCode >= http.StatusInternalServerError:
|
||||
logLevel = slog.LevelError
|
||||
} else if sw.statusCode >= http.StatusBadRequest {
|
||||
case sw.statusCode >= http.StatusBadRequest:
|
||||
logLevel = slog.LevelWarn
|
||||
} else if duration >= slowQueryThreshold {
|
||||
case duration >= slowQueryThreshold:
|
||||
logLevel = slog.LevelWarn
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user